PureBasic-like OpenWindow/WindowEvent for C++

For everything that's not in any way related to PureBasic. General chat etc...
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

PureBasic-like OpenWindow/WindowEvent for C++

Post 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;
}
milan1612
Addict
Addict
Posts: 894
Joined: Thu Apr 05, 2007 12:15 am
Location: Nuremberg, Germany
Contact:

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

Post by milan1612 »

looks very nice, but could you post a small example showing how to use your code?
thanks!
Windows 7 & PureBasic 4.4
User avatar
Alireza
Enthusiast
Enthusiast
Posts: 143
Joined: Sat Aug 16, 2008 2:02 pm
Location: Iran

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

Post by Alireza »

maybe add an exe :D
PB v 5.6 :D
freak
PureBasic Team
PureBasic Team
Posts: 5940
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

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

Post 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?
quidquid Latine dictum sit altum videtur
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

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

Post by Trond »

In any case GetMessage() more efficient than PeekMessage() because the program is blocked completely until there is an event.
Post Reply