Win32Window.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  1. 
  2. #include "../DFPSR/api/imageAPI.h"
  3. #include "../DFPSR/gui/BackendWindow.h"
  4. /*
  5. Link to these dependencies for MS Windows:
  6. gdi32
  7. user32
  8. kernel32
  9. comctl32
  10. */
  11. #include <tchar.h>
  12. #include <windows.h>
  13. #include <windowsx.h>
  14. class Win32Window : public dsr::BackendWindow {
  15. public:
  16. // The native windows handle
  17. HWND hwnd;
  18. // The cursors
  19. HCURSOR noCursor, defaultCursor;
  20. // Keep track of when the cursor is inside of the window,
  21. // so that we can show it again when leaving the window
  22. bool cursorIsInside = false;
  23. // Double buffering to allow drawing to a canvas while displaying the previous one
  24. // The image which can be drawn to
  25. dsr::AlignedImageRgbaU8 canvas;
  26. // Remembers the dimensions of the window from creation and resize events
  27. // This allow requesting the size of the window at any time
  28. int windowWidth = 0, windowHeight = 0;
  29. private:
  30. // Called before the application fetches events from the input queue
  31. // Closing the window, moving the mouse, pressing a key, et cetera
  32. void prefetchEvents() override;
  33. // Called to change the cursor visibility and returning true on success
  34. bool setCursorVisibility(bool visible) override;
  35. // Place the cursor within the window
  36. void setCursorPosition(int x, int y) override;
  37. private:
  38. // Helper methods specific to calling XLib
  39. void updateTitle();
  40. private:
  41. // Canvas methods
  42. dsr::AlignedImageRgbaU8 getCanvas() override { return this->canvas; }
  43. void resizeCanvas(int width, int height) override;
  44. // Window methods
  45. void setTitle(const dsr::String &newTitle) override {
  46. this->title = newTitle;
  47. this->updateTitle();
  48. }
  49. void removeOldWindow();
  50. void createWindowed(const dsr::String& title, int width, int height);
  51. void createFullscreen();
  52. void prepareWindow();
  53. int windowState = 0; // 0=none, 1=windowed, 2=fullscreen
  54. public:
  55. // Constructors
  56. Win32Window(const Win32Window&) = delete; // Non-copyable because of pointer aliasing.
  57. Win32Window(const dsr::String& title, int width, int height);
  58. int getWidth() const override { return this->windowWidth; };
  59. int getHeight() const override { return this->windowHeight; };
  60. // Destructor
  61. ~Win32Window();
  62. // Interface
  63. void setFullScreen(bool enabled) override;
  64. bool isFullScreen() override { return this->windowState == 2; }
  65. void redraw(HWND& hwnd); // HWND is passed by argument because drawing might be called before the constructor has assigned it to this->hwnd
  66. void showCanvas() override;
  67. };
  68. static LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
  69. static TCHAR windowClassName[] = _T("DfpsrWindowApplication");
  70. void Win32Window::updateTitle() {
  71. if (!SetWindowTextA(this->hwnd, this->title.toStdString().c_str())) {
  72. dsr::printText("Warning! Could not assign the window title ", dsr::string_mangleQuote(this->title), ".\n");
  73. }
  74. }
  75. void Win32Window::setCursorPosition(int x, int y) {
  76. POINT point; point.x = x; point.y = y;
  77. ClientToScreen(this->hwnd, &point);
  78. SetCursorPos(point.x, point.y);
  79. }
  80. bool Win32Window::setCursorVisibility(bool visible) {
  81. // Remember the cursor's visibility for anyone asking
  82. this->visibleCursor = visible;
  83. // Indicate that the feature is implemented
  84. return true;
  85. }
  86. void Win32Window::setFullScreen(bool enabled) {
  87. if (this->windowState == 1 && enabled) {
  88. // Clean up any previous window
  89. removeOldWindow();
  90. // Create the new window and graphics context
  91. this->createFullscreen();
  92. } else if (this->windowState == 2 && !enabled) {
  93. // Clean up any previous window
  94. removeOldWindow();
  95. // Create the new window and graphics context
  96. this->createWindowed(this->title, 800, 600); // TODO: Remember the dimensions from last windowed mode
  97. }
  98. }
  99. void Win32Window::removeOldWindow() {
  100. if (this->windowState != 0) {
  101. DestroyWindow(this->hwnd);
  102. }
  103. this->windowState = 0;
  104. }
  105. void Win32Window::prepareWindow() {
  106. // Reallocate the canvas
  107. this->resizeCanvas(this->windowWidth, this->windowHeight);
  108. // Show the window
  109. ShowWindow(this->hwnd, SW_NORMAL);
  110. // Repaint
  111. UpdateWindow(this->hwnd);
  112. }
  113. static bool registered = false;
  114. static void registerIfNeeded() {
  115. if (!registered) {
  116. // The Window structure
  117. WNDCLASSEX wincl;
  118. memset(&wincl, 0, sizeof(WNDCLASSEX));
  119. wincl.hInstance = NULL;
  120. wincl.lpszClassName = windowClassName;
  121. wincl.lpfnWndProc = WindowProcedure;
  122. wincl.style = 0;
  123. wincl.cbSize = sizeof(WNDCLASSEX);
  124. // Use default icon and mouse-pointer
  125. wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
  126. wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
  127. wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
  128. wincl.lpszMenuName = NULL;
  129. wincl.cbClsExtra = 0;
  130. wincl.cbWndExtra = sizeof(LPVOID);
  131. // Use Windows's default color as the background of the window
  132. wincl.hbrBackground = (HBRUSH)COLOR_BACKGROUND; // TODO: Make black
  133. // Register the window class, and if it fails quit the program
  134. if (!RegisterClassEx (&wincl)) {
  135. dsr::throwError("Call to RegisterClassEx failed!\n");
  136. }
  137. registered = true;
  138. }
  139. }
  140. void Win32Window::createWindowed(const dsr::String& title, int width, int height) {
  141. // Request to resize the canvas and interface according to the new window
  142. this->windowWidth = width;
  143. this->windowHeight = height;
  144. this->receivedWindowResize(width, height);
  145. // Register the Window class during first creation
  146. registerIfNeeded();
  147. // The class is registered, let's create the program
  148. this->hwnd = CreateWindowEx(
  149. 0, // dwExStyle
  150. windowClassName, // lpClassName
  151. _T(""), // lpWindowName
  152. WS_OVERLAPPEDWINDOW, // dwStyle
  153. CW_USEDEFAULT, // x
  154. CW_USEDEFAULT, // y
  155. width, // nWidth
  156. height, // nHeight
  157. HWND_DESKTOP, // hWndParent
  158. NULL, // hMenu
  159. NULL, // hInstance
  160. (LPVOID)this // lpParam
  161. );
  162. this->updateTitle();
  163. this->windowState = 1;
  164. this->prepareWindow();
  165. }
  166. void Win32Window::createFullscreen() {
  167. int screenWidth = GetSystemMetrics(SM_CXSCREEN);
  168. int screenHeight = GetSystemMetrics(SM_CYSCREEN);
  169. // Request to resize the canvas and interface according to the new window
  170. this->windowWidth = screenWidth;
  171. this->windowHeight = screenHeight;
  172. this->receivedWindowResize(screenWidth, screenHeight);
  173. // Register the Window class during first creation
  174. registerIfNeeded();
  175. // The class is registered, let's create the program
  176. this->hwnd = CreateWindowEx(
  177. 0, // dwExStyle
  178. windowClassName, // lpClassName
  179. _T(""), // lpWindowName
  180. WS_POPUP | WS_VISIBLE, // dwStyle
  181. 0, // x
  182. 0, // y
  183. screenWidth, // nWidth
  184. screenHeight, // nHeight
  185. HWND_DESKTOP, // hWndParent
  186. NULL, // hMenu
  187. NULL, // hInstance
  188. (LPVOID)this // lpParam
  189. );
  190. this->windowState = 2;
  191. this->prepareWindow();
  192. }
  193. Win32Window::Win32Window(const dsr::String& title, int width, int height) {
  194. bool fullScreen = false;
  195. if (width < 1 || height < 1) {
  196. fullScreen = true;
  197. }
  198. // Remember the title
  199. this->title = title;
  200. // Get the default cursor
  201. this->defaultCursor = LoadCursor(0, IDC_ARROW);
  202. // Create an invisible cursor using masks padded to 32 bits for safety
  203. uint32_t cursorAndMask = 0b11111111;
  204. uint32_t cursorXorMask = 0b00000000;
  205. this->noCursor = CreateCursor(NULL, 0, 0, 1, 1, (const void*)&cursorAndMask, (const void*)&cursorXorMask);
  206. // Create a window
  207. if (fullScreen) {
  208. this->createFullscreen();
  209. } else {
  210. this->createWindowed(title, width, height);
  211. }
  212. }
  213. static dsr::DsrKey getDsrKey(WPARAM keyCode) {
  214. dsr::DsrKey result = dsr::DsrKey_Unhandled;
  215. if (keyCode == VK_ESCAPE) {
  216. result = dsr::DsrKey_Escape;
  217. } else if (keyCode == VK_F1) {
  218. result = dsr::DsrKey_F1;
  219. } else if (keyCode == VK_F2) {
  220. result = dsr::DsrKey_F2;
  221. } else if (keyCode == VK_F3) {
  222. result = dsr::DsrKey_F3;
  223. } else if (keyCode == VK_F4) {
  224. result = dsr::DsrKey_F4;
  225. } else if (keyCode == VK_F5) {
  226. result = dsr::DsrKey_F5;
  227. } else if (keyCode == VK_F6) {
  228. result = dsr::DsrKey_F6;
  229. } else if (keyCode == VK_F7) {
  230. result = dsr::DsrKey_F7;
  231. } else if (keyCode == VK_F8) {
  232. result = dsr::DsrKey_F8;
  233. } else if (keyCode == VK_F9) {
  234. result = dsr::DsrKey_F9;
  235. } else if (keyCode == VK_F10) {
  236. result = dsr::DsrKey_F10;
  237. } else if (keyCode == VK_F11) {
  238. result = dsr::DsrKey_F11;
  239. } else if (keyCode == VK_F12) {
  240. result = dsr::DsrKey_F12;
  241. } else if (keyCode == VK_PAUSE) {
  242. result = dsr::DsrKey_Pause;
  243. } else if (keyCode == VK_SPACE) {
  244. result = dsr::DsrKey_Space;
  245. } else if (keyCode == VK_TAB) {
  246. result = dsr::DsrKey_Tab;
  247. } else if (keyCode == VK_RETURN) {
  248. result = dsr::DsrKey_Return;
  249. } else if (keyCode == VK_BACK) {
  250. result = dsr::DsrKey_BackSpace;
  251. } else if (keyCode == VK_LSHIFT) {
  252. result = dsr::DsrKey_LeftShift;
  253. } else if (keyCode == VK_RSHIFT) {
  254. result = dsr::DsrKey_RightShift;
  255. } else if (keyCode == VK_LCONTROL) {
  256. result = dsr::DsrKey_LeftControl;
  257. } else if (keyCode == VK_RCONTROL) {
  258. result = dsr::DsrKey_RightControl;
  259. } else if (keyCode == VK_LMENU) {
  260. result = dsr::DsrKey_LeftAlt;
  261. } else if (keyCode == VK_RMENU) {
  262. result = dsr::DsrKey_RightAlt;
  263. } else if (keyCode == VK_DELETE) {
  264. result = dsr::DsrKey_Delete;
  265. } else if (keyCode == VK_LEFT) {
  266. result = dsr::DsrKey_LeftArrow;
  267. } else if (keyCode == VK_RIGHT) {
  268. result = dsr::DsrKey_RightArrow;
  269. } else if (keyCode == VK_UP) {
  270. result = dsr::DsrKey_UpArrow;
  271. } else if (keyCode == VK_DOWN) {
  272. result = dsr::DsrKey_DownArrow;
  273. } else if (keyCode == 0x30) {
  274. result = dsr::DsrKey_0;
  275. } else if (keyCode == 0x31) {
  276. result = dsr::DsrKey_1;
  277. } else if (keyCode == 0x32) {
  278. result = dsr::DsrKey_2;
  279. } else if (keyCode == 0x33) {
  280. result = dsr::DsrKey_3;
  281. } else if (keyCode == 0x34) {
  282. result = dsr::DsrKey_4;
  283. } else if (keyCode == 0x35) {
  284. result = dsr::DsrKey_5;
  285. } else if (keyCode == 0x36) {
  286. result = dsr::DsrKey_6;
  287. } else if (keyCode == 0x37) {
  288. result = dsr::DsrKey_7;
  289. } else if (keyCode == 0x38) {
  290. result = dsr::DsrKey_8;
  291. } else if (keyCode == 0x39) {
  292. result = dsr::DsrKey_9;
  293. } else if (keyCode == 0x41) {
  294. result = dsr::DsrKey_A;
  295. } else if (keyCode == 0x42) {
  296. result = dsr::DsrKey_B;
  297. } else if (keyCode == 0x43) {
  298. result = dsr::DsrKey_C;
  299. } else if (keyCode == 0x44) {
  300. result = dsr::DsrKey_D;
  301. } else if (keyCode == 0x45) {
  302. result = dsr::DsrKey_E;
  303. } else if (keyCode == 0x46) {
  304. result = dsr::DsrKey_F;
  305. } else if (keyCode == 0x47) {
  306. result = dsr::DsrKey_G;
  307. } else if (keyCode == 0x48) {
  308. result = dsr::DsrKey_H;
  309. } else if (keyCode == 0x49) {
  310. result = dsr::DsrKey_I;
  311. } else if (keyCode == 0x4A) {
  312. result = dsr::DsrKey_J;
  313. } else if (keyCode == 0x4B) {
  314. result = dsr::DsrKey_K;
  315. } else if (keyCode == 0x4C) {
  316. result = dsr::DsrKey_L;
  317. } else if (keyCode == 0x4D) {
  318. result = dsr::DsrKey_M;
  319. } else if (keyCode == 0x4E) {
  320. result = dsr::DsrKey_N;
  321. } else if (keyCode == 0x4F) {
  322. result = dsr::DsrKey_O;
  323. } else if (keyCode == 0x50) {
  324. result = dsr::DsrKey_P;
  325. } else if (keyCode == 0x51) {
  326. result = dsr::DsrKey_Q;
  327. } else if (keyCode == 0x52) {
  328. result = dsr::DsrKey_R;
  329. } else if (keyCode == 0x53) {
  330. result = dsr::DsrKey_S;
  331. } else if (keyCode == 0x54) {
  332. result = dsr::DsrKey_T;
  333. } else if (keyCode == 0x55) {
  334. result = dsr::DsrKey_U;
  335. } else if (keyCode == 0x56) {
  336. result = dsr::DsrKey_V;
  337. } else if (keyCode == 0x57) {
  338. result = dsr::DsrKey_W;
  339. } else if (keyCode == 0x58) {
  340. result = dsr::DsrKey_X;
  341. } else if (keyCode == 0x59) {
  342. result = dsr::DsrKey_Y;
  343. } else if (keyCode == 0x5A) {
  344. result = dsr::DsrKey_Z;
  345. } else if (keyCode == 0x2D) {
  346. result = dsr::DsrKey_Insert;
  347. } else if (keyCode == 0x24) {
  348. result = dsr::DsrKey_Home;
  349. } else if (keyCode == 0x23) {
  350. result = dsr::DsrKey_End;
  351. } else if (keyCode == 0x21) {
  352. result = dsr::DsrKey_PageUp;
  353. } else if (keyCode == 0x22) {
  354. result = dsr::DsrKey_PageDown;
  355. }
  356. return result;
  357. }
  358. // Called from DispatchMessage via prefetchEvents
  359. static LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
  360. // Get the Win32Window owning the given hwnd
  361. Win32Window *parent = nullptr;
  362. if (message == WM_CREATE) {
  363. // Cast the pointer argument into CREATESTRUCT and get the lParam given to the window on creation
  364. CREATESTRUCT *createStruct = (CREATESTRUCT*)lParam;
  365. parent = (Win32Window*)createStruct->lpCreateParams;
  366. if (parent == nullptr) {
  367. dsr::throwError("Null handle retreived from lParam (", (intptr_t)parent, ") in WM_CREATE message.\n");
  368. }
  369. SetWindowLongPtr(hwnd, GWLP_USERDATA, (intptr_t)parent);
  370. } else {
  371. // Get the parent
  372. parent = (Win32Window*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
  373. if (parent == nullptr) {
  374. // Don't try to handle global events unrelated to any window
  375. return DefWindowProc(hwnd, message, wParam, lParam);
  376. }
  377. }
  378. // Check that we're using the correct window instance (This might not be the case while toggling full-screen)
  379. // Handle the message
  380. int result = 0;
  381. switch (message) {
  382. case WM_QUIT:
  383. PostQuitMessage(wParam);
  384. break;
  385. case WM_CLOSE:
  386. parent->queueInputEvent(new dsr::WindowEvent(dsr::WindowEventType::Close, parent->windowWidth, parent->windowHeight));
  387. DestroyWindow(hwnd);
  388. break;
  389. case WM_LBUTTONDOWN:
  390. parent->queueInputEvent(new dsr::MouseEvent(dsr::MouseEventType::MouseDown, dsr::MouseKeyEnum::Left, dsr::IVector2D(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))));
  391. break;
  392. case WM_LBUTTONUP:
  393. parent->queueInputEvent(new dsr::MouseEvent(dsr::MouseEventType::MouseUp, dsr::MouseKeyEnum::Left, dsr::IVector2D(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))));
  394. break;
  395. case WM_RBUTTONDOWN:
  396. parent->queueInputEvent(new dsr::MouseEvent(dsr::MouseEventType::MouseDown, dsr::MouseKeyEnum::Right, dsr::IVector2D(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))));
  397. break;
  398. case WM_RBUTTONUP:
  399. parent->queueInputEvent(new dsr::MouseEvent(dsr::MouseEventType::MouseUp, dsr::MouseKeyEnum::Right, dsr::IVector2D(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))));
  400. break;
  401. case WM_MBUTTONDOWN:
  402. parent->queueInputEvent(new dsr::MouseEvent(dsr::MouseEventType::MouseDown, dsr::MouseKeyEnum::Middle, dsr::IVector2D(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))));
  403. break;
  404. case WM_MBUTTONUP:
  405. parent->queueInputEvent(new dsr::MouseEvent(dsr::MouseEventType::MouseUp, dsr::MouseKeyEnum::Middle, dsr::IVector2D(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))));
  406. break;
  407. case WM_MOUSEMOVE:
  408. parent->queueInputEvent(new dsr::MouseEvent(dsr::MouseEventType::MouseMove, dsr::MouseKeyEnum::NoKey, dsr::IVector2D(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))));
  409. break;
  410. case WM_SETCURSOR:
  411. if (LOWORD(lParam) == HTCLIENT) {
  412. if (parent->visibleCursor) {
  413. SetCursor(parent->defaultCursor);
  414. } else {
  415. SetCursor(parent->noCursor);
  416. }
  417. }
  418. break;
  419. case WM_MOUSEWHEEL:
  420. {
  421. int delta = GET_WHEEL_DELTA_WPARAM(wParam);
  422. if (delta > 0) {
  423. parent->queueInputEvent(new dsr::MouseEvent(dsr::MouseEventType::Scroll, dsr::MouseKeyEnum::ScrollUp, dsr::IVector2D(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))));
  424. } else if (delta < 0) {
  425. parent->queueInputEvent(new dsr::MouseEvent(dsr::MouseEventType::Scroll, dsr::MouseKeyEnum::ScrollDown, dsr::IVector2D(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))));
  426. }
  427. }
  428. break;
  429. case WM_KEYDOWN: case WM_KEYUP:
  430. {
  431. dsr::DsrChar character;
  432. if (IsWindowUnicode(hwnd)) {
  433. dsr::CharacterEncoding encoding = dsr::CharacterEncoding::BOM_UTF16LE;
  434. dsr::String characterString = dsr::string_dangerous_decodeFromData((const void*)&wParam, encoding);
  435. character = characterString[0]; // Convert from UTF-16 surrogate to UTF-32 Unicode character
  436. } else {
  437. character = wParam; // Raw ansi character
  438. }
  439. dsr::DsrKey dsrKey = getDsrKey(wParam); // Portable key-code
  440. bool previouslyPressed = lParam & (1 << 30);
  441. if (message == WM_KEYDOWN) {
  442. // If not repeated
  443. if (!previouslyPressed) {
  444. // Physical key down
  445. parent->queueInputEvent(new dsr::KeyboardEvent(dsr::KeyboardEventType::KeyDown, character, dsrKey));
  446. }
  447. // Press typing with repeat
  448. parent->queueInputEvent(new dsr::KeyboardEvent(dsr::KeyboardEventType::KeyType, character, dsrKey));
  449. } else { // message == WM_KEYUP
  450. // Physical key up
  451. parent->queueInputEvent(new dsr::KeyboardEvent(dsr::KeyboardEventType::KeyUp, character, dsrKey));
  452. }
  453. }
  454. break;
  455. case WM_PAINT:
  456. parent->queueInputEvent(new dsr::WindowEvent(dsr::WindowEventType::Redraw, parent->windowWidth, parent->windowHeight));
  457. // BeginPaint and EndPaint must be called with the given hwnd to prevent having the redraw message sent again
  458. parent->redraw(hwnd);
  459. // Passing on the event to prevent flooding with more messages. This is only a temporary solution.
  460. // TODO: Avoid overwriting the result with any background color.
  461. //result = DefWindowProc(hwnd, message, wParam, lParam);
  462. break;
  463. case WM_SIZE:
  464. // If there's no size during minimization, don't try to resize the canvas
  465. if (wParam != SIZE_MINIMIZED) {
  466. int width = LOWORD(lParam);
  467. int height = HIWORD(lParam);
  468. parent->windowWidth = width;
  469. parent->windowHeight = height;
  470. parent->receivedWindowResize(width, height);
  471. }
  472. // Resize the window as requested
  473. result = DefWindowProc(hwnd, message, wParam, lParam);
  474. break;
  475. default:
  476. result = DefWindowProc(hwnd, message, wParam, lParam);
  477. }
  478. return result;
  479. }
  480. void Win32Window::prefetchEvents() {
  481. MSG messages;
  482. if (IsWindowUnicode(this->hwnd)) {
  483. while (PeekMessageW(&messages, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&messages); DispatchMessage(&messages); }
  484. } else {
  485. while (PeekMessage(&messages, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&messages); DispatchMessage(&messages); }
  486. }
  487. }
  488. // Locked because it overrides
  489. void Win32Window::resizeCanvas(int width, int height) {
  490. // Create a new canvas
  491. // Even thou Windows is using RGBA pack order for the window, the bitmap format used for drawing is using BGRA order
  492. this->canvas = dsr::image_create_RgbaU8_native(width, height, dsr::PackOrderIndex::BGRA);
  493. }
  494. Win32Window::~Win32Window() {
  495. // Destroy the invisible cursor
  496. DestroyCursor(this->noCursor);
  497. // Destroy the native window
  498. DestroyWindow(this->hwnd);
  499. }
  500. void Win32Window::redraw(HWND& hwnd) {
  501. // Let the source bitmap use a padded width to safely handle the stride
  502. // Windows require 8-byte alignment, but the image format uses 16-byte alignment.
  503. int paddedWidth = dsr::image_getStride(this->canvas) / 4;
  504. //int width = dsr::image_getWidth(this->canvas);
  505. int height = dsr::image_getHeight(this->canvas);
  506. InvalidateRect(this->hwnd, NULL, false);
  507. PAINTSTRUCT paintStruct;
  508. HDC targetContext = BeginPaint(this->hwnd, &paintStruct);
  509. BITMAPINFO bmi = {};
  510. bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
  511. bmi.bmiHeader.biWidth = paddedWidth;
  512. bmi.bmiHeader.biHeight = -height;
  513. bmi.bmiHeader.biPlanes = 1;
  514. bmi.bmiHeader.biBitCount = 32;
  515. bmi.bmiHeader.biCompression = BI_RGB;
  516. SetDIBitsToDevice(targetContext, 0, 0, paddedWidth, height, 0, 0, 0, height, dsr::image_dangerous_getData(this->canvas), &bmi, DIB_RGB_COLORS);
  517. EndPaint(this->hwnd, &paintStruct);
  518. }
  519. void Win32Window::showCanvas() {
  520. this->prefetchEvents();
  521. this->redraw(this->hwnd);
  522. }
  523. std::shared_ptr<dsr::BackendWindow> createBackendWindow(const dsr::String& title, int width, int height) {
  524. auto backend = std::make_shared<Win32Window>(title, width, height);
  525. return std::dynamic_pointer_cast<dsr::BackendWindow>(backend);
  526. }