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