/*------------------------------------------------------------------------------ ----------------- Copyright J.Hubert 2015 This file is part of demOS demOS is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. demOS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with demOS. If not, see . ------------------------------------------------------------------------------------------------- */ #define _CRT_SECURE_NO_WARNINGS #include #include #include #undef MEM_FREE /* collides with our MEM_FREE... */ #include "DEMOSDK/PC/WINDOW.H" #define WINDOW_KEYBUFFER_SIZE 16 #define WINDOW_MAXTITLELEN 256 STRUCT(WINdow) { HWND window; volatile bool isReady; volatile bool isDestroyed; bool isValid; HINSTANCE instance; HANDLE pixmap; HDC pixmapDC; HDC windowDC; COLORREF color; HBRUSH brush; HPEN pen; HANDLE thread; DWORD threadId; u32 width; u32 height; volatile s32 mouseX; volatile s32 mouseY; volatile s32 mouseK; volatile s32 mouseZ; volatile u32 controlKeys; volatile u32 keyBuffer[WINDOW_KEYBUFFER_SIZE]; volatile u32 keyBufferLen; char title[WINDOW_MAXTITLELEN]; }; static DWORD WINAPI Window_eventThread (void* _param); #define WINDOWCLASS "TEST_WINDOW" #define TITLEBAR_H 20 WINdow* WINconstruct (WINinitParam* _param) { WINdow* _m = (WINdow*) malloc(sizeof(WINdow)); _m->instance = _param->hInstance; _m->isDestroyed = false; _m->mouseX = 0; _m->mouseY = 0; _m->mouseK = 0; _m->mouseZ = 0; _m->keyBufferLen = 0; _m->controlKeys = 0; _m->isReady = false; _m->isValid = true; _m->window = NULL; _m->windowDC = NULL; _m->thread = INVALID_HANDLE_VALUE; _m->pixmapDC = NULL; _m->pixmap = INVALID_HANDLE_VALUE; _m->color = 0xFFFFFFFF; _m->pen = 0; _m->brush = 0; _m->width = _param->w; _m->height = _param->h; if (_param->title == NULL) { strcpy (_m->title, ""); } else { strncpy (_m->title, _param->title, WINDOW_MAXTITLELEN); _m->title [WINDOW_MAXTITLELEN - 1] = 0; } if (_param->x == WINDOW_CENTER) { RECT rect; HWND root = GetDesktopWindow(); GetWindowRect(root, &rect); _param->x = (rect.left + rect.right - _m->width ) / 2; _param->y = (rect.top + rect.bottom - _m->height) / 2; } // Launches messages dispatching thread _m->thread = CreateThread (NULL, 32768, Window_eventThread, (void*)_m, 0, &_m->threadId ); // and wait for the window creation while (_m->isReady == false) { Sleep (1); } if ( _m->isValid ) { s32 titleH; RECT clientRect; RECT windowRect; GetClientRect ( _m->window, &clientRect ); GetWindowRect ( _m->window, &windowRect ); titleH = (windowRect.bottom - windowRect.top) - (clientRect.bottom - clientRect.top); SetWindowPos( _m->window, HWND_TOP, _param->x, _param->y, _param->w, _param->h + titleH, SWP_SHOWWINDOW); Sleep(100); } return _m; } void WINdestroy (WINdow* _m) { if ( _m->pen != 0 ) { DeleteObject (_m->pen); } if ( _m->brush != 0 ) { DeleteObject (_m->brush); } if (_m->thread != INVALID_HANDLE_VALUE) { _m->isDestroyed = true; //Thread will finish alone WaitForSingleObject (_m->thread, 2000); //2 secondes should be sufficient to destroy a window } } } static void WINinsertKey (WINdow* _m, u32 _key) { if ( _m->keyBufferLen < WINDOW_KEYBUFFER_SIZE ) { memmove ((void*)(_m->keyBuffer + 1), (void*)_m->keyBuffer, (WINDOW_KEYBUFFER_SIZE - 1) * sizeof(u32)); *_m->keyBuffer = _key; _m->keyBufferLen++; } } void WINwaitLoop (WINdow* _m) { if (_m->thread != INVALID_HANDLE_VALUE) { //The message loop is done by the other thread WaitForSingleObject (_m->thread, INFINITE); //Passive wait } } void WINclear (WINdow* _m) { WINfilledRectangle (_m, 0,0,_m->width, _m->height); } void WINdrawImage (WINdow* _m, void* _image, u32 _width, u32 _height, u32 _bitsPerPixel, u8* _palette, u32 _x, u32 _y) { u8 buffer [ sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD) ]; BITMAPINFO * bmpi = (BITMAPINFO *) buffer; bmpi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmpi->bmiHeader.biWidth = _width; bmpi->bmiHeader.biHeight = -(s32)_height; // negative => top-down bmpi->bmiHeader.biPlanes = 1; bmpi->bmiHeader.biBitCount = (u16)_bitsPerPixel; bmpi->bmiHeader.biCompression = BI_RGB; bmpi->bmiHeader.biSizeImage = _width * _height * (_bitsPerPixel == 32 ? 4 : 1); bmpi->bmiHeader.biXPelsPerMeter = 0; bmpi->bmiHeader.biYPelsPerMeter = 0; bmpi->bmiHeader.biClrUsed = 0; bmpi->bmiHeader.biClrImportant = 0; if ((_palette != NULL) && (bmpi->bmiHeader.biBitCount == 8)) { memcpy (bmpi->bmiColors, _palette, 256*sizeof (RGBQUAD)); } SetDIBitsToDevice( _m->pixmapDC, _x, _y, _width, _height, 0, 0, 0, _height, _image, bmpi, DIB_RGB_COLORS); } void WINfilledRectangle (WINdow* _m, s32 _x1, s32 _y1, s32 _x2, s32 _y2) { RECT rect; rect.left = _x1; rect.top = _y1; rect.right = _x2; rect.bottom = _y2; SelectObject (_m->pixmapDC, _m->brush); FillRect(_m->pixmapDC, &rect, _m->brush); } void WINgetMouse (WINdow* _m, s32* _x, s32* _y, s32* _k, s32* _z) { if ( _x != NULL ) { (*_x) = _m->mouseX; } if ( _y != NULL ) { (*_y) = _m->mouseY; } if ( _k != NULL ) { (*_k) = _m->mouseK | _m->controlKeys; } if ( _z != NULL ) { (*_z) = _m->mouseZ; } } bool WINisKeyHit (WINdow* _m) { return _m->keyBufferLen > 0; } u32 WINgetKey (WINdow* _m) { u32 uiKey; if ( _m->keyBufferLen == 0 ) { do { } while (_m->keyBufferLen == 0); } _m->keyBufferLen--; uiKey = _m->keyBuffer[_m->keyBufferLen]; memmove ((void*) &_m->keyBuffer[0], (void*)&_m->keyBuffer[1], WINDOW_KEYBUFFER_SIZE * sizeof(u32)); return uiKey; } u32 WINgetControlKeys (WINdow* _m) { return _m->controlKeys; } void WINline (WINdow* _m, s32 _x1, s32 _y1, s32 _x2, s32 _y2) { // Optimize trivial cases if (( _x1 < 0 ) && ( _x2 < 0 )) return; if (( _y1 < 0 ) && ( _y2 < 0 )) return; if (( _x1 > (int)_m->width ) && ( _x2 > (int)_m->width )) return; if (( _y1 > (int)_m->height ) && ( _y2 > (int)_m->height )) return; SelectObject (_m->pixmapDC, _m->pen); MoveToEx (_m->pixmapDC, _x1, _y1, NULL); LineTo (_m->pixmapDC, _x2, _y2); } void WInpoint (WINdow* _m, s32 _iX, s32 _iY) { SetPixel(_m->pixmapDC, _iX, _iY, _m->color); } void WINrectangle (WINdow* _m, s32 _x1, s32 _y1, s32 _x2, s32 _y2) { SelectObject (_m->pixmapDC, _m->pen); SelectObject (_m->pixmapDC, GetStockObject(NULL_BRUSH)); Rectangle(_m->pixmapDC, _x1, _y1, _x2, _y2); } void WINsetColor (WINdow* _m, u8 _r, u8 _g, u8 _b) { DWORD color = RGB(_r,_g,_b); if ( _m->color != color ) { _m->color = color; if ( _m->pen != 0 ) { DeleteObject (_m->pen); } _m->pen = CreatePen (PS_SOLID, 1, _m->color); if ( _m->brush != 0 ) { DeleteObject (_m->brush); } _m->brush = CreateSolidBrush ( _m->color); } } void WINtext (WINdow* _m, s32 _x, s32 _y, char* _string) { SetTextColor ( _m->pixmapDC, _m->color); SetTextAlign ( _m->pixmapDC, TA_LEFT | TA_TOP); SetBkMode ( _m->pixmapDC, TRANSPARENT ); TextOut ( _m->pixmapDC, _x, _y, _string, strlen (_string) ); } bool WINisClosed (WINdow* _m) { return !_m->isReady; } void WINrender(WINdow* _m, u32 _waitms) { BitBlt (_m->windowDC, 0, 0, _m->width, _m->height, _m->pixmapDC, 0, 0, SRCCOPY); Sleep (_waitms); } LRESULT CALLBACK Window_wndProc (HWND _wnd, UINT _message, WPARAM _wparam, LPARAM _lparam) { WINdow *thisWindow = (WINdow*)(GetWindowLongPtr(_wnd, 0)); bool processDefault = false; switch (_message) { case WM_CREATE: { LPCREATESTRUCT param = (LPCREATESTRUCT) _lparam; thisWindow = (WINdow*) param->lpCreateParams; thisWindow->window = _wnd; thisWindow->windowDC = GetDC (_wnd); thisWindow->pixmapDC = CreateCompatibleDC ( thisWindow->windowDC ); thisWindow->pixmap = CreateCompatibleBitmap (thisWindow->windowDC, thisWindow->width, thisWindow->height); SelectObject (thisWindow->pixmapDC, thisWindow->pixmap); SetWindowLongPtr (_wnd, 0, (LONG)thisWindow); processDefault = true; thisWindow->isReady = true; } break; case WM_DESTROY: { if ( thisWindow->pixmap != INVALID_HANDLE_VALUE ) { HANDLE pixmap = thisWindow->pixmap; thisWindow->pixmap = INVALID_HANDLE_VALUE; DeleteObject (pixmap); } if ( thisWindow->pixmapDC != NULL ) { HDC pixmapDC = thisWindow->pixmapDC; thisWindow->pixmapDC = NULL; DeleteDC (pixmapDC); } if ( thisWindow->windowDC != NULL ) { HDC winDC = thisWindow->windowDC; thisWindow->windowDC = NULL; ReleaseDC (thisWindow->window, winDC); } PostQuitMessage (0); } break; case WM_PAINT: { if ( thisWindow != NULL) { PAINTSTRUCT ps; HDC hdc; hdc = BeginPaint(_wnd, &ps); BitBlt (hdc, 0, 0, thisWindow->width, thisWindow->height, thisWindow->pixmapDC, 0, 0, SRCCOPY); EndPaint(_wnd, &ps); } } break; case WM_MOUSEMOVE: { processDefault = true; if ( thisWindow != NULL) { s32 mouseK = 0; // also manage buttons here to detect when buttons has been released outside of the window thisWindow->mouseX = GET_X_LPARAM ( _lparam ); thisWindow->mouseY = GET_Y_LPARAM ( _lparam ); if (_wparam & MK_LBUTTON) mouseK |= MOUSE_LBUT; if (_wparam & MK_MBUTTON) mouseK |= MOUSE_MBUT; if (_wparam & MK_RBUTTON) mouseK |= MOUSE_RBUT; thisWindow->mouseK = mouseK; processDefault = false; } } break; case WM_LBUTTONDOWN: case WM_RBUTTONDOWN: case WM_MBUTTONDOWN: { processDefault = true; if ( thisWindow != NULL) { thisWindow->mouseK |= _message == WM_LBUTTONDOWN; thisWindow->mouseK |= (_message == WM_RBUTTONDOWN) << 1; thisWindow->mouseK |= (_message == WM_MBUTTONDOWN) << 2; processDefault = false; } } break; case WM_LBUTTONUP: case WM_RBUTTONUP: case WM_MBUTTONUP: { processDefault = true; if ( thisWindow != NULL) { thisWindow->mouseK &= ~(s32) (_message == WM_LBUTTONUP); thisWindow->mouseK &= ~(s32)((_message == WM_RBUTTONUP) << 1); thisWindow->mouseK &= ~(s32)((_message == WM_MBUTTONUP) << 2); processDefault = false; } } break; case WM_MOUSEWHEEL: { processDefault = true; if ( thisWindow != NULL) { s16 value = (s16)(_wparam >> 16); thisWindow->mouseZ += (s32) value; processDefault = false; } } break; case WM_KEYDOWN: processDefault = true; if ( thisWindow != NULL) { s32 key = -1; switch (_wparam) { case VK_DOWN: key = KEY_DOWN; break; case VK_UP: key = KEY_UP; break; case VK_RIGHT: key = KEY_RIGHT; break; case VK_LEFT: key = KEY_LEFT; break; case VK_HOME: key = KEY_HOME; break; case VK_END: key = KEY_END; break; case VK_INSERT: key = KEY_INSERT; break; case VK_DELETE: key = KEY_DELETE; break; case VK_PRIOR: key = KEY_PAGEUP; break; case VK_NEXT: key = KEY_PAGEDOWN; break; } if ( key != -1 ) { s32 i; for (i = 0 ; i < (_lparam & 15) ; i++) { WINinsertKey(thisWindow, key); } processDefault = false; } else { switch (_wparam) { case VK_MENU: thisWindow->controlKeys |= CONTROLKEY_ALT; processDefault = false; break; case VK_CONTROL: thisWindow->controlKeys |= CONTROLKEY_CTRL; processDefault = false; break; case VK_SHIFT: thisWindow->controlKeys |= CONTROLKEY_SHIFT; processDefault = false; break; } } } break; case WM_KEYUP: processDefault = true; if ( thisWindow != NULL) { switch (_wparam) { case VK_MENU: thisWindow->controlKeys &= ~CONTROLKEY_ALT; processDefault = false; break; case VK_CONTROL: thisWindow->controlKeys &= ~CONTROLKEY_CTRL; processDefault = false; break; case VK_SHIFT: thisWindow->controlKeys &= ~CONTROLKEY_SHIFT; processDefault = false; break; } } break; case WM_CHAR: { processDefault = true; if ( thisWindow != NULL) { s32 i; for (i = 0 ; i < (_lparam & 15) ; i++) { WINinsertKey(thisWindow, (u32)_wparam); } processDefault = false; } } break; case WM_CLOSE: thisWindow->isReady = false; processDefault = true; break; default: processDefault = true; break; } if ( processDefault ) { return DefWindowProc(_wnd, _message, _wparam, _lparam); } else { return 0; } } BOOL Window_initInstance (WINdow* _m, HINSTANCE _instance, int _cmdShow) { HWND wnd; wnd = CreateWindow( WINDOWCLASS, _m->title, WS_OVERLAPPED | WS_MINIMIZEBOX | WS_SYSMENU, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, _instance, _m); if (!wnd) { return FALSE; } ShowWindow(wnd, _cmdShow); UpdateWindow(wnd); return TRUE; } ATOM Window_registerClass (WINdow* _m, HINSTANCE _hInstance) { WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = (WNDPROC) Window_wndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = sizeof(void*) * 2; wcex.hInstance = _hInstance; wcex.hIcon = NULL; wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = NULL; wcex.lpszClassName = WINDOWCLASS; wcex.hIconSm = NULL; return RegisterClassEx(&wcex); } static DWORD WINAPI Window_eventThread (void* _param) { WINdow *thisWindow = (WINdow*) _param; MSG msg; // Window must be created by the thread which will read message queue // (there is one message queue per thread) Window_registerClass(thisWindow, thisWindow->instance); // Perform application initialization if ( !Window_initInstance (thisWindow, thisWindow->instance, SW_SHOWNORMAL) ) { thisWindow->isValid = false; thisWindow->isReady = true; } else { // message loop bool stop = false; while (!stop) { if (thisWindow->isDestroyed) { thisWindow->thread = INVALID_HANDLE_VALUE; DestroyWindow (thisWindow->window); stop = true; } else { while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) { thisWindow->thread = INVALID_HANDLE_VALUE; TranslateMessage (&msg); DispatchMessage (&msg); stop = true; } else { TranslateMessage (&msg); DispatchMessage (&msg); } } Sleep (1); } } } return 0; }