Page 1 of 1

PureBasic-like OpenWindow/WindowEvent for C++

Posted: Fri Dec 03, 2010 9:32 am
by Mistrel
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!

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

Re: PureBasic-like OpenWindow/WindowEvent for C++

Posted: Fri Dec 03, 2010 10:35 am
by milan1612
looks very nice, but could you post a small example showing how to use your code?
thanks!

Re: PureBasic-like OpenWindow/WindowEvent for C++

Posted: Fri Dec 03, 2010 12:22 pm
by Alireza
maybe add an exe :D

Re: PureBasic-like OpenWindow/WindowEvent for C++

Posted: Fri Dec 03, 2010 1:56 pm
by freak
Your WaitWindowEvent() always executes one "Sleep(delay);" even if an event is available immediately. This will slow down message processing. If there is an event, the function should return immediately.

Also why do you use InterlockedCompareExchange() for synchronization? A critical section will probably perform a more efficient wait than a loop with a Sleep() inside, so why not use that?

Re: PureBasic-like OpenWindow/WindowEvent for C++

Posted: Fri Dec 03, 2010 3:00 pm
by Trond
In any case GetMessage() more efficient than PeekMessage() because the program is blocked completely until there is an event.