Skip to main content
GameDev.net gamedev.net
🔒 Locked

WinAPI + OpenGL: Change window style and/or resolution

Started by csisy Jun 1, 2016 at 11:00 AM 20 replies 21k views
Original Post
csisy
csisy

Hey,

Unfortunately I cannot find my answer in the already existing topics, so here I come. I have some problems with the windowing system, especially with chaning the window style at runtime.

First of all, the window has 3 different mode: Windowed, Borderless and Fullscreen. Borderless and Fullscreen mode means the window's style is simply a WS_POPUP, while the Windowed mode means the window can have borders, system menu, maximize button, and so on. The window has border, caption, minimize-maximize-close buttons by default when selecting Windowed mode.

I'm using the WM_SIZE message to resize the frame buffers (aka render targets). The window does not have a resizing border, so only the engine (and the maximize/restore buttons) can change the size and cause the message to arrive.

I have a function in my Window class which supposed to be used to change the style and/or the resolution of the window. And this is not working. :) Initially, I'm creating a window with the startup settings. Now it's 1280x720 windowed = border + caption. The size is adjusted, so the client area is the 1280x720, the window itself is bigger.

For testing purposes, I'm using the F1-F3 keys to select the new window mode. The resolution is the same for now. I'm also running FRAPS to check if everything is okay. Here are the steps:

0) the window is shown, everything is okay (I have a really simple test scene)

step_0.jpg

1) switch to borderless mode: it looks like the style is changed (the borders are gone), however the FPS counter is gone and I have a strange "border" at the left and top side of the window. So it seems the style change was not really successful.

step_1.jpg

2) switch back to windowed mode: everything works again, the FPS counter is back, the window border is back, and the viewport fits.

3) switch to borderless mode again: this is the worst case - a double resize event is raised, first with size 1296x758 (which is the full size of the window with borders) then back to 1280x720. This means a double-resize of textures but the size actually isn't changed. And of course the same problems as in the 1st step.

And here is the code of the window changing function:


void WindowsWindow::Reshape(uint32 width, uint32 height, WindowMode mode)
{
    // change style based on window mode change
    if (windowMode != mode)
    {
        windowMode = mode;

        //const uint32 styleChangeFlags = SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED;

        if (mode == WindowMode::Fullscreen || mode == WindowMode::Borderless) // going full-screen
        {
            SetWindowLongPtr(hwnd, GWL_STYLE, fullscreenStyle);
            //SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, styleChangeFlags);
        }
        else // going windowed
        {
            SetWindowLongPtr(hwnd, GWL_STYLE, windowedStyle);
            //SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, styleChangeFlags);
        }
    }

    uint32 flags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED | SWP_SHOWWINDOW;

    int32 x = 0;
    int32 y = 0;

    // adjust window size and position
    if (windowMode == WindowMode::Windowed)
        flags |= SWP_NOMOVE;
    Adjust(x, y, width, height);

    SetWindowPos(hwnd, nullptr, x, y, width, height, flags);
}

I hope someone can help me solve this issue.

Edit:
The code was just changed that's why I have comments and "strange if" there. :)

Edit 2:

If I change the window mode to borderless, I get a window which looks like as in the 2nd image above. However if after this I change the resolution to ... say 1024x768 then back to 1280x720, everything works fine. (Haven't tested fullscreen mode yet, that will be another problem)

sorry for my bad english
21st Century Moose
21st Century Moose

I don't know about OpenGL, but under D3D11 I resolved similar symptoms by not actually responding directly to WM_SIZE but instead just setting a flag that a size change was requested. Then at the start of the next frame I check this flag and actually implement the size change there (clearing the flag when done).

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls. 
csisy
csisy

I though something similar, thanks for the feedback!

It solves the resizing issue (which is irrelevant from the graphics API) but unfortunately the strange behavior remains :(

sorry for my bad english
Erik Rufelt
Erik Rufelt

I use this one, has some comments and TODOs that could be double-checked..

It has a check to (supposedly) only go to fullscreen on monitors connected to the primary GPU in case you have more than one, if not that part is unnecessary..


// Get device for monitor
static bool getDeviceForMonitor(const MONITORINFOEX &monitorInfo, LPDISPLAY_DEVICE pOutDevice) {
    DISPLAY_DEVICE displayDevice = {0};
    displayDevice.cb = sizeof(displayDevice);
    
    DWORD dwDevNum = 0;
    BOOL bRet = EnumDisplayDevices(NULL, dwDevNum, &displayDevice, EDD_GET_DEVICE_INTERFACE_NAME);
    while(bRet != 0) {
        if(wcscmp(monitorInfo.szDevice, displayDevice.DeviceName) == 0) {
            *pOutDevice = displayDevice;
            
            return true;
        }
        
        ++dwDevNum;
        memset(&displayDevice, 0, sizeof(displayDevice));
        displayDevice.cb = sizeof(displayDevice);
        bRet = EnumDisplayDevices(NULL, dwDevNum, &displayDevice, EDD_GET_DEVICE_INTERFACE_NAME);
    }
    
    return false;
}

// Toggle fullscreen
// TODO try SetWindowPlacement instead?
// TODO use maximized WS_POPUP to fill screen instead of manually setting rect?  check methods...
static bool toggleFillscreen(HWND hWnd, bool setFillscreen) {
    bool resultIsFullscreen = setFillscreen;
    
    static LONG_PTR savedWindowStyle = 0;
    static LONG_PTR savedWindowExStyle = 0;
    static RECT savedWindowRect;
    
    if(setFillscreen) {
        resultIsFullscreen = false;
        
        int x = 0;
        int y = 0;
        int w = GetSystemMetrics(SM_CXSCREEN);
        int h = GetSystemMetrics(SM_CYSCREEN);
        
        HMONITOR hMonitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);
        HMONITOR hPrimaryMonitor = MonitorFromWindow(NULL, MONITOR_DEFAULTTOPRIMARY);
        if(hMonitor != NULL && hPrimaryMonitor != NULL) {
            BOOL bRet;
            bool switchToFullscreen = false;
            bool gotPrimary = false;
            bool gotTarget = false;
            
            MONITORINFOEX primaryInfo;
            ZeroMemory(&primaryInfo, sizeof(primaryInfo));
            primaryInfo.cbSize = sizeof(primaryInfo);
            bRet = GetMonitorInfo(hPrimaryMonitor, &primaryInfo);
            if(bRet != 0) {
                gotPrimary = true;
            }
            
            MONITORINFOEX monitorInfo;
            ZeroMemory(&monitorInfo, sizeof(monitorInfo));
            monitorInfo.cbSize = sizeof(monitorInfo);
            bRet = GetMonitorInfo(hMonitor, &monitorInfo);
            if(bRet != 0) {
                x = monitorInfo.rcMonitor.left;
                y = monitorInfo.rcMonitor.top;
                w = monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left;
                h = monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top;
                
                gotTarget = true;
            }
            
            if(gotTarget && gotPrimary) {
                if(wcscmp(primaryInfo.szDevice, monitorInfo.szDevice) == 0) {
                    switchToFullscreen = true;
                }
                else {
                    DISPLAY_DEVICE primaryDevice;
                    bool gotPrimaryDevice = getDeviceForMonitor(primaryInfo, &primaryDevice);
                    
                    DISPLAY_DEVICE targetDevice;
                    bool gotTargetDevice = getDeviceForMonitor(monitorInfo, &targetDevice);
                    
                    if(gotPrimaryDevice && gotTargetDevice) {
                        // always false for secondary monitors, even when connected the same GPU as the primary monitor..
                        //bool isPrimaryDevice = ((primaryDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) != 0);

                        // according to docs for EDD_GET_DEVICE_INTERFACE_NAME DeviceID is supposed to contain 'something'.. seems to always be empty
                        //if(wcscmp(primaryDevice.DeviceID, targetDevice.DeviceID) == 0)) {
                        //    switchToFullscreen = true;
                        //}

                        // seems these registry key paths are per physical GPU apart from the last component which is an index
                        // so this should probably match monitors on the same physical GPU
                        for(size_t i = wcslen(primaryDevice.DeviceKey); i > 0; --i) {
                            if(primaryDevice.DeviceKey[i - 1] == L'\\')
                                break;
                            primaryDevice.DeviceKey[i - 1] = 0;
                        }
                        for(size_t i = wcslen(targetDevice.DeviceKey); i > 0; --i) {
                            if(targetDevice.DeviceKey[i - 1] == L'\\')
                                break;
                            targetDevice.DeviceKey[i - 1] = 0;
                        }
                        if(wcscmp(primaryDevice.DeviceKey, targetDevice.DeviceKey) == 0) {
                            switchToFullscreen = true;
                        }

                        // this only compares the name of the graphics card, identical for 2 GPUs of the same type..
                        //if(wcscmp(primaryDevice.DeviceString, targetDevice.DeviceString) == 0) {
                        //    switchToFullscreen = true;
                        //}
                    }
                }
            }

            if(switchToFullscreen) {
                GetWindowRect(hWnd, &savedWindowRect);
                if(GetWindowLongPtr(hWnd, GWL_STYLE) & WS_MAXIMIZE) {
                    savedWindowStyle = SetWindowLongPtr(hWnd, GWL_STYLE, WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_OVERLAPPED | WS_VISIBLE | WS_MAXIMIZE);
                    savedWindowExStyle = SetWindowLongPtr(hWnd, GWL_EXSTYLE, 0);
                }
                else {
                    savedWindowStyle = SetWindowLongPtr(hWnd, GWL_STYLE, WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_OVERLAPPED | WS_VISIBLE);
                    savedWindowExStyle = SetWindowLongPtr(hWnd, GWL_EXSTYLE, 0);
                }
                SetWindowPos(hWnd, HWND_TOPMOST, x, y, w, h, SWP_FRAMECHANGED | SWP_DRAWFRAME);
                
                resultIsFullscreen = true;
            }
            else
                MessageBeep(MB_OK);
        }
    }
    else {
        SetWindowLongPtr(hWnd, GWL_STYLE, savedWindowStyle | WS_VISIBLE);
        SetWindowLongPtr(hWnd, GWL_EXSTYLE, savedWindowExStyle);
        HWND hWndInsertAfter = HWND_NOTOPMOST;
        if((savedWindowExStyle & WS_EX_TOPMOST) == WS_EX_TOPMOST)
            hWndInsertAfter = HWND_TOPMOST;
        SetWindowPos(
            hWnd,
            hWndInsertAfter,
            savedWindowRect.left,
            savedWindowRect.top,
            savedWindowRect.right-savedWindowRect.left,
            savedWindowRect.bottom-savedWindowRect.top,
            SWP_FRAMECHANGED | SWP_DRAWFRAME
        );
    }
    
    return resultIsFullscreen;
}

And use like this:

(Won't work for more than one window in it's current form as the toggleFillscreen function has statics to save the window placement..)


bool currentFullscreen = false;

...

if(toggleKeyPressed)
  currentFullscreen = toggleFillscreen(hWnd, !currentFullscreen);

EDIT: added a retain topmost style

csisy
csisy

Thanks Erik, I'll check your solution soon and if it works, I'll try to find the difference which makes it work. :)

Edit:
An interesting difference: when creating a window I define a style based on the parameters. The return value of SetWindowLongPtr is the previous window style which should be the same as the manually defined style, but it's not. Is that normal?

sorry for my bad english
Erik Rufelt
Erik Rufelt

An interesting difference: when creating a window I define a style based on the parameters. The return value of SetWindowLongPtr is the previous window style which should be the same as the manually defined style, but it's not. Is that normal?

Depends, it can have WS_VISIBLE and others added.. also many styles commonly used when creating a window are actually combinations of several sub-styles.

csisy
csisy

I've shortened (and copy-pasted) the code and tested it:


void WindowsWindow::Reshape(uint32 width, uint32 height, WindowMode mode)
{
    if (mode != WindowMode::Windowed)
    {
        windowMode = mode;

        int32 x = 0;
        int32 y = 0;
        Adjust(x, y, width, height);

        SetWindowLongPtr(hwnd, GWL_STYLE, WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_OVERLAPPED | WS_VISIBLE);
        SetWindowLongPtr(hwnd, GWL_EXSTYLE, 0);
        SetWindowPos(hwnd, HWND_TOPMOST, x, y, width, height, SWP_FRAMECHANGED | SWP_DRAWFRAME);

        return;
    }

    // ...
}

This is called when I try to switch to borderless mode. The adjust function is nothing special:


void WindowsWindow::Adjust(int32& x, int32& y, uint32& width, uint32& height)
{
    if (windowMode == WindowMode::Windowed)
    {
        if ((windowedStyle & WS_BORDER) != 0) // hasBorder
        {
            RECT r = { x, y, x + width, y + height }; // left, top, right, bottom
            AdjustWindowRectEx(&r, windowedStyle, 0, extendedStyle);

            x = r.left;
            y = r.top;
            width = r.right - r.left;
            height = r.bottom - r.top;
        }
    }
    else
    {
        HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
        MONITORINFO monitorInfo;
        monitorInfo.cbSize = sizeof(MONITORINFO);
        ::GetMonitorInfoA(monitor, &monitorInfo);

        const int32 monitorWidth = monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left;
        const int32 monitorHeight = monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top;

        // use the width/height <= monitor's width/height
        width = Math::Min<int32>(monitorWidth, width);
        height = Math::Min<int32>(monitorHeight, height);

        // use the monitor's left/top
        x = monitorInfo.rcMonitor.left;
        y = monitorInfo.rcMonitor.top;
    }
}

And the problem is the same, strange un-updated window are appears. Maybe it's not a WinAPI problem? Or do I miss a message which should be handled? It seems like the OpenGL does not know that the window is changed. After I actually resize the window back and forth (1280x720 -> 1024x768 -> 1280x720), everything is fine.

sorry for my bad english
Erik Rufelt
Erik Rufelt

Show your game loop with PeekMessage as well as the WndProc.

csisy
csisy

The loop is pretty standard, this is called every frame:


void WindowsApplication::PumpMessages(const float32 dt)
{
    MSG msg = { 0 };

    while (PeekMessageA(&msg, nullptr, 0, 0, PM_REMOVE))
    {
        TranslateMessage(&msg);
        DispatchMessageA(&msg);
    }
}

LRESULT WindowsApplication::ProcessMessage(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
    static ModifierKeys modifiers;

    Windows::iterator it = windows.find(hwnd);
    GenericWindowPtr window = (it != windows.end()) ? it->second : nullptr;

    switch (msg)
    {
        // alt+f4 or X button
    case WM_CLOSE:
        REQUIRE(window != nullptr);
        messageHandler.OnWindowClose(window);
        return 0;

    case WM_DESTROY:
        REQUIRE(window != nullptr);
        windows.erase(hwnd);
        return 0;

    case WM_SIZE:
    {
        const int32 w = LOWORD(lparam);
        const int32 h = HIWORD(lparam);

        switch (wparam)
        {
        case SIZE_MINIMIZED:
            // minimized
            break;

        case SIZE_MAXIMIZED: // fall through
        case SIZE_RESTORED:
            if (window != nullptr)
                messageHandler.OnWindowResized(window, w, h);
            break;

        default:
            break;
        }
    }
        break;

    case WM_ACTIVATE:
    {
        REQUIRE(window != nullptr);
        const bool activated = (LOWORD(wparam) != WA_INACTIVE);
        messageHandler.OnWindowActivationChanged(window, activated);
    }
        return 0;

    // tons of input handler

    default:
        break;
    }

    return DefWindowProc(hwnd, msg, wparam, lparam);
}
sorry for my bad english
Endurion
Endurion

MSDN on SetWindowLong states:

Specifically, if you change any of the frame styles, you must call SetWindowPos with the SWP_FRAMECHANGED flag for the cache to be updated properly.

Fruny: Ftagn! Ia! Ia! std::time_put_byname! Mglui naflftagn std::codecvt eY'ha-nthlei!,char,mbstate_t>
Shaarigan
Shaarigan

So directly from my game engine (tested and working)

Setup a surface


WNDCLASSEXA wc = {0};
		if(!boolean_cast(GetClassInfoExA(GetModuleHandleA(0), WIN32_SURFACE_CLASS_NAME, &wc)))
		{
			wc.cbClsExtra = 0;
			wc.cbWndExtra = 0;
			wc.style = CS_OWNDC;
			wc.cbSize = sizeof(WNDCLASSEX);
			wc.hInstance = GetModuleHandleA(0);
			wc.lpfnWndProc = WndProc;
			wc.hCursor = LoadCursor(NULL, IDC_ARROW);
			wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
			wc.lpszClassName = WIN32_SURFACE_CLASS_NAME;
			wc.lpszMenuName = 0;

			if(!RegisterClassExA(&wc)) return 0;
		}
		
		handle = CreateWindowExA(WS_EX_APPWINDOW, WIN32_SURFACE_CLASS_NAME, 
							     name,
							     ((border) ? (WS_OVERLAPPEDWINDOW ^ WS_THICKFRAME) : WS_POPUP),
							     0, 
							     0,
							     CW_USEDEFAULT,
							     CW_USEDEFAULT,
							     0,
							     0,
							     (HINSTANCE) GetWindowLong((HWND) handle, GWL_HINSTANCE),
							     0);

		//SetWindowLongPtr((HWND) handle, GWLP_USERDATA, (LONG_PTR)inst);
		return handle;

Changing surface style (WS_THICKFRAME is used to determine if the surface is resizeable by user using the window border)


uint32 flags = GetWindowLongA((HWND) handle, GWL_STYLE);
		if(style == Surface::Windowed && __flag_set(flags, WS_POPUP))
		{
			flags ^= WS_POPUP;
			flags |= (WS_OVERLAPPEDWINDOW ^ WS_THICKFRAME);
		}
		else if(style == Surface::Borderless && __flag_set(flags, WS_OVERLAPPEDWINDOW))
		{
			flags ^= WS_OVERLAPPEDWINDOW;
			flags |= WS_POPUP;
		}
		else if(style == Surface::Borderless && (__flag_set(flags, (WS_OVERLAPPEDWINDOW ^ WS_THICKFRAME))))
		{
			flags ^= (WS_OVERLAPPEDWINDOW ^ WS_THICKFRAME);
			flags |= WS_POPUP;
		}

		SetWindowLongA((HWND) handle, GWL_STYLE, flags);

And to resize the surface I use


RECT rect = { 0 }; GetWindowRect((HWND) handle, ▭);
		SetWindowPos((HWND) handle, 0, rect.left, rect.top, width, height, SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER);

The WNDProc implementation is class function inside the surface that handles all necessary messages and posts any other message into the global message loop so the main game loop could react on


switch(message)
		{
			case WM_CLOSE:
			case WM_DESTROY:
			case WM_SIZE:
			case WM_DEVICECHANGE:
			{
				PostMessageA(0, message, wParam, lParam);
			}
			return 0;
			default: return DefWindowProcA(hWnd, message, wParam, lParam);
		}

You need to handle the WM_SIZE message in your game loop to resize the GL viewport to the new window size but anything else worked for me as intended (except the viewport was too small then and I needed to resize it)

csisy
csisy

MSDN on SetWindowLong states:

Specifically, if you change any of the frame styles, you must call SetWindowPos with the SWP_FRAMECHANGED flag for the cache to be updated properly.

That flag is added to the SetWindowPos function both in my original and your posted code.

@Shaarigan:
Thanks! Is it necessary to use xor instead of and/or? Besides that your code is similar to mine. Of course the WM_SIZE message is handled.

The main problem is that the client area is not changed. So if the window was in 1296x758 Windowed, it had 1280x720 client area (which is what I want!). So if I change the style only (from windowed to borderless) the client are is the same. If I change both the style AND the size of the window, everything works fine.

So this works:


if (key->GetName() == InputKeys::F1)
    mainWindow->Reshape(1024, 768, WindowMode::Windowed);
else if (key->GetName() == InputKeys::F2)
    mainWindow->Reshape(1280, 720, WindowMode::Borderless);

This isn't:


if (key->GetName() == InputKeys::F1)
    mainWindow->Reshape(viewportSize.width, viewportSize.height, WindowMode::Windowed);
else if (key->GetName() == InputKeys::F2)
    mainWindow->Reshape(viewportSize.width, viewportSize.height, WindowMode::Borderless);

I post a larger chunk of code:


// register class

WNDCLASSEXA wnd = { 0 };
wnd.cbSize = sizeof(wnd);
wnd.lpszClassName = WindowsWindow::WindowName;
wnd.hInstance = GetModuleHandle(nullptr);
wnd.lpfnWndProc = &WindowsApplication::WndProc;
wnd.style = CS_OWNDC; // | CS_HREDRAW | CS_VREDRAW
wnd.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW);
wnd.hCursor = LoadCursor(NULL, IDC_ARROW);

if (RegisterClassExA(&wnd) == 0)
{
    LOG_ERROR("Failed to register class");
    return nullptr;
}

// ...

// create window

bool WindowsWindow::Create(const GenericWindowPtr& parent, const GenericWindowDefinition& def)
{
    this->title = def.title;
    this->windowMode = def.windowMode;

    // first, setup styles
    GetStyleFromDef(def, windowedStyle, fullscreenStyle, extendedStyle);

    // then adjust window region and assign the correct style
    int32 x = def.x;
    int32 y = def.y;
    uint32 width = def.width;
    uint32 height = def.height;
    Adjust(x, y, width, height);

    const uint32 style = (def.windowMode == WindowMode::Windowed) ? windowedStyle : fullscreenStyle;

    HWND parentHWND = nullptr;
    if (parent)
    {
        // it's safe to cast here
        parentHWND = (static_cast<WindowsWindow*>(parent.get()))->GetHWND();
    }

    hwnd = CreateWindowExA(extendedStyle, WindowName, def.title.c_str(), style, x, y, width, height,
                           parentHWND, nullptr, GetModuleHandle(nullptr), nullptr);

    if (!hwnd)
    {
        LOG_ERROR("Failed to create window");
        return false;
    }

    return true;
}

void WindowsWindow::GetStyleFromDef(const GenericWindowDefinition& def, LONG& style, LONG& fullscreenStyle, LONG& exStyle)
{
    if (def.hasBorder)
    {
        exStyle = WS_EX_APPWINDOW;
        //style = WS_OVERLAPPED | WS_BORDER | WS_CAPTION;
        style = WS_BORDER | WS_CAPTION;

        if (def.supportSysMenu)
        {
            style |= WS_SYSMENU;

            if (def.supportMinimize)
                style |= WS_MINIMIZEBOX;

            if (def.supportMaximize)
                style |= WS_MAXIMIZEBOX;
        }
    }
    else
    {
        exStyle = WS_EX_WINDOWEDGE;
        style = WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;

        exStyle |= (def.showInTaskbar ? WS_EX_APPWINDOW : WS_EX_TOOLWINDOW);
    }

    if (def.isTopmost)
        exStyle |= WS_EX_TOPMOST;

    fullscreenStyle = WS_POPUP;
}

// the current Reshape function which works when the size is changed as well
// note that the problem appears when the client area is not changed

void WindowsWindow::Reshape(uint32 width, uint32 height, WindowMode mode)
{
    // TODO: fix this

    // change style based on window mode change
    if (windowMode != mode)
    {
        LONG currStyle = GetWindowLongPtr(hwnd, GWL_STYLE);

        if (mode == WindowMode::Windowed) // going windowed
        {
            currStyle &= ~fullscreenStyle;
            currStyle |= windowedStyle;
        }
        else // going full-screen
        {
            currStyle &= ~windowedStyle;
            currStyle |= fullscreenStyle;
        }

        SetWindowLongPtr(hwnd, GWL_STYLE, currStyle);

        windowMode = mode;
    }

    uint32 flags = SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_FRAMECHANGED;

    int32 x = 0;
    int32 y = 0;

    // adjust window size and position
    //if (windowMode == WindowMode::Windowed)
        flags |= SWP_NOMOVE;

    Adjust(x, y, width, height);

    SetWindowPos(hwnd, 0, x, y, width, height, flags);
}

// of course after the window is created:

ShowWindow(hwnd, SW_SHOW);

// OpenGL side:
// currently I'm just setting the viewport and calling glClear for testing purposes
// call list from GLIntercept:

glViewport(0,0,1280,720)
glClearColor(1.000000,0.000000,0.000000,1.000000)
glClear(GL_COLOR_BUFFER_BIT)
wglSwapBuffers(FF0117F9)=true 
sorry for my bad english
Shaarigan
Shaarigan

Thanks! Is it necessary to use xor instead of and/or?

The main problem is that the client area is not changed. So if the window was in 1296x758 Windowed, it had 1280x720 client area (which is what I want!). So if I change the style only (from windowed to borderless) the client are is the same. If I change both the style AND the size of the window, everything works fine

I observed that the old winapi may have stomacheaches when you try to remove the window resize border and using the xor keeps any other flag set alive when cutting out the old style for the new one. An other problem I observed with the style change was that the window resized properly the client area (on Windows 8.1) but also reset the position so that the window jumps a few pixels near the top left corner.

Thats the reason because I do also a repositioning of the surface after changing the style to keep it where it was.

csisy
csisy

Thanks for your replies!

Using xor does not solve my problem unfortunately. :(

BTW, don't you have to set the SWP_FRAMECHANGED flag to the SetWindowPos function?

I don't understand this whole process. I mean, if I change the size/resolution as well, everything works.

And also, if I start the application with a Windowed (WS_CAPTION, WS_BORDER, etc.) window, then switch to Borderless (WS_POPUP) mode, a WM_SIZE message is not sent. However, if I switch back to Windowed then to Borderless again, two WM_SIZE is sent, first with 1296x758 then with 1280x720.

"Stranger things have happened"

Edit:
Attached a simple test program (does nothing just clears the window with a red color). You can switch between windowed and borderless mode with F1 and F2 keys. Don't worry it's not a virus. :)

[attachment=32085:WindowStyleChange.zip]

If you check the red area of the windowed mode and the full area of the borderless (not-looking-properly) window, it's the same (1280x720) and this is what I want.

sorry for my bad english
Shaarigan
Shaarigan

BTW, don't you have to set the SWP_FRAMECHANGED flag to the SetWindowPos function?

To simply change the window size it didnt seem that I need to set it. Tested it and it worked as expected.

The Problems you have could that come from the


const GenericWindowPtr& parent, const GenericWindowDefinition& def

stuff you have in your creation function?

Have you checked your flag to remove both, WS_OVERLAPPEDWINDOW and WS_THICKFRAME?
The reason is that if WS_THICKFRAME is stil set there might be strange behaviors in the window manager

P.S.

I tested your testprogram and it worked as expected except the little framedrop when changing styles :wink:

Erik Rufelt
Erik Rufelt

I don't get it.. does it not work? I ran your program and see nothing strange.. maybe a difference between Windows versions or something..

You should post a single-file source code we can run instead of an exe if possible.

Anyway, I think your window styles look weird. Especially setting the style when going back to windowed mode.. save the style when you decide to switch to fullscreen and restore it to the saved style when going back to windowed.. removing the fullscreen style bits will break if the windowed mode has any of the same bits set. Many window-styles are combinations of several bits.

Also it looks really complicated, too much code to combine the styles, very difficult to follow exactly what bits will remain in the end.. easy to miss things.

I don't get making the window area the same either.. if you want a particular window-area in client-mode then handle that.. but what is the point of a borderless window that doesn't cover the screen?

Also getting multiple WM_SIZE shouldn't matter, you could get that anyway if the window is resized, I don't think those messages are supposed to be relied on to send an exact size exactly once or things like that. I'm not sure a resize or especially a style-change is guaranteed to be in any way atomic with respect to the message queue.

csisy
csisy

Thanks for the tests!

Hm, it's strange that it works for you. I get a little "un-updated" area at the top of the window, when changing to borderless mode.

@Shaarigan:

I have a more flexible setup for styles, so I'm not using nor overlapped nor thickframe directly. And also I don't let the user to resize the window by hand. So those styles are not set.

@Erik:

Hm, I'll probably change my code to the save/restore version, but I'm not sure if the current solution is wrong. First, I remove the fullscreen bits THEN add the windowed bits, so if the bits overlap, it's not a problem, because the correct bits will be set at the end. Also, the windowed style is not changed at run-time, it's defined only once, when the window is created.

Well... some players like to have a non-fullscreen window without borders and stuffs. I don't know why. If I can't get it working I won't let the user have a borderless window with smaller size than the monitor's resolution. :/

sorry for my bad english
Erik Rufelt
Erik Rufelt

Try updating your graphics drivers if you don't have the newest stable one already..

From your images it seems clear that the OpenGL part stays right where it was in respect to the top-left corner of the window (not the just the client-area).. whereas I guess the top left part that was the border before gets the background-color..

Could be a difference in what theme is used in Windows or if there are any window-helper plugins or something that causes the difference in behavior on different computers..

Try dragging another window on top of the window with the error and see if the OpenGL drawn part is properly updated..

I guess you call PumpMessages from a loop and always do glClear and Swap after that?

You could try only calling DefWindowProc always and skip any viewport and resize handling etc as glClear and Swap don't care about the viewport anyway, and maybe add a Sleep(10) or something in the loop to avoid insane update-rates..

csisy
csisy

From your images it seems clear that the OpenGL part stays right where it was in respect to the top-left corner of the window (not the just the client-area).. whereas I guess the top left part that was the border before gets the background-color..

Exactly! And my FRAPS displays fps at the bottom-right corner and after the style change, the fps counter is invisible, because it's "outside" of the window. It's like the contexts are not updated properly here.

Unfortunately nothing helps :(

sorry for my bad english
Erik Rufelt
Erik Rufelt

Is that FRAPS display always active? If so, try disabling it.. it probably overrides a bunch of GL calls to put its overlay there.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.