Advertisement

Simple win32 window can not be resized/moved

Started by November 24, 2021 07:03 PM
8 comments, last by sarahm 2 years, 11 months ago

Hello,

I'm using winapi to create a window for my opengl rendering. My simple opengl scene shows up fine, but when dragging the title bar, my window does not move. Also, moving the mouse cursor near the window borders does not change the cursor to a resizing cursor and thus the window can not be resized. On top of that, clicking the minimize/maximize/close buttons of the window does not do anything.

My window does react to wm_input events though.

My game loop looks like this:

	Application* m_app = new Application();

    // while app is running
    while(m_app->isRunning())
    {
        // run
        m_app->iterateProcessingLoop();

        // sleep for a few msecs
        m_app->sleep();
    }

Within iterateProcessingLoop(), I call my message pump:

m_window->pumpSystemEvents();

which is just your run-of-the-mill message pump:

void WindowsWindow::pumpSystemEvents()
{
    //std::cout << "[WindowsWindow::pumpSystemEvents] called." << std::endl;
    //message handling
    while(PeekMessage(&m_msg,nullptr,0,0,PM_REMOVE))
    {
        TranslateMessage(&m_msg);
        DispatchMessage(&m_msg);
    }
}

I create my window like this:

WindowsWindow::WindowsWindow()
{
	m_hWnd = nullptr;							// Holds Our Window Handle
    m_hInstance = nullptr;

	WNDCLASS	wc;							// Windows Class Structure
	DWORD		dwExStyle;						// Window Extended Style
	DWORD		dwStyle;						// Window Style
	RECT WindowRect;							// Grabs Rectangle Upper Left / Lower Right Values
	WindowRect.left=(long)0;						// Set Left Value To 0
	WindowRect.right=(long)getWindowWidth();						// Set Right Value To Requested Width
	WindowRect.top=(long)0;							// Set Top Value To 0
	WindowRect.bottom=(long)getWindowHeight();						// Set Bottom Value To Requested Height

	m_hInstance		= GetModuleHandle(nullptr);			// Grab An Instance For Our Window
	wc.style		= CS_HREDRAW | CS_VREDRAW | CS_OWNDC;		// Redraw On Move, And Own DC For Window
	wc.lpfnWndProc		= (WNDPROC) &WindowsWindow::WndProc;				// WndProc Handles Messages
	wc.cbClsExtra		= 0;						// No Extra Class Data
	wc.cbWndExtra		= 0;						// No Extra Window Data
	wc.hInstance		= m_hInstance;					// Set The Instance
	wc.hIcon		= LoadIcon(nullptr, IDI_WINLOGO);			// Load The Default Icon
	wc.hCursor		= LoadCursor(nullptr, IDC_ARROW);			// Load The Arrow Pointer
	wc.hbrBackground	= nullptr;						// No Background Required For GL
	wc.lpszMenuName		= nullptr;						// We Don't Want A Menu
	wc.lpszClassName	= "Cow2019WindowsWindow";					// Set The Class Name

    if (!RegisterClass(&wc))						// Attempt To Register The Window Class
	{
		MessageBox(nullptr,"Failed To Register The Window Class.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return;
		//return FALSE;							// Exit And Return FALSE
	}

	dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;	// Window Extended Style
    dwStyle=WS_OVERLAPPEDWINDOW;					// Windows Style

    AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);		// Adjust Window To True Requested Size

    if (!(m_hWnd = CreateWindowEx(	dwExStyle,				// Extended Style For The Window
					"Cow2019WindowsWindow\0",				// Class Name
					"Cow2019\0",					// Window Title
					WS_CLIPSIBLINGS |			// Required Window Style
					WS_CLIPCHILDREN |			// Required Window Style
					dwStyle,				// Selected Window Style
					0, 0,					// Window Position
					WindowRect.right-WindowRect.left,	// Calculate Adjusted Window Width
					WindowRect.bottom-WindowRect.top,	// Calculate Adjusted Window Height
					nullptr,					// No Parent Window
					nullptr,					// No Menu
					m_hInstance,				// Instance
					this)))					// Pass self to WM_CREATE
	{
		MessageBox(nullptr,"Window Creation Error.\0","ERROR\0",MB_OK|MB_ICONEXCLAMATION);
		return;
	}
}

Then I create the opengl context like this:

WindowsOpenGLContext::WindowsOpenGLContext(void* p_hWnd)
{
	m_hRC = nullptr;
    m_hDC = nullptr;
    m_hWnd = (HWND)p_hWnd;

    // device context
    m_hDC = GetDC(m_hWnd); // Get the device context for our window

    PIXELFORMATDESCRIPTOR pfd; // Create a new PIXELFORMATDESCRIPTOR (PFD)
    memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR)); // Clear our  PFD
    pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); // Set the size of the PFD to the size of the class
    pfd.dwFlags = PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW; // Enable double buffering, opengl support and drawing to a window
    pfd.iPixelType = PFD_TYPE_RGBA; // Set our application to use RGBA pixels
    pfd.cColorBits = 32; // Give us 32 bits of color information (the higher, the more colors)
    pfd.cDepthBits = 32; // Give us 32 bits of depth information (the higher, the more depth levels)
    pfd.iLayerType = PFD_MAIN_PLANE; // Set the layer of the PFD

    //set pixel format
    int nPixelFormat = ChoosePixelFormat(m_hDC, &pfd); // Check if our PFD is valid and get a pixel format back
    if(nPixelFormat == 0) // If it fails
    {
        MessageBox(nullptr,"Unable to choose pixel format.\0","ERROR\0",MB_OK|MB_ICONEXCLAMATION);
        return;
    }

    bool bResult = SetPixelFormat(m_hDC, nPixelFormat, &pfd); // Try and set the pixel format based on our PFD
    if(!bResult) // If it fails
    {
        MessageBox(nullptr,"Unable to set pixel format.\0","ERROR\0",MB_OK|MB_ICONEXCLAMATION);
        return;
    }

    //temporary openGL 2.1 context
    HGLRC tempOpenGLContext = wglCreateContext(m_hDC); // Create an OpenGL 2.1 context for our device context
    wglMakeCurrent(m_hDC, tempOpenGLContext); // Make the OpenGL 2.1 context current and active

    //fetch context creation procedure
    PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL;
    wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) wglGetProcAddress("wglCreateContextAttribsARB");

    if(wglCreateContextAttribsARB == nullptr)
    {
        MessageBox(nullptr,"Initialization Failed - could not fetch wglCreateContextAttribsARB.","ERROR",MB_OK|MB_ICONEXCLAMATION);
        return;
    }

    //attributes of 3.x/4.x context
    int attributes[] = {
        WGL_CONTEXT_MAJOR_VERSION_ARB, m_openGLMajorVersion, // Set the MAJOR version of OpenGL
        WGL_CONTEXT_MINOR_VERSION_ARB, m_openGLMinorVersion, // Set the MINOR version of OpenGL
        WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB | WGL_CONTEXT_DEBUG_BIT_ARB, // Set our OpenGL context to be forward compatible & debug
        0
    };

    //actually create the context
    m_hRC = wglCreateContextAttribsARB(m_hDC, NULL, attributes); // Create and OpenGL 3.x context based on the given attributes

    wglMakeCurrent(NULL, NULL); // Remove the temporary context from being active
    wglDeleteContext(tempOpenGLContext); // Delete the temporary OpenGL 2.1 context
    wglMakeCurrent(m_hDC, m_hRC); // Make our OpenGL 4.2 context current
}

Then I show the window:

void WindowsWindow::showWindow()
{
    //show window
	ShowWindow(m_hWnd,SW_SHOW);						// Show The Window
	SetForegroundWindow(m_hWnd);						// Slightly Higher Priority
	SetFocus(m_hWnd);                                // set keyboard focus
	UpdateWindow(m_hWnd);                             // update
}

And this is my wndproc:

// wndproc processes messages
LRESULT CALLBACK WindowsWindow::WndProc(
                HWND	p_hWnd,					// Handle For This Window
				UINT	uMsg,					// Message For This Window
				WPARAM	wParam,					// Additional Message Information
				LPARAM	lParam)					// Additional Message Information
{
    // dbg
    //std::cout << "windowProc called." << std::endl;

    WindowsWindow* ctxptr = nullptr;

   // Get pointer to window
   if(uMsg == WM_CREATE)
   {
      ctxptr = (WindowsWindow*)((LPCREATESTRUCT)lParam)->lpCreateParams;
      SetWindowLongPtr(p_hWnd,GWLP_USERDATA,(LONG_PTR)ctxptr);
   }
   else
   {
     ctxptr = (WindowsWindow*)GetWindowLongPtr(p_hWnd,GWLP_USERDATA);
   }

    // for the first three calls, the ctx is empty
    if(ctxptr == nullptr)
    {
        //std::cout << "context is nullptr in windowProc!" << std::endl;
        return DefWindowProc(p_hWnd, uMsg, wParam, lParam);
    }
    //std::cout << "windowProc called" << std::endl;

	switch (uMsg)								// Check For Windows Messages
	{
	    case WM_QUIT:				// Have We Received A Quit Message?
        {
            // send & handle quit event
            ctxptr->getEventHandler()->queueEvent(new QuitTriggeredEvent("WM_QUIT received"));
            return 0;
        }
        case WM_ACTIVATEAPP:
        {
            ctxptr->setActive((bool)wParam);					// Program Is Active
			return DefWindowProc(p_hWnd,uMsg,wParam,lParam);						// Return To The Message Loop
		}
		case WM_ACTIVATE:						// Watch For Window Activate Message
		{
			if (!(bool)HIWORD(wParam))					// Check Minimization State
			{
				ctxptr->setActive(true);					// Program Is Active
				//SetCapture(p_hWnd);
			}
			else
			{
				ctxptr->setActive(false);					// Program Is No Longer Active
				//ReleaseCapture();
			}

			return DefWindowProc(p_hWnd,uMsg,wParam,lParam);						// Return To The Message Loop
		}
		case WM_SYSCOMMAND:						// Intercept System Commands
		{
			switch (wParam)						// Check System Calls
			{
				case SC_SCREENSAVE:				// Screensaver Trying To Start?
				case SC_MONITORPOWER:				// Monitor Trying To Enter Powersave?
                    return 0;					// Prevent From Happening
				case SC_MINIMIZE:
                    ctxptr->setActive(false);   // Program Is No Longer Active
                    return DefWindowProc(p_hWnd,uMsg,wParam,lParam);
                case SC_MAXIMIZE:
                    ctxptr->setActive(true);   // Program Is No Longer Active
                    return DefWindowProc(p_hWnd,uMsg,wParam,lParam);
                default:
                    return DefWindowProc(p_hWnd,uMsg,wParam,lParam);
			}
			//break;
		}
		case WM_CLOSE:							// Did We Receive A Close Message?
		{
			// Send A Quit Message
			//ctxptr->getEventHandler()->queueEvent(new QuitTriggeredEvent("WM_CLOSE received"));
			DestroyWindow(p_hWnd);
			return 0;						// Jump Back
		}
		case WM_DESTROY:
        {
            PostQuitMessage(0);
            return 0;
        }
		// raw input messages
		// input device change, e.g. un-/plug
		// TODO: listen for and act upon, e.g. pause game if controller removed
//		case WM_INPUT_DEVICE_CHANGE:
//        {
//            return 0;
//        }
		// input device, e.g. button press or axis change:
		case WM_INPUT:
        {
            UINT dwSize;

            GetRawInputData((HRAWINPUT)lParam, RID_INPUT, nullptr, &dwSize, sizeof(RAWINPUTHEADER));
            LPBYTE lpb = new BYTE[dwSize];
            if (lpb == nullptr)
            {
                return 0;
            }

            if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER)) != dwSize )
            {
                //OutputDebugString (TEXT("GetRawInputData does not return correct size !\n"));
                return 0;
            }

            RAWINPUT* raw = (RAWINPUT*)lpb;

            //raw->header.hDevice

            if (raw->header.dwType == RIM_TYPEKEYBOARD)
            {
                // TODO: translate scancodes here or elsewhere?
                //std::pair<std::string, int> keyName = ctxptr->GetKeyName(raw->data.keyboard.VKey, raw->data.keyboard.MakeCode, raw->data.keyboard.Flags);

                //raw->data.keyboard
                if(WM_KEYDOWN == raw->data.keyboard.Message){
                    //global quit regardless of mode
                    if (raw->data.keyboard.VKey == VK_F10)				// Was F10 Pressed?
                    {
                        // quit
                        ctxptr->getEventHandler()->queueEvent(new QuitTriggeredEvent("user pressed F10"));
                    }
                    else if (raw->data.keyboard.VKey == VK_F9)				// Was F9 Pressed?
                    {
                        if(ctxptr->isFullScreen())
                        {
                            ctxptr->makeWindowed();
                        }
                        else
                        {
                            ctxptr->makeFullscreen();
                        }
                    }
                    else
                    {
                        // // key event
                        ctxptr->getEventHandler()->queueEvent(new KeyEvent(false, raw->data.keyboard.VKey, raw->data.keyboard.MakeCode));
                    }
                }
                else if(WM_KEYUP == raw->data.keyboard.Message)
                {
                    // key event
                    ctxptr->getEventHandler()->queueEvent(new KeyEvent(true, raw->data.keyboard.VKey, raw->data.keyboard.MakeCode));
                }
            }
            else if (raw->header.dwType == RIM_TYPEMOUSE)
            {
                //if mouse is sending relative data...
                if ( MOUSE_MOVE_RELATIVE == raw->data.mouse.usFlags)
                {
                    //update mouse position
                    int mx = raw->data.mouse.lLastX;
                    int my = raw->data.mouse.lLastY;

                    if((mx != 0)||(my != 0))
                    {
                        //mouse moved
                        //mGame->getEventHandler()->queueEvent(new MouseMoveEvent((float)mx, (float)my), mlog);
                        ctxptr->getEventHandler()->queueEvent(new MouseMoveEvent((float)mx, (float)my));
                    }

                    //mouse buttons pressed
                    if((raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_DOWN))
                    {
                        ctxptr->getEventHandler()->queueEvent(new MouseButtonEvent(1, false));
                    }

                    if((raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_DOWN))
                    {
                        ctxptr->getEventHandler()->queueEvent(new MouseButtonEvent(2, false));
                    }

                    if((raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_DOWN))
                    {
                        ctxptr->getEventHandler()->queueEvent(new MouseButtonEvent(3, false));
                    }

                    if((raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_DOWN))
                    {
                        ctxptr->getEventHandler()->queueEvent(new MouseButtonEvent(4, false));
                    }

                    if((raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_DOWN))
                    {
                        ctxptr->getEventHandler()->queueEvent(new MouseButtonEvent(5, false));
                    }

                    //mouse buttons released
                    if((raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_UP))
                    {
                        ctxptr->getEventHandler()->queueEvent(new MouseButtonEvent(1, true));
                    }

                    if((raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_UP))
                    {
                        ctxptr->getEventHandler()->queueEvent(new MouseButtonEvent(2, true));
                    }

                    if((raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_UP))
                    {
                        ctxptr->getEventHandler()->queueEvent(new MouseButtonEvent(3, true));
                    }

                    if((raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_UP))
                    {
                        ctxptr->getEventHandler()->queueEvent(new MouseButtonEvent(4, true));
                    }

                    if((raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_UP))
                    {
                        ctxptr->getEventHandler()->queueEvent(new MouseButtonEvent(5, true));
                    }

                    //mouse wheel
                    if (raw->data.mouse.usButtonFlags & RI_MOUSE_WHEEL) // If the current message has a mouse_wheel message
                    {
                          if ((SHORT)raw->data.mouse.usButtonData > 0)
                          {
                                ctxptr->getEventHandler()->queueEvent(new MouseWheelEvent(1));
                          }
                          else if ((SHORT)raw->data.mouse.usButtonData < 0)
                          {
                                ctxptr->getEventHandler()->queueEvent(new MouseWheelEvent(-1));
                          }
                    }
                }
            }
            //cleanup
            delete raw;

            //delete[] lpb;
            return 0;
        }
		case WM_SIZE:							// Resize The OpenGL Window
		{
            // LoWord=Width, HiWord=Height
            ctxptr->getEventHandler()->queueEvent(new WindowResizeTriggeredEvent(LOWORD(lParam), HIWORD(lParam)));

			return 0;						// Jump Back
		}
		case WM_SIZING:
        {
            return DefWindowProc(p_hWnd,uMsg,wParam,lParam);
        }

        default:
            // Pass All Unhandled Messages To DefWindowProc
            return DefWindowProc(p_hWnd,uMsg,wParam,lParam);
	}

	// Pass All Unhandled Messages To DefWindowProc
	return DefWindowProc(p_hWnd,uMsg,wParam,lParam);
}

Even though most messages I touch are forwarded to DefWindowProc, my window won't move/resize.

Advertisement

You also have to set WS_SIZEBOX as style when creating the window.

I was personally also having additional issues where the window would then not resize most of the time (but the cursor would change), so let me know if this is enough to fix it for you.

@Juliean

WS_SIZEBOX is the same as WS_THICKFRAME, which is already included via WS_OVERLAPPEDWINDOW. I tried adding WS_SIZEBOX to the window style anyway, but it did not change anything.

Yeah, you're right, I missed that. Fng winapi .. :D

Have you set a breakpoint in Visual Studio, for example to catch if your window receives the sizing message or a syscommand is fired? First stept to bedug any WM related issues is to find out if messaging works properly. You also don't need to catch syscommand (which might be a cause for your error) in order to find out if your window still has the focus and/or is the active window. You can either listen for focus changes or active window changes or catch when the window state has changed (maximized, minimized, normal). Windows message pump is messy, can't say anything else, so catch only what you need and leave the rest to Windows.

I implemented a full C# API for Windows window handling some time ago for our SDK which can be found here https://github.com/SchroedingerEntertainment/Hyperion/blob/main/Desktop/Platform/Win32/Mixin/Window.cs#L180​

Have a look at how we handle messages and maybe this helps you getting your issues solved

Advertisement

Stepping through with a debugger (I'm using code::blocks with mingw/gdb btw.), I'm getting

WM_ACTIVATEAPP
WM_ACTIVATE
WM_SIZE

and then my window shows up and I start receiving WM_INPUT messages.

WM_INPUT isn't everything you get, there is a lot more, you should log for it, especially if you want to know what happens when you hit the minimize box or hover the resizeable border.

I'm using RAWINPUT as well in our code module to detect when to close a context or popup window and we never had any issues with resizing or minimizing the window by user input. So either you're missing some messages for whatever reason or you setup RAWINPUT in the wrong way

The way I register for raw input is

RAWINPUTDEVICE Rid[2];

//    //rawinputdevice for mouse
	Rid[0].usUsagePage = 0x01;
    Rid[0].usUsage = 0x02;
    Rid[0].dwFlags = RIDEV_NOLEGACY;
    Rid[0].hwndTarget = 0;
//
//    //rawinputdevice for keyboard
    Rid[1].usUsagePage = 0x01;
    Rid[1].usUsage = 0x06;
    Rid[1].dwFlags = RIDEV_NOLEGACY;
    Rid[1].hwndTarget = 0;

    if (RegisterRawInputDevices(Rid, 2, sizeof(Rid[0])) == FALSE) {
        //registration failed. Call GetLastError for the cause of the error
        DWORD errornum = GetLastError();

        (*getLog()) << "[WindowsDeviceEnumerator::registerDefaultDevices] Could not register raw input for mouse and keyboard, errornumber:" << errornum << std::endl;
        return false;
    }
    return true;

This topic is closed to new replies.

Advertisement