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);
}
}
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);
}
}
};