1 /**
2 * @file win32drv.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9
10 #include "win32drv.h"
11
12 #if USE_WIN32DRV
13
14 #include <windowsx.h>
15 #include <malloc.h>
16 #include <process.h>
17 #include <stdbool.h>
18 #include <stdint.h>
19
20 /*********************
21 * DEFINES
22 *********************/
23
24 #define WINDOW_EX_STYLE \
25 WS_EX_CLIENTEDGE
26
27 #define WINDOW_STYLE \
28 (WS_OVERLAPPEDWINDOW & ~(WS_SIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME))
29
30 #ifndef WIN32DRV_MONITOR_ZOOM
31 #define WIN32DRV_MONITOR_ZOOM 1
32 #endif
33
34 #ifndef USER_DEFAULT_SCREEN_DPI
35 #define USER_DEFAULT_SCREEN_DPI 96
36 #endif
37
38 /**********************
39 * TYPEDEFS
40 **********************/
41
42 typedef struct _WINDOW_THREAD_PARAMETER
43 {
44 HANDLE window_mutex;
45 HINSTANCE instance_handle;
46 HICON icon_handle;
47 lv_coord_t hor_res;
48 lv_coord_t ver_res;
49 int show_window_mode;
50 } WINDOW_THREAD_PARAMETER, * PWINDOW_THREAD_PARAMETER;
51
52 /**********************
53 * STATIC PROTOTYPES
54 **********************/
55
56 /**
57 * @brief Creates a B8G8R8A8 frame buffer.
58 * @param WindowHandle A handle to the window for the creation of the frame
59 * buffer. If this value is NULL, the entire screen will be
60 * referenced.
61 * @param Width The width of the frame buffer.
62 * @param Height The height of the frame buffer.
63 * @param PixelBuffer The raw pixel buffer of the frame buffer you created.
64 * @param PixelBufferSize The size of the frame buffer you created.
65 * @return If the function succeeds, the return value is a handle to the device
66 * context (DC) for the frame buffer. If the function fails, the return
67 * value is NULL, and PixelBuffer parameter is NULL.
68 */
69 static HDC lv_win32_create_frame_buffer(
70 _In_opt_ HWND WindowHandle,
71 _In_ LONG Width,
72 _In_ LONG Height,
73 _Out_ UINT32** PixelBuffer,
74 _Out_ SIZE_T* PixelBufferSize);
75
76 /**
77 * @brief Enables WM_DPICHANGED message for child window for the associated
78 * window.
79 * @param WindowHandle The window you want to enable WM_DPICHANGED message for
80 * child window.
81 * @return If the function succeeds, the return value is non-zero. If the
82 * function fails, the return value is zero.
83 * @remarks You need to use this function in Windows 10 Threshold 1 or Windows
84 * 10 Threshold 2.
85 */
86 static BOOL lv_win32_enable_child_window_dpi_message(
87 _In_ HWND WindowHandle);
88
89 /**
90 * @brief Registers a window as being touch-capable.
91 * @param hWnd The handle of the window being registered.
92 * @param ulFlags A set of bit flags that specify optional modifications.
93 * @return If the function succeeds, the return value is nonzero. If the
94 * function fails, the return value is zero.
95 * @remark For more information, see RegisterTouchWindow.
96 */
97 static BOOL lv_win32_register_touch_window(
98 HWND hWnd,
99 ULONG ulFlags);
100
101 /**
102 * @brief Retrieves detailed information about touch inputs associated with a
103 * particular touch input handle.
104 * @param hTouchInput The touch input handle received in the LPARAM of a touch
105 * message.
106 * @param cInputs The number of structures in the pInputs array.
107 * @param pInputs A pointer to an array of TOUCHINPUT structures to receive
108 * information about the touch points associated with the
109 * specified touch input handle.
110 * @param cbSize The size, in bytes, of a single TOUCHINPUT structure.
111 * @return If the function succeeds, the return value is nonzero. If the
112 * function fails, the return value is zero.
113 * @remark For more information, see GetTouchInputInfo.
114 */
115 static BOOL lv_win32_get_touch_input_info(
116 HTOUCHINPUT hTouchInput,
117 UINT cInputs,
118 PTOUCHINPUT pInputs,
119 int cbSize);
120
121 /**
122 * @brief Closes a touch input handle, frees process memory associated with it,
123 and invalidates the handle.
124 * @param hTouchInput The touch input handle received in the LPARAM of a touch
125 * message.
126 * @return If the function succeeds, the return value is nonzero. If the
127 * function fails, the return value is zero.
128 * @remark For more information, see CloseTouchInputHandle.
129 */
130 static BOOL lv_win32_close_touch_input_handle(
131 HTOUCHINPUT hTouchInput);
132
133 /**
134 * @brief Returns the dots per inch (dpi) value for the associated window.
135 * @param WindowHandle The window you want to get information about.
136 * @return The DPI for the window.
137 */
138 static UINT lv_win32_get_dpi_for_window(
139 _In_ HWND WindowHandle);
140
141 static void lv_win32_display_driver_flush_callback(
142 lv_disp_drv_t* disp_drv,
143 const lv_area_t* area,
144 lv_color_t* color_p);
145
146 static void lv_win32_pointer_driver_read_callback(
147 lv_indev_drv_t* indev_drv,
148 lv_indev_data_t* data);
149
150 static void lv_win32_keypad_driver_read_callback(
151 lv_indev_drv_t* indev_drv,
152 lv_indev_data_t* data);
153
154 static void lv_win32_encoder_driver_read_callback(
155 lv_indev_drv_t* indev_drv,
156 lv_indev_data_t* data);
157
158 static lv_win32_window_context_t* lv_win32_get_display_context(
159 lv_disp_t* display);
160
161 static LRESULT CALLBACK lv_win32_window_message_callback(
162 HWND hWnd,
163 UINT uMsg,
164 WPARAM wParam,
165 LPARAM lParam);
166
167 static unsigned int __stdcall lv_win32_window_thread_entrypoint(
168 void* raw_parameter);
169
lv_win32_push_key_to_keyboard_queue(lv_win32_window_context_t * context,uint32_t key,lv_indev_state_t state)170 static void lv_win32_push_key_to_keyboard_queue(
171 lv_win32_window_context_t* context,
172 uint32_t key,
173 lv_indev_state_t state)
174 {
175 lv_win32_keyboard_queue_item_t* current =
176 (lv_win32_keyboard_queue_item_t*)(_aligned_malloc(
177 sizeof(lv_win32_keyboard_queue_item_t),
178 MEMORY_ALLOCATION_ALIGNMENT));
179 if (current)
180 {
181 current->key = key;
182 current->state = state;
183 InterlockedPushEntrySList(
184 context->keyboard_queue,
185 ¤t->ItemEntry);
186 }
187 }
188
189 /**********************
190 * GLOBAL VARIABLES
191 **********************/
192
193 EXTERN_C bool lv_win32_quit_signal = false;
194
195 EXTERN_C lv_indev_t* lv_win32_pointer_device_object = NULL;
196 EXTERN_C lv_indev_t* lv_win32_keypad_device_object = NULL;
197 EXTERN_C lv_indev_t* lv_win32_encoder_device_object = NULL;
198
199 /**********************
200 * STATIC VARIABLES
201 **********************/
202
203 static HWND g_window_handle = NULL;
204
205 /**********************
206 * MACROS
207 **********************/
208
209 /**********************
210 * GLOBAL FUNCTIONS
211 **********************/
212
lv_win32_add_all_input_devices_to_group(lv_group_t * group)213 EXTERN_C void lv_win32_add_all_input_devices_to_group(
214 lv_group_t* group)
215 {
216 if (!group)
217 {
218 LV_LOG_WARN(
219 "The group object is NULL. Get the default group object instead.");
220
221 group = lv_group_get_default();
222 if (!group)
223 {
224 LV_LOG_WARN(
225 "The default group object is NULL. Create a new group object "
226 "and set it to default instead.");
227
228 group = lv_group_create();
229 if (group)
230 {
231 lv_group_set_default(group);
232 }
233 }
234 }
235
236 LV_ASSERT_MSG(group, "Cannot obtain an available group object.");
237
238 lv_indev_set_group(lv_win32_pointer_device_object, group);
239 lv_indev_set_group(lv_win32_keypad_device_object, group);
240 lv_indev_set_group(lv_win32_encoder_device_object, group);
241 }
242
lv_win32_get_window_context(HWND window_handle)243 EXTERN_C lv_win32_window_context_t* lv_win32_get_window_context(
244 HWND window_handle)
245 {
246 return (lv_win32_window_context_t*)(
247 GetPropW(window_handle, L"LVGL.SimulatorWindow.WindowContext"));
248 }
249
lv_win32_init_window_class()250 EXTERN_C bool lv_win32_init_window_class()
251 {
252 WNDCLASSEXW window_class;
253 window_class.cbSize = sizeof(WNDCLASSEXW);
254 window_class.style = 0;
255 window_class.lpfnWndProc = lv_win32_window_message_callback;
256 window_class.cbClsExtra = 0;
257 window_class.cbWndExtra = 0;
258 window_class.hInstance = NULL;
259 window_class.hIcon = NULL;
260 window_class.hCursor = LoadCursorW(NULL, IDC_ARROW);
261 window_class.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
262 window_class.lpszMenuName = NULL;
263 window_class.lpszClassName = LVGL_SIMULATOR_WINDOW_CLASS;
264 window_class.hIconSm = NULL;
265 return RegisterClassExW(&window_class);
266 }
267
lv_win32_create_display_window(const wchar_t * window_title,lv_coord_t hor_res,lv_coord_t ver_res,HINSTANCE instance_handle,HICON icon_handle,int show_window_mode)268 EXTERN_C HWND lv_win32_create_display_window(
269 const wchar_t* window_title,
270 lv_coord_t hor_res,
271 lv_coord_t ver_res,
272 HINSTANCE instance_handle,
273 HICON icon_handle,
274 int show_window_mode)
275 {
276 HWND display_window_handle = CreateWindowExW(
277 WINDOW_EX_STYLE,
278 LVGL_SIMULATOR_WINDOW_CLASS,
279 window_title,
280 WINDOW_STYLE,
281 CW_USEDEFAULT,
282 0,
283 hor_res,
284 ver_res,
285 NULL,
286 NULL,
287 instance_handle,
288 NULL);
289 if (display_window_handle)
290 {
291 SendMessageW(
292 display_window_handle,
293 WM_SETICON,
294 TRUE,
295 (LPARAM)icon_handle);
296 SendMessageW(
297 display_window_handle,
298 WM_SETICON,
299 FALSE,
300 (LPARAM)icon_handle);
301
302 ShowWindow(display_window_handle, show_window_mode);
303 UpdateWindow(display_window_handle);
304 }
305
306 return display_window_handle;
307 }
308
lv_win32_init(HINSTANCE instance_handle,int show_window_mode,lv_coord_t hor_res,lv_coord_t ver_res,HICON icon_handle)309 EXTERN_C bool lv_win32_init(
310 HINSTANCE instance_handle,
311 int show_window_mode,
312 lv_coord_t hor_res,
313 lv_coord_t ver_res,
314 HICON icon_handle)
315 {
316 if (!lv_win32_init_window_class())
317 {
318 return false;
319 }
320
321 PWINDOW_THREAD_PARAMETER parameter =
322 (PWINDOW_THREAD_PARAMETER)malloc(sizeof(WINDOW_THREAD_PARAMETER));
323 parameter->window_mutex = CreateEventExW(NULL, NULL, 0, EVENT_ALL_ACCESS);
324 parameter->instance_handle = instance_handle;
325 parameter->icon_handle = icon_handle;
326 parameter->hor_res = hor_res;
327 parameter->ver_res = ver_res;
328 parameter->show_window_mode = show_window_mode;
329
330 _beginthreadex(
331 NULL,
332 0,
333 lv_win32_window_thread_entrypoint,
334 parameter,
335 0,
336 NULL);
337
338 WaitForSingleObjectEx(parameter->window_mutex, INFINITE, FALSE);
339
340 lv_win32_window_context_t* context = (lv_win32_window_context_t*)(
341 lv_win32_get_window_context(g_window_handle));
342 if (!context)
343 {
344 return false;
345 }
346
347 lv_win32_pointer_device_object = context->mouse_device_object;
348 lv_win32_keypad_device_object = context->keyboard_device_object;
349 lv_win32_encoder_device_object = context->mousewheel_device_object;
350
351 return true;
352 }
353
354 /**********************
355 * STATIC FUNCTIONS
356 **********************/
357
lv_win32_create_frame_buffer(HWND WindowHandle,LONG Width,LONG Height,UINT32 ** PixelBuffer,SIZE_T * PixelBufferSize)358 static HDC lv_win32_create_frame_buffer(
359 HWND WindowHandle,
360 LONG Width,
361 LONG Height,
362 UINT32** PixelBuffer,
363 SIZE_T* PixelBufferSize)
364 {
365 HDC hFrameBufferDC = NULL;
366
367 if (PixelBuffer && PixelBufferSize)
368 {
369 HDC hWindowDC = GetDC(WindowHandle);
370 if (hWindowDC)
371 {
372 hFrameBufferDC = CreateCompatibleDC(hWindowDC);
373 ReleaseDC(WindowHandle, hWindowDC);
374 }
375
376 if (hFrameBufferDC)
377 {
378 #if LV_COLOR_DEPTH == 32
379 BITMAPINFO BitmapInfo = { 0 };
380 #elif LV_COLOR_DEPTH == 16
381 typedef struct _BITMAPINFO_16BPP {
382 BITMAPINFOHEADER bmiHeader;
383 DWORD bmiColorMask[3];
384 } BITMAPINFO_16BPP, *PBITMAPINFO_16BPP;
385
386 BITMAPINFO_16BPP BitmapInfo = { 0 };
387 #elif LV_COLOR_DEPTH == 8
388 typedef struct _BITMAPINFO_8BPP {
389 BITMAPINFOHEADER bmiHeader;
390 RGBQUAD bmiColors[256];
391 } BITMAPINFO_8BPP, *PBITMAPINFO_8BPP;
392
393 BITMAPINFO_8BPP BitmapInfo = { 0 };
394 #elif LV_COLOR_DEPTH == 1
395 typedef struct _BITMAPINFO_1BPP {
396 BITMAPINFOHEADER bmiHeader;
397 RGBQUAD bmiColors[2];
398 } BITMAPINFO_1BPP, *PBITMAPINFO_1BPP;
399
400 BITMAPINFO_1BPP BitmapInfo = { 0 };
401 #else
402 BITMAPINFO BitmapInfo = { 0 };
403 #endif
404
405 BitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
406 BitmapInfo.bmiHeader.biWidth = Width;
407 BitmapInfo.bmiHeader.biHeight = -Height;
408 BitmapInfo.bmiHeader.biPlanes = 1;
409 #if LV_COLOR_DEPTH == 32
410 BitmapInfo.bmiHeader.biBitCount = 32;
411 BitmapInfo.bmiHeader.biCompression = BI_RGB;
412 #elif LV_COLOR_DEPTH == 16
413 BitmapInfo.bmiHeader.biBitCount = 16;
414 BitmapInfo.bmiHeader.biCompression = BI_BITFIELDS;
415 BitmapInfo.bmiColorMask[0] = 0xF800;
416 BitmapInfo.bmiColorMask[1] = 0x07E0;
417 BitmapInfo.bmiColorMask[2] = 0x001F;
418 #elif LV_COLOR_DEPTH == 8
419 BitmapInfo.bmiHeader.biBitCount = 8;
420 BitmapInfo.bmiHeader.biCompression = BI_RGB;
421 for (size_t i = 0; i < 256; ++i)
422 {
423 lv_color8_t color;
424 color.full = i;
425
426 BitmapInfo.bmiColors[i].rgbRed = LV_COLOR_GET_R(color) * 36;
427 BitmapInfo.bmiColors[i].rgbGreen = LV_COLOR_GET_G(color) * 36;
428 BitmapInfo.bmiColors[i].rgbBlue = LV_COLOR_GET_B(color) * 85;
429 BitmapInfo.bmiColors[i].rgbReserved = 0xFF;
430 }
431 #elif LV_COLOR_DEPTH == 1
432 BitmapInfo.bmiHeader.biBitCount = 8;
433 BitmapInfo.bmiHeader.biCompression = BI_RGB;
434 BitmapInfo.bmiHeader.biClrUsed = 2;
435 BitmapInfo.bmiHeader.biClrImportant = 2;
436 BitmapInfo.bmiColors[0].rgbRed = 0x00;
437 BitmapInfo.bmiColors[0].rgbGreen = 0x00;
438 BitmapInfo.bmiColors[0].rgbBlue = 0x00;
439 BitmapInfo.bmiColors[0].rgbReserved = 0xFF;
440 BitmapInfo.bmiColors[1].rgbRed = 0xFF;
441 BitmapInfo.bmiColors[1].rgbGreen = 0xFF;
442 BitmapInfo.bmiColors[1].rgbBlue = 0xFF;
443 BitmapInfo.bmiColors[1].rgbReserved = 0xFF;
444 #else
445 BitmapInfo.bmiHeader.biBitCount = 32;
446 BitmapInfo.bmiHeader.biCompression = BI_RGB;
447 #endif
448
449 HBITMAP hBitmap = CreateDIBSection(
450 hFrameBufferDC,
451 (PBITMAPINFO)(&BitmapInfo),
452 DIB_RGB_COLORS,
453 (void**)PixelBuffer,
454 NULL,
455 0);
456 if (hBitmap)
457 {
458 #if LV_COLOR_DEPTH == 32
459 *PixelBufferSize = Width * Height * sizeof(UINT32);
460 #elif LV_COLOR_DEPTH == 16
461 *PixelBufferSize = Width * Height * sizeof(UINT16);
462 #elif LV_COLOR_DEPTH == 8
463 *PixelBufferSize = Width * Height * sizeof(UINT8);
464 #elif LV_COLOR_DEPTH == 1
465 *PixelBufferSize = Width * Height * sizeof(UINT8);
466 #else
467 *PixelBufferSize = Width * Height * sizeof(UINT32);
468 #endif
469
470 DeleteObject(SelectObject(hFrameBufferDC, hBitmap));
471 DeleteObject(hBitmap);
472 }
473 else
474 {
475 DeleteDC(hFrameBufferDC);
476 hFrameBufferDC = NULL;
477 }
478 }
479 }
480
481 return hFrameBufferDC;
482 }
483
lv_win32_enable_child_window_dpi_message(HWND WindowHandle)484 static BOOL lv_win32_enable_child_window_dpi_message(
485 HWND WindowHandle)
486 {
487 // This hack is only for Windows 10 TH1/TH2 only.
488 // We don't need this hack if the Per Monitor Aware V2 is existed.
489 OSVERSIONINFOEXW OSVersionInfoEx = { 0 };
490 OSVersionInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
491 OSVersionInfoEx.dwMajorVersion = 10;
492 OSVersionInfoEx.dwMinorVersion = 0;
493 OSVersionInfoEx.dwBuildNumber = 14393;
494 if (!VerifyVersionInfoW(
495 &OSVersionInfoEx,
496 VER_MAJORVERSION | VER_MINORVERSION | VER_BUILDNUMBER,
497 VerSetConditionMask(
498 VerSetConditionMask(
499 VerSetConditionMask(
500 0,
501 VER_MAJORVERSION,
502 VER_GREATER_EQUAL),
503 VER_MINORVERSION,
504 VER_GREATER_EQUAL),
505 VER_BUILDNUMBER,
506 VER_LESS)))
507 {
508 return FALSE;
509 }
510
511 HMODULE ModuleHandle = GetModuleHandleW(L"user32.dll");
512 if (!ModuleHandle)
513 {
514 return FALSE;
515 }
516
517 typedef BOOL(WINAPI* FunctionType)(HWND, BOOL);
518
519 FunctionType pFunction = (FunctionType)(
520 GetProcAddress(ModuleHandle, "EnableChildWindowDpiMessage"));
521 if (!pFunction)
522 {
523 return FALSE;
524 }
525
526 return pFunction(WindowHandle, TRUE);
527 }
528
lv_win32_register_touch_window(HWND hWnd,ULONG ulFlags)529 static BOOL lv_win32_register_touch_window(
530 HWND hWnd,
531 ULONG ulFlags)
532 {
533 HMODULE ModuleHandle = GetModuleHandleW(L"user32.dll");
534 if (!ModuleHandle)
535 {
536 return FALSE;
537 }
538
539 typedef BOOL(WINAPI* FunctionType)(HWND, ULONG);
540
541 FunctionType pFunction = (FunctionType)(
542 GetProcAddress(ModuleHandle, "RegisterTouchWindow"));
543 if (!pFunction)
544 {
545 return FALSE;
546 }
547
548 return pFunction(hWnd, ulFlags);
549 }
550
lv_win32_get_touch_input_info(HTOUCHINPUT hTouchInput,UINT cInputs,PTOUCHINPUT pInputs,int cbSize)551 static BOOL lv_win32_get_touch_input_info(
552 HTOUCHINPUT hTouchInput,
553 UINT cInputs,
554 PTOUCHINPUT pInputs,
555 int cbSize)
556 {
557 HMODULE ModuleHandle = GetModuleHandleW(L"user32.dll");
558 if (!ModuleHandle)
559 {
560 return FALSE;
561 }
562
563 typedef BOOL(WINAPI* FunctionType)(HTOUCHINPUT, UINT, PTOUCHINPUT, int);
564
565 FunctionType pFunction = (FunctionType)(
566 GetProcAddress(ModuleHandle, "GetTouchInputInfo"));
567 if (!pFunction)
568 {
569 return FALSE;
570 }
571
572 return pFunction(hTouchInput, cInputs, pInputs, cbSize);
573 }
574
lv_win32_close_touch_input_handle(HTOUCHINPUT hTouchInput)575 static BOOL lv_win32_close_touch_input_handle(
576 HTOUCHINPUT hTouchInput)
577 {
578 HMODULE ModuleHandle = GetModuleHandleW(L"user32.dll");
579 if (!ModuleHandle)
580 {
581 return FALSE;
582 }
583
584 typedef BOOL(WINAPI* FunctionType)(HTOUCHINPUT);
585
586 FunctionType pFunction = (FunctionType)(
587 GetProcAddress(ModuleHandle, "CloseTouchInputHandle"));
588 if (!pFunction)
589 {
590 return FALSE;
591 }
592
593 return pFunction(hTouchInput);
594 }
595
lv_win32_get_dpi_for_window(_In_ HWND WindowHandle)596 static UINT lv_win32_get_dpi_for_window(
597 _In_ HWND WindowHandle)
598 {
599 UINT Result = (UINT)(-1);
600
601 HMODULE ModuleHandle = LoadLibraryW(L"SHCore.dll");
602 if (ModuleHandle)
603 {
604 typedef enum MONITOR_DPI_TYPE_PRIVATE {
605 MDT_EFFECTIVE_DPI = 0,
606 MDT_ANGULAR_DPI = 1,
607 MDT_RAW_DPI = 2,
608 MDT_DEFAULT = MDT_EFFECTIVE_DPI
609 } MONITOR_DPI_TYPE_PRIVATE;
610
611 typedef HRESULT(WINAPI* FunctionType)(
612 HMONITOR, MONITOR_DPI_TYPE_PRIVATE, UINT*, UINT*);
613
614 FunctionType pFunction = (FunctionType)(
615 GetProcAddress(ModuleHandle, "GetDpiForMonitor"));
616 if (pFunction)
617 {
618 HMONITOR MonitorHandle = MonitorFromWindow(
619 WindowHandle,
620 MONITOR_DEFAULTTONEAREST);
621
622 UINT dpiX = 0;
623 UINT dpiY = 0;
624 if (SUCCEEDED(pFunction(
625 MonitorHandle,
626 MDT_EFFECTIVE_DPI,
627 &dpiX,
628 &dpiY)))
629 {
630 Result = dpiX;
631 }
632 }
633
634 FreeLibrary(ModuleHandle);
635 }
636
637 if (Result == (UINT)(-1))
638 {
639 HDC hWindowDC = GetDC(WindowHandle);
640 if (hWindowDC)
641 {
642 Result = GetDeviceCaps(hWindowDC, LOGPIXELSX);
643 ReleaseDC(WindowHandle, hWindowDC);
644 }
645 }
646
647 if (Result == (UINT)(-1))
648 {
649 Result = USER_DEFAULT_SCREEN_DPI;
650 }
651
652 return Result;
653 }
654
lv_win32_display_driver_flush_callback(lv_disp_drv_t * disp_drv,const lv_area_t * area,lv_color_t * color_p)655 static void lv_win32_display_driver_flush_callback(
656 lv_disp_drv_t* disp_drv,
657 const lv_area_t* area,
658 lv_color_t* color_p)
659 {
660 lv_win32_window_context_t* context = (lv_win32_window_context_t*)(
661 lv_win32_get_window_context((HWND)disp_drv->user_data));
662 if (context)
663 {
664 if (lv_disp_flush_is_last(disp_drv))
665 {
666 #if (LV_COLOR_DEPTH == 32) || \
667 (LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP == 0) || \
668 (LV_COLOR_DEPTH == 8) || \
669 (LV_COLOR_DEPTH == 1)
670 UNREFERENCED_PARAMETER(color_p);
671 #elif (LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP != 0)
672 SIZE_T count = context->display_framebuffer_size / sizeof(UINT16);
673 PUINT16 source = (PUINT16)color_p;
674 PUINT16 destination = (PUINT16)context->display_framebuffer_base;
675 for (SIZE_T i = 0; i < count; ++i)
676 {
677 UINT16 current = *source;
678 *destination = (LOBYTE(current) << 8) | HIBYTE(current);
679
680 ++source;
681 ++destination;
682 }
683 #else
684 uint32_t* destination = context->display_framebuffer_base;
685
686 for (int y = area->y1; y <= area->y2; ++y)
687 {
688 for (int x = area->x1; x <= area->x2; ++x)
689 {
690 destination[y * disp_drv->hor_res + x] =
691 lv_color_to32(*color_p);
692 color_p++;
693 }
694 }
695 #endif
696
697 InvalidateRect(disp_drv->user_data, NULL, FALSE);
698 }
699 }
700
701 lv_disp_flush_ready(disp_drv);
702 }
703
lv_win32_pointer_driver_read_callback(lv_indev_drv_t * indev_drv,lv_indev_data_t * data)704 static void lv_win32_pointer_driver_read_callback(
705 lv_indev_drv_t* indev_drv,
706 lv_indev_data_t* data)
707 {
708 lv_win32_window_context_t* context = (lv_win32_window_context_t*)(
709 lv_win32_get_display_context(indev_drv->disp));
710 if (!context)
711 {
712 return;
713 }
714
715 data->state = context->mouse_state;
716 data->point = context->mouse_point;
717 }
718
lv_win32_keypad_driver_read_callback(lv_indev_drv_t * indev_drv,lv_indev_data_t * data)719 static void lv_win32_keypad_driver_read_callback(
720 lv_indev_drv_t* indev_drv,
721 lv_indev_data_t* data)
722 {
723 lv_win32_window_context_t* context = (lv_win32_window_context_t*)(
724 lv_win32_get_display_context(indev_drv->disp));
725 if (!context)
726 {
727 return;
728 }
729
730 EnterCriticalSection(&context->keyboard_mutex);
731
732 lv_win32_keyboard_queue_item_t* current =
733 (lv_win32_keyboard_queue_item_t*)(InterlockedPopEntrySList(
734 context->keyboard_queue));
735 if (current)
736 {
737 data->key = current->key;
738 data->state = current->state;
739
740 _aligned_free(current);
741
742 data->continue_reading = true;
743 }
744
745 LeaveCriticalSection(&context->keyboard_mutex);
746 }
747
lv_win32_encoder_driver_read_callback(lv_indev_drv_t * indev_drv,lv_indev_data_t * data)748 static void lv_win32_encoder_driver_read_callback(
749 lv_indev_drv_t* indev_drv,
750 lv_indev_data_t* data)
751 {
752 lv_win32_window_context_t* context = (lv_win32_window_context_t*)(
753 lv_win32_get_display_context(indev_drv->disp));
754 if (!context)
755 {
756 return;
757 }
758
759 data->state = context->mousewheel_state;
760 data->enc_diff = context->mousewheel_enc_diff;
761 context->mousewheel_enc_diff = 0;
762 }
763
lv_win32_get_display_context(lv_disp_t * display)764 static lv_win32_window_context_t* lv_win32_get_display_context(
765 lv_disp_t* display)
766 {
767 if (display)
768 {
769 return lv_win32_get_window_context((HWND)display->driver->user_data);
770 }
771
772 return NULL;
773 }
774
lv_win32_window_message_callback(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)775 static LRESULT CALLBACK lv_win32_window_message_callback(
776 HWND hWnd,
777 UINT uMsg,
778 WPARAM wParam,
779 LPARAM lParam)
780 {
781 switch (uMsg)
782 {
783 case WM_CREATE:
784 {
785 // Note: Return -1 directly because WM_DESTROY message will be sent
786 // when destroy the window automatically. We free the resource when
787 // processing the WM_DESTROY message of this window.
788
789 lv_win32_window_context_t* context = (lv_win32_window_context_t*)(
790 malloc(sizeof(lv_win32_window_context_t)));
791 if (!context)
792 {
793 return -1;
794 }
795
796 RECT request_content_size;
797 GetWindowRect(hWnd, &request_content_size);
798
799 context->display_hor_res =
800 request_content_size.right - request_content_size.left;
801 context->display_ver_res =
802 request_content_size.bottom - request_content_size.top;
803 context->display_dpi = lv_win32_get_dpi_for_window(hWnd);
804 context->display_framebuffer_context_handle =
805 lv_win32_create_frame_buffer(
806 hWnd,
807 context->display_hor_res,
808 context->display_ver_res,
809 &context->display_framebuffer_base,
810 &context->display_framebuffer_size);
811 #if (LV_COLOR_DEPTH == 32) || \
812 (LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP == 0) || \
813 (LV_COLOR_DEPTH == 8) || \
814 (LV_COLOR_DEPTH == 1)
815 lv_disp_draw_buf_init(
816 &context->display_buffer,
817 (lv_color_t*)context->display_framebuffer_base,
818 NULL,
819 context->display_hor_res * context->display_ver_res);
820 #else
821 size_t draw_buffer_size = sizeof(lv_color_t);
822 draw_buffer_size *= context->display_hor_res;
823 draw_buffer_size *= context->display_ver_res;
824 lv_disp_draw_buf_init(
825 &context->display_buffer,
826 (lv_color_t*)malloc(draw_buffer_size),
827 NULL,
828 context->display_hor_res * context->display_ver_res);
829 #endif
830 lv_disp_drv_init(&context->display_driver);
831 context->display_driver.hor_res = context->display_hor_res;
832 context->display_driver.ver_res = context->display_ver_res;
833 context->display_driver.flush_cb =
834 lv_win32_display_driver_flush_callback;
835 context->display_driver.draw_buf = &context->display_buffer;
836 context->display_driver.direct_mode = 1;
837 context->display_driver.user_data = hWnd;
838 context->display_device_object =
839 lv_disp_drv_register(&context->display_driver);
840 if (!context->display_device_object)
841 {
842 return -1;
843 }
844
845 context->mouse_state = LV_INDEV_STATE_REL;
846 context->mouse_point.x = 0;
847 context->mouse_point.y = 0;
848 lv_indev_drv_init(&context->mouse_driver);
849 context->mouse_driver.type = LV_INDEV_TYPE_POINTER;
850 context->mouse_driver.disp = context->display_device_object;
851 context->mouse_driver.read_cb =
852 lv_win32_pointer_driver_read_callback;
853 context->mouse_device_object =
854 lv_indev_drv_register(&context->mouse_driver);
855 if (!context->mouse_device_object)
856 {
857 return -1;
858 }
859
860 context->mousewheel_state = LV_INDEV_STATE_REL;
861 context->mousewheel_enc_diff = 0;
862 lv_indev_drv_init(&context->mousewheel_driver);
863 context->mousewheel_driver.type = LV_INDEV_TYPE_ENCODER;
864 context->mousewheel_driver.disp = context->display_device_object;
865 context->mousewheel_driver.read_cb =
866 lv_win32_encoder_driver_read_callback;
867 context->mousewheel_device_object =
868 lv_indev_drv_register(&context->mousewheel_driver);
869 if (!context->mousewheel_device_object)
870 {
871 return -1;
872 }
873
874 InitializeCriticalSection(&context->keyboard_mutex);
875 context->keyboard_queue = _aligned_malloc(
876 sizeof(SLIST_HEADER),
877 MEMORY_ALLOCATION_ALIGNMENT);
878 if (!context->keyboard_queue)
879 {
880 return -1;
881 }
882 InitializeSListHead(context->keyboard_queue);
883 context->keyboard_utf16_high_surrogate = 0;
884 context->keyboard_utf16_low_surrogate = 0;
885 lv_indev_drv_init(&context->keyboard_driver);
886 context->keyboard_driver.type = LV_INDEV_TYPE_KEYPAD;
887 context->keyboard_driver.disp = context->display_device_object;
888 context->keyboard_driver.read_cb =
889 lv_win32_keypad_driver_read_callback;
890 context->keyboard_device_object =
891 lv_indev_drv_register(&context->keyboard_driver);
892 if (!context->keyboard_device_object)
893 {
894 return -1;
895 }
896
897 if (!SetPropW(
898 hWnd,
899 L"LVGL.SimulatorWindow.WindowContext",
900 (HANDLE)(context)))
901 {
902 return -1;
903 }
904
905 RECT calculated_window_size;
906
907 calculated_window_size.left = 0;
908 calculated_window_size.right = MulDiv(
909 context->display_hor_res * WIN32DRV_MONITOR_ZOOM,
910 context->display_dpi,
911 USER_DEFAULT_SCREEN_DPI);
912 calculated_window_size.top = 0;
913 calculated_window_size.bottom = MulDiv(
914 context->display_ver_res * WIN32DRV_MONITOR_ZOOM,
915 context->display_dpi,
916 USER_DEFAULT_SCREEN_DPI);
917
918 AdjustWindowRectEx(
919 &calculated_window_size,
920 WINDOW_STYLE,
921 FALSE,
922 WINDOW_EX_STYLE);
923 OffsetRect(
924 &calculated_window_size,
925 -calculated_window_size.left,
926 -calculated_window_size.top);
927
928 SetWindowPos(
929 hWnd,
930 NULL,
931 0,
932 0,
933 calculated_window_size.right,
934 calculated_window_size.bottom,
935 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE);
936
937 lv_win32_register_touch_window(hWnd, 0);
938
939 lv_win32_enable_child_window_dpi_message(hWnd);
940
941 break;
942 }
943 case WM_MOUSEMOVE:
944 case WM_LBUTTONDOWN:
945 case WM_LBUTTONUP:
946 case WM_MBUTTONDOWN:
947 case WM_MBUTTONUP:
948 {
949 lv_win32_window_context_t* context = (lv_win32_window_context_t*)(
950 lv_win32_get_window_context(hWnd));
951 if (!context)
952 {
953 return 0;
954 }
955
956 context->mouse_point.x = MulDiv(
957 GET_X_LPARAM(lParam),
958 USER_DEFAULT_SCREEN_DPI,
959 WIN32DRV_MONITOR_ZOOM * context->display_dpi);
960 context->mouse_point.y = MulDiv(
961 GET_Y_LPARAM(lParam),
962 USER_DEFAULT_SCREEN_DPI,
963 WIN32DRV_MONITOR_ZOOM * context->display_dpi);
964 if (context->mouse_point.x < 0)
965 {
966 context->mouse_point.x = 0;
967 }
968 if (context->mouse_point.x > context->display_hor_res - 1)
969 {
970 context->mouse_point.x = context->display_hor_res - 1;
971 }
972 if (context->mouse_point.y < 0)
973 {
974 context->mouse_point.y = 0;
975 }
976 if (context->mouse_point.y > context->display_ver_res - 1)
977 {
978 context->mouse_point.y = context->display_ver_res - 1;
979 }
980
981 if (uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONUP)
982 {
983 context->mouse_state = (
984 uMsg == WM_LBUTTONDOWN
985 ? LV_INDEV_STATE_PR
986 : LV_INDEV_STATE_REL);
987 }
988 else if (uMsg == WM_MBUTTONDOWN || uMsg == WM_MBUTTONUP)
989 {
990 context->mousewheel_state = (
991 uMsg == WM_MBUTTONDOWN
992 ? LV_INDEV_STATE_PR
993 : LV_INDEV_STATE_REL);
994 }
995 return 0;
996 }
997 case WM_KEYDOWN:
998 case WM_KEYUP:
999 {
1000 lv_win32_window_context_t* context = (lv_win32_window_context_t*)(
1001 lv_win32_get_window_context(hWnd));
1002 if (context)
1003 {
1004 EnterCriticalSection(&context->keyboard_mutex);
1005
1006 bool skip_translation = false;
1007 uint32_t translated_key = 0;
1008
1009 switch (wParam)
1010 {
1011 case VK_UP:
1012 translated_key = LV_KEY_UP;
1013 break;
1014 case VK_DOWN:
1015 translated_key = LV_KEY_DOWN;
1016 break;
1017 case VK_LEFT:
1018 translated_key = LV_KEY_LEFT;
1019 break;
1020 case VK_RIGHT:
1021 translated_key = LV_KEY_RIGHT;
1022 break;
1023 case VK_ESCAPE:
1024 translated_key = LV_KEY_ESC;
1025 break;
1026 case VK_DELETE:
1027 translated_key = LV_KEY_DEL;
1028 break;
1029 case VK_BACK:
1030 translated_key = LV_KEY_BACKSPACE;
1031 break;
1032 case VK_RETURN:
1033 translated_key = LV_KEY_ENTER;
1034 break;
1035 case VK_TAB:
1036 case VK_NEXT:
1037 translated_key = LV_KEY_NEXT;
1038 break;
1039 case VK_PRIOR:
1040 translated_key = LV_KEY_PREV;
1041 break;
1042 case VK_HOME:
1043 translated_key = LV_KEY_HOME;
1044 break;
1045 case VK_END:
1046 translated_key = LV_KEY_END;
1047 break;
1048 default:
1049 skip_translation = true;
1050 break;
1051 }
1052
1053 if (!skip_translation)
1054 {
1055 lv_win32_push_key_to_keyboard_queue(
1056 context,
1057 translated_key,
1058 ((uMsg == WM_KEYUP)
1059 ? LV_INDEV_STATE_REL
1060 : LV_INDEV_STATE_PR));
1061 }
1062
1063 LeaveCriticalSection(&context->keyboard_mutex);
1064 }
1065
1066 break;
1067 }
1068 case WM_CHAR:
1069 {
1070 lv_win32_window_context_t* context = (lv_win32_window_context_t*)(
1071 lv_win32_get_window_context(hWnd));
1072 if (context)
1073 {
1074 EnterCriticalSection(&context->keyboard_mutex);
1075
1076 uint16_t raw_code_point = (uint16_t)(wParam);
1077
1078 if (raw_code_point >= 0x20 && raw_code_point != 0x7F)
1079 {
1080 if (IS_HIGH_SURROGATE(raw_code_point))
1081 {
1082 context->keyboard_utf16_high_surrogate = raw_code_point;
1083 }
1084 else if (IS_LOW_SURROGATE(raw_code_point))
1085 {
1086 context->keyboard_utf16_low_surrogate = raw_code_point;
1087 }
1088
1089 uint32_t code_point = raw_code_point;
1090
1091 if (context->keyboard_utf16_high_surrogate &&
1092 context->keyboard_utf16_low_surrogate)
1093 {
1094 uint16_t high_surrogate =
1095 context->keyboard_utf16_high_surrogate;
1096 uint16_t low_surrogate =
1097 context->keyboard_utf16_low_surrogate;
1098
1099 code_point = (low_surrogate & 0x03FF);
1100 code_point += (((high_surrogate & 0x03FF) + 0x40) << 10);
1101
1102 context->keyboard_utf16_high_surrogate = 0;
1103 context->keyboard_utf16_low_surrogate = 0;
1104 }
1105
1106 uint32_t lvgl_code_point =
1107 _lv_txt_unicode_to_encoded(code_point);
1108
1109 lv_win32_push_key_to_keyboard_queue(
1110 context,
1111 lvgl_code_point,
1112 LV_INDEV_STATE_PR);
1113 lv_win32_push_key_to_keyboard_queue(
1114 context,
1115 lvgl_code_point,
1116 LV_INDEV_STATE_REL);
1117 }
1118
1119 LeaveCriticalSection(&context->keyboard_mutex);
1120 }
1121
1122 break;
1123 }
1124 case WM_MOUSEWHEEL:
1125 {
1126 lv_win32_window_context_t* context = (lv_win32_window_context_t*)(
1127 lv_win32_get_window_context(hWnd));
1128 if (context)
1129 {
1130 context->mousewheel_enc_diff =
1131 -(GET_WHEEL_DELTA_WPARAM(wParam) / WHEEL_DELTA);
1132 }
1133
1134 break;
1135 }
1136 case WM_TOUCH:
1137 {
1138 lv_win32_window_context_t* context = (lv_win32_window_context_t*)(
1139 lv_win32_get_window_context(hWnd));
1140 if (context)
1141 {
1142 UINT cInputs = LOWORD(wParam);
1143 HTOUCHINPUT hTouchInput = (HTOUCHINPUT)(lParam);
1144
1145 PTOUCHINPUT pInputs = malloc(cInputs * sizeof(TOUCHINPUT));
1146 if (pInputs)
1147 {
1148 if (lv_win32_get_touch_input_info(
1149 hTouchInput,
1150 cInputs,
1151 pInputs,
1152 sizeof(TOUCHINPUT)))
1153 {
1154 for (UINT i = 0; i < cInputs; ++i)
1155 {
1156 POINT Point;
1157 Point.x = TOUCH_COORD_TO_PIXEL(pInputs[i].x);
1158 Point.y = TOUCH_COORD_TO_PIXEL(pInputs[i].y);
1159 if (!ScreenToClient(hWnd, &Point))
1160 {
1161 continue;
1162 }
1163
1164 context->mouse_point.x = MulDiv(
1165 Point.x,
1166 USER_DEFAULT_SCREEN_DPI,
1167 WIN32DRV_MONITOR_ZOOM * context->display_dpi);
1168 context->mouse_point.y = MulDiv(
1169 Point.y,
1170 USER_DEFAULT_SCREEN_DPI,
1171 WIN32DRV_MONITOR_ZOOM * context->display_dpi);
1172
1173 DWORD MousePressedMask =
1174 TOUCHEVENTF_MOVE | TOUCHEVENTF_DOWN;
1175
1176 context->mouse_state = (
1177 pInputs[i].dwFlags & MousePressedMask
1178 ? LV_INDEV_STATE_PR
1179 : LV_INDEV_STATE_REL);
1180 }
1181 }
1182
1183 free(pInputs);
1184 }
1185
1186 lv_win32_close_touch_input_handle(hTouchInput);
1187 }
1188
1189 break;
1190 }
1191 case WM_DPICHANGED:
1192 {
1193 lv_win32_window_context_t* context = (lv_win32_window_context_t*)(
1194 lv_win32_get_window_context(hWnd));
1195 if (context)
1196 {
1197 context->display_dpi = HIWORD(wParam);
1198
1199 LPRECT SuggestedRect = (LPRECT)lParam;
1200
1201 SetWindowPos(
1202 hWnd,
1203 NULL,
1204 SuggestedRect->left,
1205 SuggestedRect->top,
1206 SuggestedRect->right,
1207 SuggestedRect->bottom,
1208 SWP_NOZORDER | SWP_NOACTIVATE);
1209
1210 RECT ClientRect;
1211 GetClientRect(hWnd, &ClientRect);
1212
1213 int WindowWidth = MulDiv(
1214 context->display_hor_res * WIN32DRV_MONITOR_ZOOM,
1215 context->display_dpi,
1216 USER_DEFAULT_SCREEN_DPI);
1217 int WindowHeight = MulDiv(
1218 context->display_ver_res * WIN32DRV_MONITOR_ZOOM,
1219 context->display_dpi,
1220 USER_DEFAULT_SCREEN_DPI);
1221
1222 SetWindowPos(
1223 hWnd,
1224 NULL,
1225 SuggestedRect->left,
1226 SuggestedRect->top,
1227 SuggestedRect->right + (WindowWidth - ClientRect.right),
1228 SuggestedRect->bottom + (WindowHeight - ClientRect.bottom),
1229 SWP_NOZORDER | SWP_NOACTIVATE);
1230 }
1231
1232 break;
1233 }
1234 case WM_PAINT:
1235 {
1236 PAINTSTRUCT ps;
1237 HDC hdc = BeginPaint(hWnd, &ps);
1238
1239 lv_win32_window_context_t* context = (lv_win32_window_context_t*)(
1240 lv_win32_get_window_context(hWnd));
1241 if (context)
1242 {
1243 if (context->display_framebuffer_context_handle)
1244 {
1245 SetStretchBltMode(hdc, HALFTONE);
1246
1247 StretchBlt(
1248 hdc,
1249 ps.rcPaint.left,
1250 ps.rcPaint.top,
1251 ps.rcPaint.right - ps.rcPaint.left,
1252 ps.rcPaint.bottom - ps.rcPaint.top,
1253 context->display_framebuffer_context_handle,
1254 0,
1255 0,
1256 MulDiv(
1257 ps.rcPaint.right - ps.rcPaint.left,
1258 USER_DEFAULT_SCREEN_DPI,
1259 WIN32DRV_MONITOR_ZOOM * context->display_dpi),
1260 MulDiv(
1261 ps.rcPaint.bottom - ps.rcPaint.top,
1262 USER_DEFAULT_SCREEN_DPI,
1263 WIN32DRV_MONITOR_ZOOM * context->display_dpi),
1264 SRCCOPY);
1265 }
1266 }
1267
1268 EndPaint(hWnd, &ps);
1269
1270 break;
1271 }
1272 case WM_DESTROY:
1273 {
1274 lv_win32_window_context_t* context = (lv_win32_window_context_t*)(
1275 RemovePropW(hWnd, L"LVGL.SimulatorWindow.WindowContext"));
1276 if (context)
1277 {
1278 lv_disp_t* display_device_object = context->display_device_object;
1279 context->display_device_object = NULL;
1280 lv_disp_remove(display_device_object);
1281 #if (LV_COLOR_DEPTH == 32) || \
1282 (LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP == 0) || \
1283 (LV_COLOR_DEPTH == 8) || \
1284 (LV_COLOR_DEPTH == 1)
1285 #else
1286 free(context->display_buffer.buf1);
1287 #endif
1288 DeleteDC(context->display_framebuffer_context_handle);
1289
1290 lv_indev_t* mouse_device_object =
1291 context->mouse_device_object;
1292 context->mouse_device_object = NULL;
1293 lv_indev_delete(mouse_device_object);
1294
1295 lv_indev_t* mousewheel_device_object =
1296 context->mousewheel_device_object;
1297 context->mousewheel_device_object = NULL;
1298 lv_indev_delete(mousewheel_device_object);
1299
1300 lv_indev_t* keyboard_device_object =
1301 context->keyboard_device_object;
1302 context->keyboard_device_object = NULL;
1303 lv_indev_delete(keyboard_device_object);
1304 do
1305 {
1306 PSLIST_ENTRY current = InterlockedPopEntrySList(
1307 context->keyboard_queue);
1308 if (!current)
1309 {
1310 _aligned_free(context->keyboard_queue);
1311 context->keyboard_queue = NULL;
1312 break;
1313 }
1314
1315 _aligned_free(current);
1316
1317 } while (true);
1318 DeleteCriticalSection(&context->keyboard_mutex);
1319
1320 free(context);
1321 }
1322
1323 PostQuitMessage(0);
1324
1325 break;
1326 }
1327 default:
1328 return DefWindowProcW(hWnd, uMsg, wParam, lParam);
1329 }
1330
1331 return 0;
1332 }
1333
lv_win32_window_thread_entrypoint(void * raw_parameter)1334 static unsigned int __stdcall lv_win32_window_thread_entrypoint(
1335 void* raw_parameter)
1336 {
1337 PWINDOW_THREAD_PARAMETER parameter =
1338 (PWINDOW_THREAD_PARAMETER)raw_parameter;
1339
1340 g_window_handle = lv_win32_create_display_window(
1341 L"LVGL Simulator for Windows Desktop (Display 1)",
1342 parameter->hor_res,
1343 parameter->ver_res,
1344 parameter->instance_handle,
1345 parameter->icon_handle,
1346 parameter->show_window_mode);
1347 if (!g_window_handle)
1348 {
1349 return 0;
1350 }
1351
1352 SetEvent(parameter->window_mutex);
1353
1354 MSG message;
1355 while (GetMessageW(&message, NULL, 0, 0))
1356 {
1357 TranslateMessage(&message);
1358 DispatchMessageW(&message);
1359 }
1360
1361 lv_win32_quit_signal = true;
1362
1363 return 0;
1364 }
1365
1366 #endif /*USE_WIN32DRV*/
1367