/** * @file win32drv.c * */ /********************* * INCLUDES *********************/ #include "win32drv.h" #if USE_WIN32DRV #include #include #include #include #include /********************* * DEFINES *********************/ #define WINDOW_EX_STYLE \ WS_EX_CLIENTEDGE #define WINDOW_STYLE \ (WS_OVERLAPPEDWINDOW & ~(WS_SIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME)) #ifndef WIN32DRV_MONITOR_ZOOM #define WIN32DRV_MONITOR_ZOOM 1 #endif #ifndef USER_DEFAULT_SCREEN_DPI #define USER_DEFAULT_SCREEN_DPI 96 #endif /********************** * TYPEDEFS **********************/ typedef struct _WINDOW_THREAD_PARAMETER { HANDLE window_mutex; HINSTANCE instance_handle; HICON icon_handle; lv_coord_t hor_res; lv_coord_t ver_res; int show_window_mode; } WINDOW_THREAD_PARAMETER, * PWINDOW_THREAD_PARAMETER; /********************** * STATIC PROTOTYPES **********************/ /** * @brief Creates a B8G8R8A8 frame buffer. * @param WindowHandle A handle to the window for the creation of the frame * buffer. If this value is NULL, the entire screen will be * referenced. * @param Width The width of the frame buffer. * @param Height The height of the frame buffer. * @param PixelBuffer The raw pixel buffer of the frame buffer you created. * @param PixelBufferSize The size of the frame buffer you created. * @return If the function succeeds, the return value is a handle to the device * context (DC) for the frame buffer. If the function fails, the return * value is NULL, and PixelBuffer parameter is NULL. */ static HDC lv_win32_create_frame_buffer( _In_opt_ HWND WindowHandle, _In_ LONG Width, _In_ LONG Height, _Out_ UINT32** PixelBuffer, _Out_ SIZE_T* PixelBufferSize); /** * @brief Enables WM_DPICHANGED message for child window for the associated * window. * @param WindowHandle The window you want to enable WM_DPICHANGED message for * child window. * @return If the function succeeds, the return value is non-zero. If the * function fails, the return value is zero. * @remarks You need to use this function in Windows 10 Threshold 1 or Windows * 10 Threshold 2. */ static BOOL lv_win32_enable_child_window_dpi_message( _In_ HWND WindowHandle); /** * @brief Registers a window as being touch-capable. * @param hWnd The handle of the window being registered. * @param ulFlags A set of bit flags that specify optional modifications. * @return If the function succeeds, the return value is nonzero. If the * function fails, the return value is zero. * @remark For more information, see RegisterTouchWindow. */ static BOOL lv_win32_register_touch_window( HWND hWnd, ULONG ulFlags); /** * @brief Retrieves detailed information about touch inputs associated with a * particular touch input handle. * @param hTouchInput The touch input handle received in the LPARAM of a touch * message. * @param cInputs The number of structures in the pInputs array. * @param pInputs A pointer to an array of TOUCHINPUT structures to receive * information about the touch points associated with the * specified touch input handle. * @param cbSize The size, in bytes, of a single TOUCHINPUT structure. * @return If the function succeeds, the return value is nonzero. If the * function fails, the return value is zero. * @remark For more information, see GetTouchInputInfo. */ static BOOL lv_win32_get_touch_input_info( HTOUCHINPUT hTouchInput, UINT cInputs, PTOUCHINPUT pInputs, int cbSize); /** * @brief Closes a touch input handle, frees process memory associated with it, and invalidates the handle. * @param hTouchInput The touch input handle received in the LPARAM of a touch * message. * @return If the function succeeds, the return value is nonzero. If the * function fails, the return value is zero. * @remark For more information, see CloseTouchInputHandle. */ static BOOL lv_win32_close_touch_input_handle( HTOUCHINPUT hTouchInput); /** * @brief Returns the dots per inch (dpi) value for the associated window. * @param WindowHandle The window you want to get information about. * @return The DPI for the window. */ static UINT lv_win32_get_dpi_for_window( _In_ HWND WindowHandle); static void lv_win32_display_driver_flush_callback( lv_disp_drv_t* disp_drv, const lv_area_t* area, lv_color_t* color_p); static void lv_win32_pointer_driver_read_callback( lv_indev_drv_t* indev_drv, lv_indev_data_t* data); static void lv_win32_keypad_driver_read_callback( lv_indev_drv_t* indev_drv, lv_indev_data_t* data); static void lv_win32_encoder_driver_read_callback( lv_indev_drv_t* indev_drv, lv_indev_data_t* data); static lv_win32_window_context_t* lv_win32_get_display_context( lv_disp_t* display); static LRESULT CALLBACK lv_win32_window_message_callback( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); static unsigned int __stdcall lv_win32_window_thread_entrypoint( void* raw_parameter); static void lv_win32_push_key_to_keyboard_queue( lv_win32_window_context_t* context, uint32_t key, lv_indev_state_t state) { lv_win32_keyboard_queue_item_t* current = (lv_win32_keyboard_queue_item_t*)(_aligned_malloc( sizeof(lv_win32_keyboard_queue_item_t), MEMORY_ALLOCATION_ALIGNMENT)); if (current) { current->key = key; current->state = state; InterlockedPushEntrySList( context->keyboard_queue, ¤t->ItemEntry); } } /********************** * GLOBAL VARIABLES **********************/ EXTERN_C bool lv_win32_quit_signal = false; EXTERN_C lv_indev_t* lv_win32_pointer_device_object = NULL; EXTERN_C lv_indev_t* lv_win32_keypad_device_object = NULL; EXTERN_C lv_indev_t* lv_win32_encoder_device_object = NULL; /********************** * STATIC VARIABLES **********************/ static HWND g_window_handle = NULL; /********************** * MACROS **********************/ /********************** * GLOBAL FUNCTIONS **********************/ EXTERN_C void lv_win32_add_all_input_devices_to_group( lv_group_t* group) { if (!group) { LV_LOG_WARN( "The group object is NULL. Get the default group object instead."); group = lv_group_get_default(); if (!group) { LV_LOG_WARN( "The default group object is NULL. Create a new group object " "and set it to default instead."); group = lv_group_create(); if (group) { lv_group_set_default(group); } } } LV_ASSERT_MSG(group, "Cannot obtain an available group object."); lv_indev_set_group(lv_win32_pointer_device_object, group); lv_indev_set_group(lv_win32_keypad_device_object, group); lv_indev_set_group(lv_win32_encoder_device_object, group); } EXTERN_C lv_win32_window_context_t* lv_win32_get_window_context( HWND window_handle) { return (lv_win32_window_context_t*)( GetPropW(window_handle, L"LVGL.SimulatorWindow.WindowContext")); } EXTERN_C bool lv_win32_init_window_class() { WNDCLASSEXW window_class; window_class.cbSize = sizeof(WNDCLASSEXW); window_class.style = 0; window_class.lpfnWndProc = lv_win32_window_message_callback; window_class.cbClsExtra = 0; window_class.cbWndExtra = 0; window_class.hInstance = NULL; window_class.hIcon = NULL; window_class.hCursor = LoadCursorW(NULL, IDC_ARROW); window_class.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); window_class.lpszMenuName = NULL; window_class.lpszClassName = LVGL_SIMULATOR_WINDOW_CLASS; window_class.hIconSm = NULL; return RegisterClassExW(&window_class); } EXTERN_C HWND 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) { HWND display_window_handle = CreateWindowExW( WINDOW_EX_STYLE, LVGL_SIMULATOR_WINDOW_CLASS, window_title, WINDOW_STYLE, CW_USEDEFAULT, 0, hor_res, ver_res, NULL, NULL, instance_handle, NULL); if (display_window_handle) { SendMessageW( display_window_handle, WM_SETICON, TRUE, (LPARAM)icon_handle); SendMessageW( display_window_handle, WM_SETICON, FALSE, (LPARAM)icon_handle); ShowWindow(display_window_handle, show_window_mode); UpdateWindow(display_window_handle); } return display_window_handle; } EXTERN_C bool lv_win32_init( HINSTANCE instance_handle, int show_window_mode, lv_coord_t hor_res, lv_coord_t ver_res, HICON icon_handle) { if (!lv_win32_init_window_class()) { return false; } PWINDOW_THREAD_PARAMETER parameter = (PWINDOW_THREAD_PARAMETER)malloc(sizeof(WINDOW_THREAD_PARAMETER)); parameter->window_mutex = CreateEventExW(NULL, NULL, 0, EVENT_ALL_ACCESS); parameter->instance_handle = instance_handle; parameter->icon_handle = icon_handle; parameter->hor_res = hor_res; parameter->ver_res = ver_res; parameter->show_window_mode = show_window_mode; _beginthreadex( NULL, 0, lv_win32_window_thread_entrypoint, parameter, 0, NULL); WaitForSingleObjectEx(parameter->window_mutex, INFINITE, FALSE); lv_win32_window_context_t* context = (lv_win32_window_context_t*)( lv_win32_get_window_context(g_window_handle)); if (!context) { return false; } lv_win32_pointer_device_object = context->mouse_device_object; lv_win32_keypad_device_object = context->keyboard_device_object; lv_win32_encoder_device_object = context->mousewheel_device_object; return true; } /********************** * STATIC FUNCTIONS **********************/ static HDC lv_win32_create_frame_buffer( HWND WindowHandle, LONG Width, LONG Height, UINT32** PixelBuffer, SIZE_T* PixelBufferSize) { HDC hFrameBufferDC = NULL; if (PixelBuffer && PixelBufferSize) { HDC hWindowDC = GetDC(WindowHandle); if (hWindowDC) { hFrameBufferDC = CreateCompatibleDC(hWindowDC); ReleaseDC(WindowHandle, hWindowDC); } if (hFrameBufferDC) { #if LV_COLOR_DEPTH == 32 BITMAPINFO BitmapInfo = { 0 }; #elif LV_COLOR_DEPTH == 16 typedef struct _BITMAPINFO_16BPP { BITMAPINFOHEADER bmiHeader; DWORD bmiColorMask[3]; } BITMAPINFO_16BPP, *PBITMAPINFO_16BPP; BITMAPINFO_16BPP BitmapInfo = { 0 }; #elif LV_COLOR_DEPTH == 8 typedef struct _BITMAPINFO_8BPP { BITMAPINFOHEADER bmiHeader; RGBQUAD bmiColors[256]; } BITMAPINFO_8BPP, *PBITMAPINFO_8BPP; BITMAPINFO_8BPP BitmapInfo = { 0 }; #elif LV_COLOR_DEPTH == 1 typedef struct _BITMAPINFO_1BPP { BITMAPINFOHEADER bmiHeader; RGBQUAD bmiColors[2]; } BITMAPINFO_1BPP, *PBITMAPINFO_1BPP; BITMAPINFO_1BPP BitmapInfo = { 0 }; #else BITMAPINFO BitmapInfo = { 0 }; #endif BitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); BitmapInfo.bmiHeader.biWidth = Width; BitmapInfo.bmiHeader.biHeight = -Height; BitmapInfo.bmiHeader.biPlanes = 1; #if LV_COLOR_DEPTH == 32 BitmapInfo.bmiHeader.biBitCount = 32; BitmapInfo.bmiHeader.biCompression = BI_RGB; #elif LV_COLOR_DEPTH == 16 BitmapInfo.bmiHeader.biBitCount = 16; BitmapInfo.bmiHeader.biCompression = BI_BITFIELDS; BitmapInfo.bmiColorMask[0] = 0xF800; BitmapInfo.bmiColorMask[1] = 0x07E0; BitmapInfo.bmiColorMask[2] = 0x001F; #elif LV_COLOR_DEPTH == 8 BitmapInfo.bmiHeader.biBitCount = 8; BitmapInfo.bmiHeader.biCompression = BI_RGB; for (size_t i = 0; i < 256; ++i) { lv_color8_t color; color.full = i; BitmapInfo.bmiColors[i].rgbRed = LV_COLOR_GET_R(color) * 36; BitmapInfo.bmiColors[i].rgbGreen = LV_COLOR_GET_G(color) * 36; BitmapInfo.bmiColors[i].rgbBlue = LV_COLOR_GET_B(color) * 85; BitmapInfo.bmiColors[i].rgbReserved = 0xFF; } #elif LV_COLOR_DEPTH == 1 BitmapInfo.bmiHeader.biBitCount = 8; BitmapInfo.bmiHeader.biCompression = BI_RGB; BitmapInfo.bmiHeader.biClrUsed = 2; BitmapInfo.bmiHeader.biClrImportant = 2; BitmapInfo.bmiColors[0].rgbRed = 0x00; BitmapInfo.bmiColors[0].rgbGreen = 0x00; BitmapInfo.bmiColors[0].rgbBlue = 0x00; BitmapInfo.bmiColors[0].rgbReserved = 0xFF; BitmapInfo.bmiColors[1].rgbRed = 0xFF; BitmapInfo.bmiColors[1].rgbGreen = 0xFF; BitmapInfo.bmiColors[1].rgbBlue = 0xFF; BitmapInfo.bmiColors[1].rgbReserved = 0xFF; #else BitmapInfo.bmiHeader.biBitCount = 32; BitmapInfo.bmiHeader.biCompression = BI_RGB; #endif HBITMAP hBitmap = CreateDIBSection( hFrameBufferDC, (PBITMAPINFO)(&BitmapInfo), DIB_RGB_COLORS, (void**)PixelBuffer, NULL, 0); if (hBitmap) { #if LV_COLOR_DEPTH == 32 *PixelBufferSize = Width * Height * sizeof(UINT32); #elif LV_COLOR_DEPTH == 16 *PixelBufferSize = Width * Height * sizeof(UINT16); #elif LV_COLOR_DEPTH == 8 *PixelBufferSize = Width * Height * sizeof(UINT8); #elif LV_COLOR_DEPTH == 1 *PixelBufferSize = Width * Height * sizeof(UINT8); #else *PixelBufferSize = Width * Height * sizeof(UINT32); #endif DeleteObject(SelectObject(hFrameBufferDC, hBitmap)); DeleteObject(hBitmap); } else { DeleteDC(hFrameBufferDC); hFrameBufferDC = NULL; } } } return hFrameBufferDC; } static BOOL lv_win32_enable_child_window_dpi_message( HWND WindowHandle) { // This hack is only for Windows 10 TH1/TH2 only. // We don't need this hack if the Per Monitor Aware V2 is existed. OSVERSIONINFOEXW OSVersionInfoEx = { 0 }; OSVersionInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); OSVersionInfoEx.dwMajorVersion = 10; OSVersionInfoEx.dwMinorVersion = 0; OSVersionInfoEx.dwBuildNumber = 14393; if (!VerifyVersionInfoW( &OSVersionInfoEx, VER_MAJORVERSION | VER_MINORVERSION | VER_BUILDNUMBER, VerSetConditionMask( VerSetConditionMask( VerSetConditionMask( 0, VER_MAJORVERSION, VER_GREATER_EQUAL), VER_MINORVERSION, VER_GREATER_EQUAL), VER_BUILDNUMBER, VER_LESS))) { return FALSE; } HMODULE ModuleHandle = GetModuleHandleW(L"user32.dll"); if (!ModuleHandle) { return FALSE; } typedef BOOL(WINAPI* FunctionType)(HWND, BOOL); FunctionType pFunction = (FunctionType)( GetProcAddress(ModuleHandle, "EnableChildWindowDpiMessage")); if (!pFunction) { return FALSE; } return pFunction(WindowHandle, TRUE); } static BOOL lv_win32_register_touch_window( HWND hWnd, ULONG ulFlags) { HMODULE ModuleHandle = GetModuleHandleW(L"user32.dll"); if (!ModuleHandle) { return FALSE; } typedef BOOL(WINAPI* FunctionType)(HWND, ULONG); FunctionType pFunction = (FunctionType)( GetProcAddress(ModuleHandle, "RegisterTouchWindow")); if (!pFunction) { return FALSE; } return pFunction(hWnd, ulFlags); } static BOOL lv_win32_get_touch_input_info( HTOUCHINPUT hTouchInput, UINT cInputs, PTOUCHINPUT pInputs, int cbSize) { HMODULE ModuleHandle = GetModuleHandleW(L"user32.dll"); if (!ModuleHandle) { return FALSE; } typedef BOOL(WINAPI* FunctionType)(HTOUCHINPUT, UINT, PTOUCHINPUT, int); FunctionType pFunction = (FunctionType)( GetProcAddress(ModuleHandle, "GetTouchInputInfo")); if (!pFunction) { return FALSE; } return pFunction(hTouchInput, cInputs, pInputs, cbSize); } static BOOL lv_win32_close_touch_input_handle( HTOUCHINPUT hTouchInput) { HMODULE ModuleHandle = GetModuleHandleW(L"user32.dll"); if (!ModuleHandle) { return FALSE; } typedef BOOL(WINAPI* FunctionType)(HTOUCHINPUT); FunctionType pFunction = (FunctionType)( GetProcAddress(ModuleHandle, "CloseTouchInputHandle")); if (!pFunction) { return FALSE; } return pFunction(hTouchInput); } static UINT lv_win32_get_dpi_for_window( _In_ HWND WindowHandle) { UINT Result = (UINT)(-1); HMODULE ModuleHandle = LoadLibraryW(L"SHCore.dll"); if (ModuleHandle) { typedef enum MONITOR_DPI_TYPE_PRIVATE { MDT_EFFECTIVE_DPI = 0, MDT_ANGULAR_DPI = 1, MDT_RAW_DPI = 2, MDT_DEFAULT = MDT_EFFECTIVE_DPI } MONITOR_DPI_TYPE_PRIVATE; typedef HRESULT(WINAPI* FunctionType)( HMONITOR, MONITOR_DPI_TYPE_PRIVATE, UINT*, UINT*); FunctionType pFunction = (FunctionType)( GetProcAddress(ModuleHandle, "GetDpiForMonitor")); if (pFunction) { HMONITOR MonitorHandle = MonitorFromWindow( WindowHandle, MONITOR_DEFAULTTONEAREST); UINT dpiX = 0; UINT dpiY = 0; if (SUCCEEDED(pFunction( MonitorHandle, MDT_EFFECTIVE_DPI, &dpiX, &dpiY))) { Result = dpiX; } } FreeLibrary(ModuleHandle); } if (Result == (UINT)(-1)) { HDC hWindowDC = GetDC(WindowHandle); if (hWindowDC) { Result = GetDeviceCaps(hWindowDC, LOGPIXELSX); ReleaseDC(WindowHandle, hWindowDC); } } if (Result == (UINT)(-1)) { Result = USER_DEFAULT_SCREEN_DPI; } return Result; } static void lv_win32_display_driver_flush_callback( lv_disp_drv_t* disp_drv, const lv_area_t* area, lv_color_t* color_p) { lv_win32_window_context_t* context = (lv_win32_window_context_t*)( lv_win32_get_window_context((HWND)disp_drv->user_data)); if (context) { if (lv_disp_flush_is_last(disp_drv)) { #if (LV_COLOR_DEPTH == 32) || \ (LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP == 0) || \ (LV_COLOR_DEPTH == 8) || \ (LV_COLOR_DEPTH == 1) UNREFERENCED_PARAMETER(color_p); #elif (LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP != 0) SIZE_T count = context->display_framebuffer_size / sizeof(UINT16); PUINT16 source = (PUINT16)color_p; PUINT16 destination = (PUINT16)context->display_framebuffer_base; for (SIZE_T i = 0; i < count; ++i) { UINT16 current = *source; *destination = (LOBYTE(current) << 8) | HIBYTE(current); ++source; ++destination; } #else uint32_t* destination = context->display_framebuffer_base; for (int y = area->y1; y <= area->y2; ++y) { for (int x = area->x1; x <= area->x2; ++x) { destination[y * disp_drv->hor_res + x] = lv_color_to32(*color_p); color_p++; } } #endif InvalidateRect(disp_drv->user_data, NULL, FALSE); } } lv_disp_flush_ready(disp_drv); } static void lv_win32_pointer_driver_read_callback( lv_indev_drv_t* indev_drv, lv_indev_data_t* data) { lv_win32_window_context_t* context = (lv_win32_window_context_t*)( lv_win32_get_display_context(indev_drv->disp)); if (!context) { return; } data->state = context->mouse_state; data->point = context->mouse_point; } static void lv_win32_keypad_driver_read_callback( lv_indev_drv_t* indev_drv, lv_indev_data_t* data) { lv_win32_window_context_t* context = (lv_win32_window_context_t*)( lv_win32_get_display_context(indev_drv->disp)); if (!context) { return; } EnterCriticalSection(&context->keyboard_mutex); lv_win32_keyboard_queue_item_t* current = (lv_win32_keyboard_queue_item_t*)(InterlockedPopEntrySList( context->keyboard_queue)); if (current) { data->key = current->key; data->state = current->state; _aligned_free(current); data->continue_reading = true; } LeaveCriticalSection(&context->keyboard_mutex); } static void lv_win32_encoder_driver_read_callback( lv_indev_drv_t* indev_drv, lv_indev_data_t* data) { lv_win32_window_context_t* context = (lv_win32_window_context_t*)( lv_win32_get_display_context(indev_drv->disp)); if (!context) { return; } data->state = context->mousewheel_state; data->enc_diff = context->mousewheel_enc_diff; context->mousewheel_enc_diff = 0; } static lv_win32_window_context_t* lv_win32_get_display_context( lv_disp_t* display) { if (display) { return lv_win32_get_window_context((HWND)display->driver->user_data); } return NULL; } static LRESULT CALLBACK lv_win32_window_message_callback( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_CREATE: { // Note: Return -1 directly because WM_DESTROY message will be sent // when destroy the window automatically. We free the resource when // processing the WM_DESTROY message of this window. lv_win32_window_context_t* context = (lv_win32_window_context_t*)( malloc(sizeof(lv_win32_window_context_t))); if (!context) { return -1; } RECT request_content_size; GetWindowRect(hWnd, &request_content_size); context->display_hor_res = request_content_size.right - request_content_size.left; context->display_ver_res = request_content_size.bottom - request_content_size.top; context->display_dpi = lv_win32_get_dpi_for_window(hWnd); context->display_framebuffer_context_handle = lv_win32_create_frame_buffer( hWnd, context->display_hor_res, context->display_ver_res, &context->display_framebuffer_base, &context->display_framebuffer_size); #if (LV_COLOR_DEPTH == 32) || \ (LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP == 0) || \ (LV_COLOR_DEPTH == 8) || \ (LV_COLOR_DEPTH == 1) lv_disp_draw_buf_init( &context->display_buffer, (lv_color_t*)context->display_framebuffer_base, NULL, context->display_hor_res * context->display_ver_res); #else size_t draw_buffer_size = sizeof(lv_color_t); draw_buffer_size *= context->display_hor_res; draw_buffer_size *= context->display_ver_res; lv_disp_draw_buf_init( &context->display_buffer, (lv_color_t*)malloc(draw_buffer_size), NULL, context->display_hor_res * context->display_ver_res); #endif lv_disp_drv_init(&context->display_driver); context->display_driver.hor_res = context->display_hor_res; context->display_driver.ver_res = context->display_ver_res; context->display_driver.flush_cb = lv_win32_display_driver_flush_callback; context->display_driver.draw_buf = &context->display_buffer; context->display_driver.direct_mode = 1; context->display_driver.user_data = hWnd; context->display_device_object = lv_disp_drv_register(&context->display_driver); if (!context->display_device_object) { return -1; } context->mouse_state = LV_INDEV_STATE_REL; context->mouse_point.x = 0; context->mouse_point.y = 0; lv_indev_drv_init(&context->mouse_driver); context->mouse_driver.type = LV_INDEV_TYPE_POINTER; context->mouse_driver.disp = context->display_device_object; context->mouse_driver.read_cb = lv_win32_pointer_driver_read_callback; context->mouse_device_object = lv_indev_drv_register(&context->mouse_driver); if (!context->mouse_device_object) { return -1; } context->mousewheel_state = LV_INDEV_STATE_REL; context->mousewheel_enc_diff = 0; lv_indev_drv_init(&context->mousewheel_driver); context->mousewheel_driver.type = LV_INDEV_TYPE_ENCODER; context->mousewheel_driver.disp = context->display_device_object; context->mousewheel_driver.read_cb = lv_win32_encoder_driver_read_callback; context->mousewheel_device_object = lv_indev_drv_register(&context->mousewheel_driver); if (!context->mousewheel_device_object) { return -1; } InitializeCriticalSection(&context->keyboard_mutex); context->keyboard_queue = _aligned_malloc( sizeof(SLIST_HEADER), MEMORY_ALLOCATION_ALIGNMENT); if (!context->keyboard_queue) { return -1; } InitializeSListHead(context->keyboard_queue); context->keyboard_utf16_high_surrogate = 0; context->keyboard_utf16_low_surrogate = 0; lv_indev_drv_init(&context->keyboard_driver); context->keyboard_driver.type = LV_INDEV_TYPE_KEYPAD; context->keyboard_driver.disp = context->display_device_object; context->keyboard_driver.read_cb = lv_win32_keypad_driver_read_callback; context->keyboard_device_object = lv_indev_drv_register(&context->keyboard_driver); if (!context->keyboard_device_object) { return -1; } if (!SetPropW( hWnd, L"LVGL.SimulatorWindow.WindowContext", (HANDLE)(context))) { return -1; } RECT calculated_window_size; calculated_window_size.left = 0; calculated_window_size.right = MulDiv( context->display_hor_res * WIN32DRV_MONITOR_ZOOM, context->display_dpi, USER_DEFAULT_SCREEN_DPI); calculated_window_size.top = 0; calculated_window_size.bottom = MulDiv( context->display_ver_res * WIN32DRV_MONITOR_ZOOM, context->display_dpi, USER_DEFAULT_SCREEN_DPI); AdjustWindowRectEx( &calculated_window_size, WINDOW_STYLE, FALSE, WINDOW_EX_STYLE); OffsetRect( &calculated_window_size, -calculated_window_size.left, -calculated_window_size.top); SetWindowPos( hWnd, NULL, 0, 0, calculated_window_size.right, calculated_window_size.bottom, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE); lv_win32_register_touch_window(hWnd, 0); lv_win32_enable_child_window_dpi_message(hWnd); break; } case WM_MOUSEMOVE: case WM_LBUTTONDOWN: case WM_LBUTTONUP: case WM_MBUTTONDOWN: case WM_MBUTTONUP: { lv_win32_window_context_t* context = (lv_win32_window_context_t*)( lv_win32_get_window_context(hWnd)); if (!context) { return 0; } context->mouse_point.x = MulDiv( GET_X_LPARAM(lParam), USER_DEFAULT_SCREEN_DPI, WIN32DRV_MONITOR_ZOOM * context->display_dpi); context->mouse_point.y = MulDiv( GET_Y_LPARAM(lParam), USER_DEFAULT_SCREEN_DPI, WIN32DRV_MONITOR_ZOOM * context->display_dpi); if (context->mouse_point.x < 0) { context->mouse_point.x = 0; } if (context->mouse_point.x > context->display_hor_res - 1) { context->mouse_point.x = context->display_hor_res - 1; } if (context->mouse_point.y < 0) { context->mouse_point.y = 0; } if (context->mouse_point.y > context->display_ver_res - 1) { context->mouse_point.y = context->display_ver_res - 1; } if (uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONUP) { context->mouse_state = ( uMsg == WM_LBUTTONDOWN ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL); } else if (uMsg == WM_MBUTTONDOWN || uMsg == WM_MBUTTONUP) { context->mousewheel_state = ( uMsg == WM_MBUTTONDOWN ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL); } return 0; } case WM_KEYDOWN: case WM_KEYUP: { lv_win32_window_context_t* context = (lv_win32_window_context_t*)( lv_win32_get_window_context(hWnd)); if (context) { EnterCriticalSection(&context->keyboard_mutex); bool skip_translation = false; uint32_t translated_key = 0; switch (wParam) { case VK_UP: translated_key = LV_KEY_UP; break; case VK_DOWN: translated_key = LV_KEY_DOWN; break; case VK_LEFT: translated_key = LV_KEY_LEFT; break; case VK_RIGHT: translated_key = LV_KEY_RIGHT; break; case VK_ESCAPE: translated_key = LV_KEY_ESC; break; case VK_DELETE: translated_key = LV_KEY_DEL; break; case VK_BACK: translated_key = LV_KEY_BACKSPACE; break; case VK_RETURN: translated_key = LV_KEY_ENTER; break; case VK_TAB: case VK_NEXT: translated_key = LV_KEY_NEXT; break; case VK_PRIOR: translated_key = LV_KEY_PREV; break; case VK_HOME: translated_key = LV_KEY_HOME; break; case VK_END: translated_key = LV_KEY_END; break; default: skip_translation = true; break; } if (!skip_translation) { lv_win32_push_key_to_keyboard_queue( context, translated_key, ((uMsg == WM_KEYUP) ? LV_INDEV_STATE_REL : LV_INDEV_STATE_PR)); } LeaveCriticalSection(&context->keyboard_mutex); } break; } case WM_CHAR: { lv_win32_window_context_t* context = (lv_win32_window_context_t*)( lv_win32_get_window_context(hWnd)); if (context) { EnterCriticalSection(&context->keyboard_mutex); uint16_t raw_code_point = (uint16_t)(wParam); if (raw_code_point >= 0x20 && raw_code_point != 0x7F) { if (IS_HIGH_SURROGATE(raw_code_point)) { context->keyboard_utf16_high_surrogate = raw_code_point; } else if (IS_LOW_SURROGATE(raw_code_point)) { context->keyboard_utf16_low_surrogate = raw_code_point; } uint32_t code_point = raw_code_point; if (context->keyboard_utf16_high_surrogate && context->keyboard_utf16_low_surrogate) { uint16_t high_surrogate = context->keyboard_utf16_high_surrogate; uint16_t low_surrogate = context->keyboard_utf16_low_surrogate; code_point = (low_surrogate & 0x03FF); code_point += (((high_surrogate & 0x03FF) + 0x40) << 10); context->keyboard_utf16_high_surrogate = 0; context->keyboard_utf16_low_surrogate = 0; } uint32_t lvgl_code_point = _lv_txt_unicode_to_encoded(code_point); lv_win32_push_key_to_keyboard_queue( context, lvgl_code_point, LV_INDEV_STATE_PR); lv_win32_push_key_to_keyboard_queue( context, lvgl_code_point, LV_INDEV_STATE_REL); } LeaveCriticalSection(&context->keyboard_mutex); } break; } case WM_MOUSEWHEEL: { lv_win32_window_context_t* context = (lv_win32_window_context_t*)( lv_win32_get_window_context(hWnd)); if (context) { context->mousewheel_enc_diff = -(GET_WHEEL_DELTA_WPARAM(wParam) / WHEEL_DELTA); } break; } case WM_TOUCH: { lv_win32_window_context_t* context = (lv_win32_window_context_t*)( lv_win32_get_window_context(hWnd)); if (context) { UINT cInputs = LOWORD(wParam); HTOUCHINPUT hTouchInput = (HTOUCHINPUT)(lParam); PTOUCHINPUT pInputs = malloc(cInputs * sizeof(TOUCHINPUT)); if (pInputs) { if (lv_win32_get_touch_input_info( hTouchInput, cInputs, pInputs, sizeof(TOUCHINPUT))) { for (UINT i = 0; i < cInputs; ++i) { POINT Point; Point.x = TOUCH_COORD_TO_PIXEL(pInputs[i].x); Point.y = TOUCH_COORD_TO_PIXEL(pInputs[i].y); if (!ScreenToClient(hWnd, &Point)) { continue; } context->mouse_point.x = MulDiv( Point.x, USER_DEFAULT_SCREEN_DPI, WIN32DRV_MONITOR_ZOOM * context->display_dpi); context->mouse_point.y = MulDiv( Point.y, USER_DEFAULT_SCREEN_DPI, WIN32DRV_MONITOR_ZOOM * context->display_dpi); DWORD MousePressedMask = TOUCHEVENTF_MOVE | TOUCHEVENTF_DOWN; context->mouse_state = ( pInputs[i].dwFlags & MousePressedMask ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL); } } free(pInputs); } lv_win32_close_touch_input_handle(hTouchInput); } break; } case WM_DPICHANGED: { lv_win32_window_context_t* context = (lv_win32_window_context_t*)( lv_win32_get_window_context(hWnd)); if (context) { context->display_dpi = HIWORD(wParam); LPRECT SuggestedRect = (LPRECT)lParam; SetWindowPos( hWnd, NULL, SuggestedRect->left, SuggestedRect->top, SuggestedRect->right, SuggestedRect->bottom, SWP_NOZORDER | SWP_NOACTIVATE); RECT ClientRect; GetClientRect(hWnd, &ClientRect); int WindowWidth = MulDiv( context->display_hor_res * WIN32DRV_MONITOR_ZOOM, context->display_dpi, USER_DEFAULT_SCREEN_DPI); int WindowHeight = MulDiv( context->display_ver_res * WIN32DRV_MONITOR_ZOOM, context->display_dpi, USER_DEFAULT_SCREEN_DPI); SetWindowPos( hWnd, NULL, SuggestedRect->left, SuggestedRect->top, SuggestedRect->right + (WindowWidth - ClientRect.right), SuggestedRect->bottom + (WindowHeight - ClientRect.bottom), SWP_NOZORDER | SWP_NOACTIVATE); } break; } case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); lv_win32_window_context_t* context = (lv_win32_window_context_t*)( lv_win32_get_window_context(hWnd)); if (context) { if (context->display_framebuffer_context_handle) { SetStretchBltMode(hdc, HALFTONE); StretchBlt( hdc, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top, context->display_framebuffer_context_handle, 0, 0, MulDiv( ps.rcPaint.right - ps.rcPaint.left, USER_DEFAULT_SCREEN_DPI, WIN32DRV_MONITOR_ZOOM * context->display_dpi), MulDiv( ps.rcPaint.bottom - ps.rcPaint.top, USER_DEFAULT_SCREEN_DPI, WIN32DRV_MONITOR_ZOOM * context->display_dpi), SRCCOPY); } } EndPaint(hWnd, &ps); break; } case WM_DESTROY: { lv_win32_window_context_t* context = (lv_win32_window_context_t*)( RemovePropW(hWnd, L"LVGL.SimulatorWindow.WindowContext")); if (context) { lv_disp_t* display_device_object = context->display_device_object; context->display_device_object = NULL; lv_disp_remove(display_device_object); #if (LV_COLOR_DEPTH == 32) || \ (LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP == 0) || \ (LV_COLOR_DEPTH == 8) || \ (LV_COLOR_DEPTH == 1) #else free(context->display_buffer.buf1); #endif DeleteDC(context->display_framebuffer_context_handle); lv_indev_t* mouse_device_object = context->mouse_device_object; context->mouse_device_object = NULL; lv_indev_delete(mouse_device_object); lv_indev_t* mousewheel_device_object = context->mousewheel_device_object; context->mousewheel_device_object = NULL; lv_indev_delete(mousewheel_device_object); lv_indev_t* keyboard_device_object = context->keyboard_device_object; context->keyboard_device_object = NULL; lv_indev_delete(keyboard_device_object); do { PSLIST_ENTRY current = InterlockedPopEntrySList( context->keyboard_queue); if (!current) { _aligned_free(context->keyboard_queue); context->keyboard_queue = NULL; break; } _aligned_free(current); } while (true); DeleteCriticalSection(&context->keyboard_mutex); free(context); } PostQuitMessage(0); break; } default: return DefWindowProcW(hWnd, uMsg, wParam, lParam); } return 0; } static unsigned int __stdcall lv_win32_window_thread_entrypoint( void* raw_parameter) { PWINDOW_THREAD_PARAMETER parameter = (PWINDOW_THREAD_PARAMETER)raw_parameter; g_window_handle = lv_win32_create_display_window( L"LVGL Simulator for Windows Desktop (Display 1)", parameter->hor_res, parameter->ver_res, parameter->instance_handle, parameter->icon_handle, parameter->show_window_mode); if (!g_window_handle) { return 0; } SetEvent(parameter->window_mutex); MSG message; while (GetMessageW(&message, NULL, 0, 0)) { TranslateMessage(&message); DispatchMessageW(&message); } lv_win32_quit_signal = true; return 0; } #endif /*USE_WIN32DRV*/