PureBasic-like OpenWindow/WindowEvent for C++
Posted: Fri Dec 03, 2010 9:32 am
I wanted to simplify working with windows in C++ with PureGDK so I went looking for what simple solutions were available. I tried glut, which was nice, but I didn't need all of the fancy callbacks. Freeglut solved that, but it has some weird glitches that required to many workarounds.
Then I wanted multiple windows and thread safety. So instead of opting for something massive like Qt, GTK, or wx, I just ported what was familiar.
Oh, and it's thread safe!
Then I wanted multiple windows and thread safety. So instead of opting for something massive like Qt, GTK, or wx, I just ported what was familiar.

Oh, and it's thread safe!
Code: Select all
#include <string>
#include <map>
#include <deque>
#include <ctime>
#include <windows.h>
LONG globPgdkWndLock = 0;
std::map<DWORD, std::deque<UINT> > globPgdkWndMap;
LRESULT _pgdk_Window_DefWndCallback(HWND hWnd, UINT uMsg, WPARAM wParam,
LPARAM lParam)
{
switch (uMsg) {
// Do not evaluate null messages
case WM_NULL:
break;
// Priory messages
case WM_CLOSE:
case WM_DESTROY:
globPgdkWndMap[GetCurrentThreadId()].push_front(uMsg);
break;
default:
globPgdkWndMap[GetCurrentThreadId()].push_back(uMsg);
break;
}
switch (uMsg) {
case WM_CLOSE:
return 1;
}
return DefWindowProc(hWnd,uMsg,wParam,lParam);
}
UINT windowEvent() {
typedef HANDLE (WINAPI *(pOpenThread))(DWORD,BOOL,DWORD);
UINT result = WM_NULL;
MSG msg;
DWORD threadID = 0;
DWORD currentThreadID = GetCurrentThreadId();
HANDLE hThread = 0;
HMODULE hLib = 0;
pOpenThread ptr = 0;
DWORD exitCode = 0;
do {
Sleep(1);
} while (InterlockedCompareExchange(&globPgdkWndLock, 1, 0) == 0);
// Purge any thread deques which no longer exist
for (std::map<DWORD, std::deque<UINT> >::const_iterator iter =
globPgdkWndMap.begin(); iter != globPgdkWndMap.end(); iter++)
{
threadID = iter->first;
if (!ptr) {
hLib = GetModuleHandle("kernel32.dll");
ptr = (pOpenThread)GetProcAddress(hLib, "OpenThread");
}
// Win2k and up
if (ptr)
hThread = ptr(THREAD_QUERY_INFORMATION, 0, currentThreadID);
GetExitCodeThread(hThread, &exitCode);
if (exitCode != STILL_ACTIVE) {
globPgdkWndMap.erase(threadID);
if (hThread)
CloseHandle(hThread);
}
}
if (!globPgdkWndMap[currentThreadID].empty()) {
result = globPgdkWndMap[currentThreadID].front();
globPgdkWndMap[currentThreadID].pop_front();
}
if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
InterlockedCompareExchange(&globPgdkWndLock, 0, 1);
return result;
}
UINT waitWindowEvent(int delay) {
int elapsedStart;
UINT event;
elapsedStart = time(0);
do {
event = windowEvent();
Sleep(delay);
}
while (!event && time(0) - elapsedStart < delay);
return event;
}
HWND openWindow(int x, int y, int width, int height, std::string title,
int style = WS_OVERLAPPED | WS_CLIPSIBLINGS | WS_CAPTION | WS_SYSMENU |
WS_CLIPCHILDREN | WS_VISIBLE, bool centered = false)
{
WNDCLASS Wnd;
RECT rect;
HWND hWnd;
HINSTANCE hInstance = GetModuleHandle(0);
const char* className = "wndclass";
Wnd.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
Wnd.lpfnWndProc = (WNDPROC)_pgdk_Window_DefWndCallback;
Wnd.cbClsExtra = 0;
Wnd.cbWndExtra = 0;
Wnd.hInstance = hInstance;
Wnd.hIcon = LoadIcon(NULL, IDI_WINLOGO);
Wnd.hCursor = LoadCursor(NULL, IDC_ARROW);
Wnd.hbrBackground = GetSysColorBrush(COLOR_BTNFACE);
Wnd.lpszMenuName = 0;
Wnd.lpszClassName = className;
RegisterClass(&Wnd);
SetRect(&rect, 0, 0, width, height);
AdjustWindowRectEx(&rect, style, 0, WS_EX_WINDOWEDGE);
if (centered) {
x = GetSystemMetrics(SM_CXSCREEN)/2 - (rect.right - rect.left)/2;
y = GetSystemMetrics(SM_CYSCREEN)/2 - (rect.bottom - rect.top)/2;
}
hWnd = CreateWindowEx(WS_EX_WINDOWEDGE, className, title.c_str(), style, x,
y, rect.right - rect.left, rect.bottom - rect.top, 0, 0, hInstance, 0);
if (!hWnd)
return 0;
// Paint window
UpdateWindow(hWnd);
return hWnd;
}