0

I've been making a Window class for a game, and I'm having trouble with the message pump.

I pull events off the Windows-provided message queue and send them to the windows they pertain to. Here's the function that Translates and Dispatches.

From what I remember of Win32 programming, Translating and Dispatching a message calls the specified WindowProc with the message's contents for parameters. So here's the WindowProc I specified...

currWin, currhwnd and winMap are defined as variables local to Window.cpp, at the top...

Anyway, calling distributeSystemMessages() seems to cause an infinite loop.

IMPORTANT NOTE: THE GAME LOOP IS NOT INSIDE THE MESSAGE HANDLING LOOP, AND NEITHER IS THE MESSAGE HANDLING CODE. THE MESSAGE HANDLING LOOP SHOULD EMPTY THE MESSAGE QUEUE ONCE PER FRAME, SENDING EACH MESSAGE TO THE WINDOW IT PERTAINS TO.

Here's Window.h...

#ifndef WINDOW_H_INCLUDED
#define WINDOW_H_INCLUDED

#include "SystemMessage.h"
#include <queue>
#include <windows.h>
using namespace std;

///THIS FUNCTION MUST BE CALLED TO GET MESSAGES INTO WINDOW QUEUES
void distributeSystemMessages();

class Window
{
protected:
    HDC hDC;
    HWND hWnd;
    HINSTANCE hInst;
    HGLRC hRC;

public:
    queue<SystemMessage> messages;

    Window(unsigned int width, unsigned int height, const char* name, unsigned int colorBits = 24, unsigned int depthBits = 24, unsigned int stencilBits = 0);
    ~Window();

    void makeContextCurrent();
    void swapBuffers();

    unsigned int getHeight();
    unsigned int getWidth();

    int getMouseX();
    int getMouseY();

    void setSize(unsigned int width, unsigned int height);
};

#endif // WINDOW_H_INCLUDED

Here's Window.cpp...

#include "Window.h"
#include <map>
#include <cstdio>
#include <GLee.h>
#include <GL/gl.h>
#include <GL/glu.h>
using namespace std;

HWND currhwnd;
Window* currWin;
map<HWND, Window*> winMap = map<HWND, Window*>();

LRESULT CALLBACK MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    if(currhwnd != hwnd)
    {
        map<HWND, Window*>::iterator i = winMap.find(hwnd);
        if(i != winMap.end())
        {
            currWin = (*i).second;
            currhwnd = hwnd;
        }
        else return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }

    SystemMessage msg(hwnd, uMsg, wParam, lParam);

    currWin->messages.push(msg);

    return 0;
}

Window::Window(unsigned int width, unsigned int height, const char* name, unsigned int colorBits, unsigned int depthBits, unsigned int stencilBits)
{
    //TODO: ADD TIME FUNCTIONS TO A TIMER CLASS
    //QueryPerformanceCounter(&startTime);
    //lastTime = startTime;
    messages = queue<SystemMessage>();


    hInst = GetModuleHandle(NULL);

    WNDCLASSEX wincl;        /* Data structure for the windowclass */

    /* The Window structure */
    wincl.hInstance = hInst;
    wincl.lpszClassName = "Squirrel Engine Window";
    wincl.lpfnWndProc = MainWndProc;      /* This function is called by windows */
    wincl.style = CS_DBLCLKS;                 /* Catch double-clicks */
    wincl.cbSize = sizeof (WNDCLASSEX);

    /* Use default icon and mouse-pointer */
    wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    wincl.lpszMenuName = NULL;                 /* No menu */
    wincl.cbClsExtra = 0;                      /* No extra bytes after the window class */
    wincl.cbWndExtra = 0;                      /* structure or the window instance */
    /* Use Windows's default colour as the background of the window */
    wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;

    /* Register the window class, and if it fails quit the program */
    if (!RegisterClassEx (&wincl))
    {
        printf("Could not register window class.\n");
        return;
    }

    /* The class is registered, let's create the program*/
    hWnd = CreateWindowEx (
           0,                   /* Extended possibilites for variation */
           "Squirrel Engine Window",/* Classname */
           name,       /* Title Text */
           WS_OVERLAPPEDWINDOW, /* default window */
           CW_USEDEFAULT,       /* Windows decides the position */
           CW_USEDEFAULT,       /* where the window ends after on the screen */
           10,                 /* The programs width */
           10,                 /* and height in pixels */
           HWND_DESKTOP,        /* The window is a child-window to desktop */
           NULL,                /* No menu */
           hInst,       /* Program Instance handler */
           NULL                 /* No Window Creation data */
           );

    RECT rcWindow;
    RECT rcClient;

    GetWindowRect(hWnd, &rcWindow);
    GetClientRect(hWnd, &rcClient);

    POINT ptDiff;
    ptDiff.x = (rcWindow.right - rcWindow.left) - rcClient.right;
    ptDiff.y = (rcWindow.bottom - rcWindow.top) - rcClient.bottom;

    MoveWindow(hWnd,rcWindow.left, rcWindow.top, width + ptDiff.x, height + ptDiff.y, TRUE);

    ShowWindow (hWnd, SW_SHOW);

    hDC = GetDC( hWnd );

    PIXELFORMATDESCRIPTOR pfd;
    ZeroMemory( &pfd, sizeof( pfd ) );
    pfd.nSize = sizeof( pfd );
    pfd.nVersion = 1;
    pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL |
                  PFD_DOUBLEBUFFER;
    pfd.iPixelType = PFD_TYPE_RGBA;
    pfd.cColorBits = colorBits;
    pfd.cDepthBits = depthBits;
    pfd.cStencilBits = stencilBits;
    pfd.iLayerType = PFD_MAIN_PLANE;
    int iFormat = ChoosePixelFormat( hDC, &pfd );
    SetPixelFormat( hDC, iFormat, &pfd );

    hRC = wglCreateContext(hDC);
    hDC = hDC;

    //makeContextCurrent();

    winMap[hWnd] = this;
    //TODO: Find out what this function does
    wglSwapIntervalEXT(0);
}

Window::~Window()
{
    wglDeleteContext(hRC);
    ReleaseDC(hWnd, hDC);
    winMap.erase(hWnd);
    PostQuitMessage(0);
}

unsigned int Window::getWidth()
{
    RECT rcClient;
    GetClientRect(hWnd, &rcClient);
    return rcClient.right;
}
unsigned int Window::getHeight()
{
    RECT rcClient;
    GetClientRect(hWnd, &rcClient);
    return rcClient.bottom;
}

void Window::setSize(unsigned int width, unsigned int height)
{
    RECT rcWindow;
    RECT rcClient;

    GetWindowRect(hWnd, &rcWindow);
    GetClientRect(hWnd, &rcClient);

    POINT ptDiff;
    ptDiff.x = (rcWindow.right - rcWindow.left) - rcClient.right;
    ptDiff.y = (rcWindow.bottom - rcWindow.top) - rcClient.bottom;

    MoveWindow(hWnd,rcWindow.left, rcWindow.top, width + ptDiff.x, height + ptDiff.y, TRUE);
}

void Window::makeContextCurrent()
{
    wglMakeCurrent( hDC, hRC );
}

void Window::swapBuffers()
{
    SwapBuffers( hDC );
}

int Window::getMouseX()
{
    POINT p;
    GetCursorPos(&p);

    RECT rcWindow;
    RECT rcClient;

    GetWindowRect(hWnd, &rcWindow);
    GetClientRect(hWnd, &rcClient);

    POINT ptDiff;
    ptDiff.x = (rcWindow.right - rcWindow.left) - rcClient.right;
    ptDiff.y = (rcWindow.bottom - rcWindow.top) - rcClient.bottom;


    return p.x - (rcWindow.left + ptDiff.x);
}
int Window::getMouseY()
{
    POINT p;
    GetCursorPos(&p);

    RECT rcWindow;
    RECT rcClient;

    GetWindowRect(hWnd, &rcWindow);
    GetClientRect(hWnd, &rcClient);

    POINT ptDiff;
    ptDiff.x = (rcWindow.right - rcWindow.left) - rcClient.right;
    ptDiff.y = (rcWindow.bottom - rcWindow.top) - rcClient.bottom;

    return p.y - (rcWindow.top + ptDiff.y);
}

void distributeSystemMessages()
{
    MSG messages;
    while(PeekMessage (&messages, NULL, 0, 0, PM_REMOVE))
    {
        printf("MessageLoop\n");
        TranslateMessage(&messages);
        DispatchMessage(&messages);
    }
}
Miles
  • 1,858
  • 1
  • 21
  • 34

1 Answers1

2

Typically, you use an object architecture. Windows provides access to space local to each hWnd that is explicitly reserved for your use for basically this exact purpose. You don't need any global variables to make this work.

class Window {
    // Can be virtual if necessary
    LRESULT WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
        // Process the message here
    }
    static LRESULT WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
        if (Window* ptr = reinterpret_cast<Window*>(GetWindowLongPtr(hwnd, GWLP_USERDATA))) {
            return ptr->WindowProc(hwnd, uMsg, wParam, lParam);
        }
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    HWND hwnd;
public:
    Window() {
         hwnd = CreateWindowEx(....);
         SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
         ShowWindow(...); // update the ptr
    }
    void ProcessMessages() {
        MSG messages;
        while(PeekMessage (&messages, hwnd, 0, 0, PM_REMOVE))
        {
            printf("MessageLoop\n");
            TranslateMessage(&messages);
            DispatchMessage(&messages);
        }
    }
};
Puppy
  • 144,682
  • 38
  • 256
  • 465
  • Which WindowProc are you specifying in CreateWindowEx? I've been unable to use a method of the object as the WindowProc. Here...why don't I just post the whole class. – Miles Jan 28 '12 at 22:57
  • @Miles: You use the static WindowProc. Ahem... I should not have named them both WindowProc. My mistake. – Puppy Jan 28 '12 at 23:10
  • But how does the static WindowProc know which window's event queue to send the messages to? The idea is to sort the messages. – Miles Jan 28 '12 at 23:11
  • @Miles Rufat-Latre: The key part is the `GetWindowLongPtr()` and `SetWindowLongPtr()` methods. With those two methods you store the pointer to the `Window` object into the operating system's window structures identified by the `HWND`, then retrieve the same `Window` pointer given only an `HWND`. – In silico Jan 28 '12 at 23:15
  • Ah! Thanks! I'll use this structure, and see if it gets fixed (the infinite loop is probably just an error in the usage of queues anyway) – Miles Jan 28 '12 at 23:20
  • @Miles: Do not forget to read the documentation on `ShowWindow`, which you must call in order to use the technique. – Puppy Jan 28 '12 at 23:23
  • and you'd need to get calling convention specified for static wndproc – David Heffernan Jan 28 '12 at 23:35
  • calling convention? Also, I'm still finding an infinite loop. – Miles Jan 28 '12 at 23:35
  • I did use ShowWindow already (in Window's constructor): ShowWindow (hWnd, SW_SHOW); – Miles Jan 28 '12 at 23:42
  • Victory! That function no longer causes an infinite loop, and the initial message pump is working! The only thing getting in my way is a SIGSEGV from bad queue handling elsewhere. This question is answered. – Miles Jan 28 '12 at 23:54