1
votes

I've been playing around with the Windows api for uni, but I cant get the window messages to call WM_PAINT after the inital creation. It calls it when the window is created but not after.

All Other messages get called! Just cant get the WM_PAINT to be called.

This is part of a game lib we have to make using the Windows api, (the goal is to learn abit of the windows api, rather than the intricacies of making game libs).

From what I gather the UpdateWindow(handle) command calls inititates the WM_PAINT message, but no matter where I put this command (currently in the game loop) I cant get it to call the message.

There's a lot of code, so I've trimmed it a little.

I know I have multiple WM_PAINT cases, they are a result of trying to make sure its getting through.

Window Creation HWND hwnd;

    hwnd = CreateWindowEx(WS_EX_APPWINDOW | WS_EX_WINDOWEDGE,
        L"FirstWindowClass",
        L"Mootlib",
        WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        m_screenWidth,
        m_screenHeight,
        NULL,
        NULL,
        WindowsUtilities::windowInstance(),
        NULL);

Register the Window

    WNDCLASSEX  wcex;                                   

    wcex.cbSize        = sizeof (wcex);             
    wcex.style         = CS_HREDRAW | CS_VREDRAW;       
    wcex.lpfnWndProc   = windowProc;
    wcex.cbClsExtra    = 0;                             
    wcex.cbWndExtra    = 0;                             
    wcex.hInstance     = WindowsUtilities::windowInstance();
    wcex.hIcon         = 0; 
    wcex.hCursor       = LoadCursor (NULL, IDC_ARROW);  

    wcex.hbrBackground = (HBRUSH) (COLOR_WINDOW+1);
    wcex.lpszMenuName  = NULL;                          
    wcex.lpszClassName = L"FirstWindowClass";               
    wcex.hIconSm       = 0; 

    RegisterClassEx (&wcex);

Winproc

    LRESULT CALLBACK windowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        LONG_PTR lptr = GetWindowLongPtr(hwnd, GWLP_USERDATA);

        switch(message)
        {
        case(WM_PAINT):
            int f = 3;
        }

        if (lptr)
        {
            Window* obj = reinterpret_cast<Window*>(lptr);
            return obj->handleWindowsMessages(message, wParam, lParam);
        }
        else
            return DefWindowProc(hwnd, message, wParam, lParam);
    }

Internal command

LRESULT Window::handleWindowsMessages(UINT message, WPARAM wParam, LPARAM lParam)
{

    std::wstring szHello = L"Hello, world!";
    PAINTSTRUCT ps;
    HDC hdc;

    switch(message)
    {
    case(WM_PAINT):
        hdc = BeginPaint(m_handle, &ps); 
        TextOut(hdc, 0, 0, L"Hello, Windows!", 15); 
        EndPaint(m_handle, &ps); 
        return 0; 

    case(WM_CLOSE):
        quitGame();
        return 0;

    // Move Me!!!
    case(WM_KEYDOWN):
    case(WM_LBUTTONDOWN):
    case(WM_RBUTTONDOWN):
        inputManager().addButtonEvent(reinterpret_cast<int&>(wParam), BUTTON_DOWN);
        return 0;

    // Move Me!!!
    case(WM_KEYUP):
    case(WM_LBUTTONUP):
    case(WM_RBUTTONUP):
        inputManager().addButtonEvent(reinterpret_cast<int&>(wParam), BUTTON_UP);
        return 0;   
    }

    return DefWindowProc(handle(), message, wParam, lParam);
}

Game Loop

        while(IsWindowVisible(handle()))
        {
            UpdateWindow(handle());
            WindowsUtilities::processMessages();

            draw();
            update();

            inputManager().update();
            inputManager().clearButtonUpEvents(); // this should be in the input update()
        }

Thanks.

4
First thought, from the title, did you set WS_VISIBLE?Daniel Mošmondor

4 Answers

3
votes

First, I really don't like your game loop as its not at all obvious where the windows message pump is.

Somewhere along the line you need to call PeekMessage() or GetMessage() and TranslateMessage() & DispatchMessage() on any messages retrieved. Lots of people unfamiliar with windows will filter the messages, either in a range, or for a window: don't - always pump all messages for all windows on the current thread.

Then you need to call InvalidateRect to mark the areas of the window you want repainted. Games will want to pass FALSE as getting the background painted is just a waste of time when youre painting the entire scene in WM_PAINT.

RedrawWindow is useful for a game if you just want to mark an entire part of the window dirty and repaint it in one step. The advantage of InvalidateRect is that it just adds the rectangle to a dirty region, so if your game consists of just a few small 'active' sprites, as each sprite animates you can mark just the corresponding rect dirty, and windows will collect the dirty rects and paint them in a batch when next you call UpdateWindow, or let the message loop generate a WM_PAINT message.

1
votes

Here's a snippet from the canonical Windows code, auto-generated when you create a new Win32 project in Visual Studio:

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hInst = hInstance; // Store instance handle in our global variable

   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

Note what's missing in yours: you forgot to call ShowWindow().

0
votes

If you want WM_PAINT to be called, you can use RedrawWindow or InvalidateRect.

-1
votes

MSDN states:

The UpdateWindow function updates the client area of the specified window by sending a WM_PAINT message to the window if the window's update region is not empty.

Your issue is that you haven't set an update region.

You could just do:

SendMessage( handle(), WM_PAINT, 0, 0 );