1 /**
2  * @file lv_windows_context.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 
10 #include "lv_windows_context.h"
11 #if LV_USE_WINDOWS
12 
13 #ifdef __GNUC__
14     #pragma GCC diagnostic ignored "-Wcast-function-type"
15 #endif
16 
17 #include "lv_windows_display.h"
18 #include "lv_windows_input_private.h"
19 #include "../../osal/lv_os.h"
20 
21 /*********************
22  *      DEFINES
23  *********************/
24 
25 /**********************
26  *      TYPEDEFS
27  **********************/
28 
29 /**********************
30  *  STATIC PROTOTYPES
31  **********************/
32 
33 static uint32_t lv_windows_tick_count_callback(void);
34 
35 static void lv_windows_delay_callback(uint32_t ms);
36 
37 static void lv_windows_check_display_existence_timer_callback(
38     lv_timer_t * timer);
39 
40 static bool lv_windows_window_message_callback_nolock(
41     HWND hWnd,
42     UINT uMsg,
43     WPARAM wParam,
44     LPARAM lParam,
45     LRESULT * plResult);
46 
47 static LRESULT CALLBACK lv_windows_window_message_callback(
48     HWND   hWnd,
49     UINT   uMsg,
50     WPARAM wParam,
51     LPARAM lParam);
52 
53 /**********************
54  *  STATIC VARIABLES
55  **********************/
56 
57 /**********************
58  *      MACROS
59  **********************/
60 
61 /**********************
62  *   GLOBAL FUNCTIONS
63  **********************/
64 
lv_windows_platform_init(void)65 void lv_windows_platform_init(void)
66 {
67     lv_tick_set_cb(lv_windows_tick_count_callback);
68 
69     lv_delay_set_cb(lv_windows_delay_callback);
70 
71     lv_timer_create(
72         lv_windows_check_display_existence_timer_callback,
73         200,
74         NULL);
75 
76     // Try to ensure the default group exists.
77     {
78         lv_group_t * default_group = lv_group_get_default();
79         if(!default_group) {
80             default_group = lv_group_create();
81             if(default_group) {
82                 lv_group_set_default(default_group);
83             }
84         }
85     }
86 
87     WNDCLASSEXW window_class;
88     lv_memzero(&window_class, sizeof(WNDCLASSEXW));
89     window_class.cbSize = sizeof(WNDCLASSEXW);
90     window_class.style = 0;
91     window_class.lpfnWndProc = lv_windows_window_message_callback;
92     window_class.cbClsExtra = 0;
93     window_class.cbWndExtra = 0;
94     window_class.hInstance = NULL;
95     window_class.hIcon = NULL;
96     window_class.hCursor = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW);
97     window_class.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
98     window_class.lpszMenuName = NULL;
99     window_class.lpszClassName = L"LVGL.Window";
100     window_class.hIconSm = NULL;
101     LV_ASSERT(RegisterClassExW(&window_class));
102 }
103 
lv_windows_get_window_context(HWND window_handle)104 lv_windows_window_context_t * lv_windows_get_window_context(
105     HWND window_handle)
106 {
107     return (lv_windows_window_context_t *)(
108                GetPropW(window_handle, L"LVGL.Window.Context"));
109 }
110 
111 /**********************
112  *   STATIC FUNCTIONS
113  **********************/
114 
lv_windows_tick_count_callback(void)115 static uint32_t lv_windows_tick_count_callback(void)
116 {
117     LARGE_INTEGER Frequency;
118     if(QueryPerformanceFrequency(&Frequency)) {
119         LARGE_INTEGER PerformanceCount;
120         if(QueryPerformanceCounter(&PerformanceCount)) {
121             return (uint32_t)(PerformanceCount.QuadPart * 1000 / Frequency.QuadPart);
122         }
123     }
124 
125     return (uint32_t)GetTickCount64();
126 }
127 
lv_windows_delay_callback(uint32_t ms)128 static void lv_windows_delay_callback(uint32_t ms)
129 {
130     HANDLE timer_handle = CreateWaitableTimerExW(
131                               NULL,
132                               NULL,
133                               CREATE_WAITABLE_TIMER_MANUAL_RESET |
134                               CREATE_WAITABLE_TIMER_HIGH_RESOLUTION,
135                               TIMER_ALL_ACCESS);
136     if(timer_handle) {
137         LARGE_INTEGER due_time;
138         due_time.QuadPart = -((int64_t)ms) * 1000 * 10;
139         SetWaitableTimer(timer_handle, &due_time, 0, NULL, NULL, FALSE);
140         WaitForSingleObject(timer_handle, INFINITE);
141 
142         CloseHandle(timer_handle);
143     }
144 }
145 
lv_windows_check_display_existence_timer_callback(lv_timer_t * timer)146 static void lv_windows_check_display_existence_timer_callback(
147     lv_timer_t * timer)
148 {
149     LV_UNUSED(timer);
150     if(!lv_display_get_next(NULL)) {
151         // Don't use lv_deinit() due to it will cause exception when parallel
152         // rendering is enabled.
153         exit(0);
154     }
155 }
156 
lv_windows_create_frame_buffer(HWND window_handle,LONG width,LONG height,UINT32 ** pixel_buffer,SIZE_T * pixel_buffer_size)157 static HDC lv_windows_create_frame_buffer(
158     HWND window_handle,
159     LONG width,
160     LONG height,
161     UINT32 ** pixel_buffer,
162     SIZE_T * pixel_buffer_size)
163 {
164     HDC frame_buffer_dc_handle = NULL;
165 
166     LV_ASSERT_NULL(pixel_buffer);
167     LV_ASSERT_NULL(pixel_buffer_size);
168 
169     HDC window_dc_handle = GetDC(window_handle);
170     if(window_dc_handle) {
171         frame_buffer_dc_handle = CreateCompatibleDC(window_dc_handle);
172         ReleaseDC(window_handle, window_dc_handle);
173     }
174 
175     if(frame_buffer_dc_handle) {
176 #if (LV_COLOR_DEPTH == 32) || (LV_COLOR_DEPTH == 24)
177         BITMAPINFO bitmap_info = { 0 };
178 #elif (LV_COLOR_DEPTH == 16)
179         typedef struct _BITMAPINFO_16BPP {
180             BITMAPINFOHEADER bmiHeader;
181             DWORD bmiColorMask[3];
182         } BITMAPINFO_16BPP;
183 
184         BITMAPINFO_16BPP bitmap_info = { 0 };
185 #else
186 #error [lv_windows] Unsupported LV_COLOR_DEPTH.
187 #endif
188 
189         bitmap_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
190         bitmap_info.bmiHeader.biWidth = width;
191         bitmap_info.bmiHeader.biHeight = -height;
192         bitmap_info.bmiHeader.biPlanes = 1;
193         bitmap_info.bmiHeader.biBitCount = lv_color_format_get_bpp(
194                                                LV_COLOR_FORMAT_NATIVE);
195 #if (LV_COLOR_DEPTH == 32) || (LV_COLOR_DEPTH == 24)
196         bitmap_info.bmiHeader.biCompression = BI_RGB;
197 #elif (LV_COLOR_DEPTH == 16)
198         bitmap_info.bmiHeader.biCompression = BI_BITFIELDS;
199         bitmap_info.bmiColorMask[0] = 0xF800;
200         bitmap_info.bmiColorMask[1] = 0x07E0;
201         bitmap_info.bmiColorMask[2] = 0x001F;
202 #else
203 #error [lv_windows] Unsupported LV_COLOR_DEPTH.
204 #endif
205 
206         HBITMAP hBitmap = CreateDIBSection(
207                               frame_buffer_dc_handle,
208                               (PBITMAPINFO)(&bitmap_info),
209                               DIB_RGB_COLORS,
210                               (void **)pixel_buffer,
211                               NULL,
212                               0);
213         if(hBitmap) {
214             *pixel_buffer_size = width * height;
215             *pixel_buffer_size *= lv_color_format_get_size(
216                                       LV_COLOR_FORMAT_NATIVE);
217 
218             DeleteObject(SelectObject(frame_buffer_dc_handle, hBitmap));
219             DeleteObject(hBitmap);
220         }
221         else {
222             DeleteDC(frame_buffer_dc_handle);
223             frame_buffer_dc_handle = NULL;
224         }
225     }
226 
227     return frame_buffer_dc_handle;
228 }
229 
lv_windows_display_timer_callback(lv_timer_t * timer)230 static void lv_windows_display_timer_callback(lv_timer_t * timer)
231 {
232     lv_windows_window_context_t * context = lv_timer_get_user_data(timer);
233     LV_ASSERT_NULL(context);
234 
235     if(!context->display_resolution_changed) {
236         return;
237     }
238 
239     lv_display_set_resolution(
240         context->display_device_object,
241         context->requested_display_resolution.x,
242         context->requested_display_resolution.y);
243 
244     int32_t hor_res = lv_display_get_horizontal_resolution(
245                           context->display_device_object);
246     int32_t ver_res = lv_display_get_vertical_resolution(
247                           context->display_device_object);
248 
249     HWND window_handle = lv_windows_get_display_window_handle(
250                              context->display_device_object);
251     if(window_handle) {
252         if(context->display_framebuffer_context_handle) {
253             context->display_framebuffer_base = NULL;
254             context->display_framebuffer_size = 0;
255             DeleteDC(context->display_framebuffer_context_handle);
256             context->display_framebuffer_context_handle = NULL;
257         }
258 
259         context->display_framebuffer_context_handle =
260             lv_windows_create_frame_buffer(
261                 window_handle,
262                 hor_res,
263                 ver_res,
264                 &context->display_framebuffer_base,
265                 &context->display_framebuffer_size);
266         if(context->display_framebuffer_context_handle) {
267             lv_display_set_buffers(
268                 context->display_device_object,
269                 context->display_framebuffer_base,
270                 NULL,
271                 (uint32_t)context->display_framebuffer_size,
272                 LV_DISPLAY_RENDER_MODE_DIRECT);
273         }
274     }
275 
276     context->display_resolution_changed = false;
277     context->requested_display_resolution.x = 0;
278     context->requested_display_resolution.y = 0;
279 }
280 
lv_windows_display_driver_flush_callback(lv_display_t * display,const lv_area_t * area,uint8_t * px_map)281 static void lv_windows_display_driver_flush_callback(
282     lv_display_t * display,
283     const lv_area_t * area,
284     uint8_t * px_map)
285 {
286     LV_UNUSED(area);
287 
288     HWND window_handle = lv_windows_get_display_window_handle(display);
289     if(!window_handle) {
290         lv_display_flush_ready(display);
291         return;
292     }
293 
294     lv_windows_window_context_t * context = lv_windows_get_window_context(
295                                                 window_handle);
296     if(!context) {
297         lv_display_flush_ready(display);
298         return;
299     }
300 
301     if(lv_display_flush_is_last(display)) {
302 #if (LV_COLOR_DEPTH == 32) || \
303     (LV_COLOR_DEPTH == 24) || \
304     (LV_COLOR_DEPTH == 16)
305         UNREFERENCED_PARAMETER(px_map);
306 #else
307 #error [lv_windows] Unsupported LV_COLOR_DEPTH.
308 #endif
309 
310         HDC hdc = GetDC(window_handle);
311         if(hdc) {
312             SetStretchBltMode(hdc, HALFTONE);
313 
314             RECT client_rect;
315             GetClientRect(window_handle, &client_rect);
316 
317             int32_t width = lv_windows_zoom_to_logical(
318                                 client_rect.right - client_rect.left,
319                                 context->zoom_level);
320             int32_t height = lv_windows_zoom_to_logical(
321                                  client_rect.bottom - client_rect.top,
322                                  context->zoom_level);
323             if(context->simulator_mode) {
324                 width = lv_windows_dpi_to_logical(width, context->window_dpi);
325                 height = lv_windows_dpi_to_logical(height, context->window_dpi);
326             }
327 
328             StretchBlt(
329                 hdc,
330                 client_rect.left,
331                 client_rect.top,
332                 client_rect.right - client_rect.left,
333                 client_rect.bottom - client_rect.top,
334                 context->display_framebuffer_context_handle,
335                 0,
336                 0,
337                 width,
338                 height,
339                 SRCCOPY);
340 
341             ReleaseDC(window_handle, hdc);
342         }
343     }
344 
345     lv_display_flush_ready(display);
346 }
347 
lv_windows_get_dpi_for_window(HWND window_handle)348 static UINT lv_windows_get_dpi_for_window(HWND window_handle)
349 {
350     UINT result = (UINT)(-1);
351 
352     HMODULE module_handle = LoadLibraryW(L"SHCore.dll");
353     if(module_handle) {
354         typedef enum MONITOR_DPI_TYPE_PRIVATE {
355             MDT_EFFECTIVE_DPI = 0,
356             MDT_ANGULAR_DPI = 1,
357             MDT_RAW_DPI = 2,
358             MDT_DEFAULT = MDT_EFFECTIVE_DPI
359         } MONITOR_DPI_TYPE_PRIVATE;
360 
361         typedef HRESULT(WINAPI * function_type)(
362             HMONITOR, MONITOR_DPI_TYPE_PRIVATE, UINT *, UINT *);
363 
364         function_type function = (function_type)(
365                                      GetProcAddress(module_handle, "GetDpiForMonitor"));
366         if(function) {
367             HMONITOR MonitorHandle = MonitorFromWindow(
368                                          window_handle,
369                                          MONITOR_DEFAULTTONEAREST);
370 
371             UINT dpiX = 0;
372             UINT dpiY = 0;
373             if(SUCCEEDED(function(
374                              MonitorHandle,
375                              MDT_EFFECTIVE_DPI,
376                              &dpiX,
377                              &dpiY))) {
378                 result = dpiX;
379             }
380         }
381 
382         FreeLibrary(module_handle);
383     }
384 
385     if(result == (UINT)(-1)) {
386         HDC hWindowDC = GetDC(window_handle);
387         if(hWindowDC) {
388             result = GetDeviceCaps(hWindowDC, LOGPIXELSX);
389             ReleaseDC(window_handle, hWindowDC);
390         }
391     }
392 
393     if(result == (UINT)(-1)) {
394         result = USER_DEFAULT_SCREEN_DPI;
395     }
396 
397     return result;
398 }
399 
lv_windows_register_touch_window(HWND window_handle,ULONG flags)400 static BOOL lv_windows_register_touch_window(
401     HWND window_handle,
402     ULONG flags)
403 {
404     HMODULE module_handle = GetModuleHandleW(L"user32.dll");
405     if(!module_handle) {
406         return FALSE;
407     }
408 
409     typedef BOOL(WINAPI * function_type)(HWND, ULONG);
410 
411     function_type function = (function_type)(
412                                  GetProcAddress(module_handle, "RegisterTouchWindow"));
413     if(!function) {
414         return FALSE;
415     }
416 
417     return function(window_handle, flags);
418 }
419 
lv_windows_enable_child_window_dpi_message(HWND WindowHandle)420 static BOOL lv_windows_enable_child_window_dpi_message(
421     HWND WindowHandle)
422 {
423     // The private Per-Monitor DPI Awareness support extension is Windows 10
424     // only. We don't need the private Per-Monitor DPI Awareness support
425     // extension if the Per-Monitor (V2) DPI Awareness exists.
426     OSVERSIONINFOEXW os_version_info_ex = { 0 };
427     os_version_info_ex.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
428     os_version_info_ex.dwMajorVersion = 10;
429     os_version_info_ex.dwMinorVersion = 0;
430     os_version_info_ex.dwBuildNumber = 14986;
431     if(!VerifyVersionInfoW(
432            &os_version_info_ex,
433            VER_MAJORVERSION | VER_MINORVERSION | VER_BUILDNUMBER,
434            VerSetConditionMask(
435                VerSetConditionMask(
436                    VerSetConditionMask(
437                        0,
438                        VER_MAJORVERSION,
439                        VER_GREATER_EQUAL),
440                    VER_MINORVERSION,
441                    VER_GREATER_EQUAL),
442                VER_BUILDNUMBER,
443                VER_LESS))) {
444         return FALSE;
445     }
446 
447     HMODULE module_handle = GetModuleHandleW(L"user32.dll");
448     if(!module_handle) {
449         return FALSE;
450     }
451 
452     typedef BOOL(WINAPI * function_type)(HWND, BOOL);
453 
454     function_type function = (function_type)(
455                                  GetProcAddress(module_handle, "EnableChildWindowDpiMessage"));
456     if(!function) {
457         return FALSE;
458     }
459 
460     return function(WindowHandle, TRUE);
461 }
462 
lv_windows_window_message_callback_nolock(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam,LRESULT * plResult)463 static bool lv_windows_window_message_callback_nolock(
464     HWND hWnd,
465     UINT uMsg,
466     WPARAM wParam,
467     LPARAM lParam,
468     LRESULT * plResult)
469 {
470     switch(uMsg) {
471         case WM_CREATE: {
472                 // Note: Return -1 directly because WM_DESTROY message will be sent
473                 // when destroy the window automatically. We free the resource when
474                 // processing the WM_DESTROY message of this window.
475 
476                 lv_windows_create_display_data_t * data =
477                     (lv_windows_create_display_data_t *)(
478                         ((LPCREATESTRUCTW)(lParam))->lpCreateParams);
479                 if(!data) {
480                     return -1;
481                 }
482 
483                 lv_windows_window_context_t * context =
484                     (lv_windows_window_context_t *)(HeapAlloc(
485                                                         GetProcessHeap(),
486                                                         HEAP_ZERO_MEMORY,
487                                                         sizeof(lv_windows_window_context_t)));
488                 if(!context) {
489                     return -1;
490                 }
491 
492                 if(!SetPropW(hWnd, L"LVGL.Window.Context", (HANDLE)(context))) {
493                     return -1;
494                 }
495 
496                 context->window_dpi = lv_windows_get_dpi_for_window(hWnd);
497                 context->zoom_level = data->zoom_level;
498                 context->allow_dpi_override = data->allow_dpi_override;
499                 context->simulator_mode = data->simulator_mode;
500 
501                 context->display_timer_object = lv_timer_create(
502                                                     lv_windows_display_timer_callback,
503                                                     LV_DEF_REFR_PERIOD,
504                                                     context);
505 
506                 context->display_resolution_changed = false;
507                 context->requested_display_resolution.x = 0;
508                 context->requested_display_resolution.y = 0;
509 
510                 context->display_device_object = lv_display_create(0, 0);
511                 if(!context->display_device_object) {
512                     return -1;
513                 }
514                 RECT request_content_size;
515                 GetWindowRect(hWnd, &request_content_size);
516                 lv_display_set_resolution(
517                     context->display_device_object,
518                     request_content_size.right - request_content_size.left,
519                     request_content_size.bottom - request_content_size.top);
520                 lv_display_set_flush_cb(
521                     context->display_device_object,
522                     lv_windows_display_driver_flush_callback);
523                 lv_display_set_driver_data(
524                     context->display_device_object,
525                     hWnd);
526                 if(!context->allow_dpi_override) {
527                     lv_display_set_dpi(
528                         context->display_device_object,
529                         context->window_dpi);
530                 }
531 
532                 if(context->simulator_mode) {
533                     context->display_resolution_changed = true;
534                     context->requested_display_resolution.x =
535                         lv_display_get_horizontal_resolution(
536                             context->display_device_object);
537                     context->requested_display_resolution.y =
538                         lv_display_get_vertical_resolution(
539                             context->display_device_object);
540                 }
541 
542                 lv_windows_register_touch_window(hWnd, 0);
543 
544                 lv_windows_enable_child_window_dpi_message(hWnd);
545 
546                 break;
547             }
548         case WM_SIZE: {
549                 if(wParam != SIZE_MINIMIZED) {
550                     lv_windows_window_context_t * context = (lv_windows_window_context_t *)(
551                                                                 lv_windows_get_window_context(hWnd));
552                     if(context) {
553                         if(!context->simulator_mode) {
554                             context->display_resolution_changed = true;
555                             context->requested_display_resolution.x = LOWORD(lParam);
556                             context->requested_display_resolution.y = HIWORD(lParam);
557                         }
558                         else {
559                             int32_t window_width = lv_windows_dpi_to_physical(
560                                                        lv_windows_zoom_to_physical(
561                                                            lv_display_get_horizontal_resolution(
562                                                                context->display_device_object),
563                                                            context->zoom_level),
564                                                        context->window_dpi);
565                             int32_t window_height = lv_windows_dpi_to_physical(
566                                                         lv_windows_zoom_to_physical(
567                                                             lv_display_get_vertical_resolution(
568                                                                 context->display_device_object),
569                                                             context->zoom_level),
570                                                         context->window_dpi);
571 
572                             RECT window_rect;
573                             GetWindowRect(hWnd, &window_rect);
574 
575                             RECT client_rect;
576                             GetClientRect(hWnd, &client_rect);
577 
578                             int32_t original_window_width =
579                                 window_rect.right - window_rect.left;
580                             int32_t original_window_height =
581                                 window_rect.bottom - window_rect.top;
582 
583                             int32_t original_client_width =
584                                 client_rect.right - client_rect.left;
585                             int32_t original_client_height =
586                                 client_rect.bottom - client_rect.top;
587 
588                             int32_t reserved_width =
589                                 original_window_width - original_client_width;
590                             int32_t reserved_height =
591                                 original_window_height - original_client_height;
592 
593                             SetWindowPos(
594                                 hWnd,
595                                 NULL,
596                                 0,
597                                 0,
598                                 reserved_width + window_width,
599                                 reserved_height + window_height,
600                                 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE);
601                         }
602                     }
603                 }
604                 break;
605             }
606         case WM_DPICHANGED: {
607                 lv_windows_window_context_t * context = (lv_windows_window_context_t *)(
608                                                             lv_windows_get_window_context(hWnd));
609                 if(context) {
610                     context->window_dpi = HIWORD(wParam);
611 
612                     if(!context->allow_dpi_override) {
613                         lv_display_set_dpi(
614                             context->display_device_object,
615                             context->window_dpi);
616                     }
617 
618                     LPRECT suggested_rect = (LPRECT)lParam;
619 
620                     SetWindowPos(
621                         hWnd,
622                         NULL,
623                         suggested_rect->left,
624                         suggested_rect->top,
625                         suggested_rect->right,
626                         suggested_rect->bottom,
627                         SWP_NOZORDER | SWP_NOACTIVATE);
628                 }
629 
630                 break;
631             }
632         case WM_ERASEBKGND: {
633                 return TRUE;
634             }
635         case WM_DESTROY: {
636                 lv_windows_window_context_t * context = (lv_windows_window_context_t *)(
637                                                             RemovePropW(hWnd, L"LVGL.Window.Context"));
638                 if(context) {
639                     lv_display_t * display_device_object =
640                         context->display_device_object;
641                     context->display_device_object = NULL;
642                     lv_display_delete(display_device_object);
643                     DeleteDC(context->display_framebuffer_context_handle);
644 
645                     lv_timer_delete(context->display_timer_object);
646 
647                     HeapFree(GetProcessHeap(), 0, context);
648                 }
649 
650                 PostQuitMessage(0);
651 
652                 break;
653             }
654         default: {
655                 lv_windows_window_context_t * context = (lv_windows_window_context_t *)(
656                                                             lv_windows_get_window_context(hWnd));
657                 if(context) {
658                     if(context->pointer.indev &&
659                        lv_windows_pointer_device_window_message_handler(
660                            hWnd,
661                            uMsg,
662                            wParam,
663                            lParam,
664                            plResult)) {
665                         // Handled
666                         return true;
667                     }
668                     else if(context->keypad.indev &&
669                             lv_windows_keypad_device_window_message_handler(
670                                 hWnd,
671                                 uMsg,
672                                 wParam,
673                                 lParam,
674                                 plResult)) {
675                         // Handled
676                         return true;
677                     }
678                     else if(context->encoder.indev &&
679                             lv_windows_encoder_device_window_message_handler(
680                                 hWnd,
681                                 uMsg,
682                                 wParam,
683                                 lParam,
684                                 plResult)) {
685                         // Handled
686                         return true;
687                     }
688                 }
689 
690                 // Not Handled
691                 return false;
692             }
693     }
694 
695     // Handled
696     *plResult = 0;
697     return true;
698 }
699 
lv_windows_window_message_callback(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)700 static LRESULT CALLBACK lv_windows_window_message_callback(
701     HWND hWnd,
702     UINT uMsg,
703     WPARAM wParam,
704     LPARAM lParam)
705 {
706     lv_lock();
707 
708     LRESULT lResult = 0;
709     bool Handled = lv_windows_window_message_callback_nolock(
710                        hWnd,
711                        uMsg,
712                        wParam,
713                        lParam,
714                        &lResult);
715 
716     lv_unlock();
717 
718     return Handled ? lResult : DefWindowProcW(hWnd, uMsg, wParam, lParam);
719 }
720 
721 #endif // LV_USE_WINDOWS
722