/** * @file lv_windows_input.c * */ /********************* * INCLUDES *********************/ #include "lv_windows_input.h" #if LV_USE_WINDOWS #ifdef __GNUC__ #pragma GCC diagnostic ignored "-Wcast-function-type" #endif #include "lv_windows_context.h" #include "lv_windows_display.h" #include "lv_windows_input_private.h" #include "../../misc/lv_text_private.h" #include "../../core/lv_obj_private.h" #include #include "../../widgets/textarea/lv_textarea_private.h" #include "../../widgets/keyboard/lv_keyboard.h" /********************* * DEFINES *********************/ /********************** * TYPEDEFS **********************/ /********************** * STATIC PROTOTYPES **********************/ static void lv_windows_pointer_driver_read_callback( lv_indev_t * indev, lv_indev_data_t * data); static void lv_windows_release_pointer_device_event_callback(lv_event_t * e); static void lv_windows_keypad_driver_read_callback( lv_indev_t * indev, lv_indev_data_t * data); static void lv_windows_release_keypad_device_event_callback(lv_event_t * e); static void lv_windows_encoder_driver_read_callback( lv_indev_t * indev, lv_indev_data_t * data); static void lv_windows_release_encoder_device_event_callback(lv_event_t * e); /********************** * STATIC VARIABLES **********************/ /********************** * MACROS **********************/ /********************** * GLOBAL FUNCTIONS **********************/ HWND lv_windows_get_indev_window_handle(lv_indev_t * indev) { return lv_windows_get_display_window_handle(lv_indev_get_display(indev)); } lv_indev_t * lv_windows_acquire_pointer_indev(lv_display_t * display) { HWND window_handle = lv_windows_get_display_window_handle(display); if(!window_handle) { return NULL; } lv_windows_window_context_t * context = lv_windows_get_window_context( window_handle); if(!context) { return NULL; } if(!context->pointer.indev) { context->pointer.state = LV_INDEV_STATE_RELEASED; context->pointer.point.x = 0; context->pointer.point.y = 0; context->pointer.indev = lv_indev_create(); if(context->pointer.indev) { lv_indev_set_type( context->pointer.indev, LV_INDEV_TYPE_POINTER); lv_indev_set_read_cb( context->pointer.indev, lv_windows_pointer_driver_read_callback); lv_indev_set_display( context->pointer.indev, context->display_device_object); lv_indev_add_event_cb( context->pointer.indev, lv_windows_release_pointer_device_event_callback, LV_EVENT_DELETE, context->pointer.indev); lv_indev_set_group( context->pointer.indev, lv_group_get_default()); } } return context->pointer.indev; } lv_indev_t * lv_windows_acquire_keypad_indev(lv_display_t * display) { HWND window_handle = lv_windows_get_display_window_handle(display); if(!window_handle) { return NULL; } lv_windows_window_context_t * context = lv_windows_get_window_context( window_handle); if(!context) { return NULL; } if(!context->keypad.indev) { lv_ll_init( &context->keypad.queue, sizeof(lv_windows_keypad_queue_item_t)); context->keypad.utf16_high_surrogate = 0; context->keypad.utf16_low_surrogate = 0; context->keypad.indev = lv_indev_create(); if(context->keypad.indev) { lv_indev_set_type( context->keypad.indev, LV_INDEV_TYPE_KEYPAD); lv_indev_set_read_cb( context->keypad.indev, lv_windows_keypad_driver_read_callback); lv_indev_set_display( context->keypad.indev, context->display_device_object); lv_indev_add_event_cb( context->keypad.indev, lv_windows_release_keypad_device_event_callback, LV_EVENT_DELETE, context->keypad.indev); lv_indev_set_group( context->keypad.indev, lv_group_get_default()); } } return context->keypad.indev; } lv_indev_t * lv_windows_acquire_encoder_indev(lv_display_t * display) { HWND window_handle = lv_windows_get_display_window_handle(display); if(!window_handle) { return NULL; } lv_windows_window_context_t * context = lv_windows_get_window_context( window_handle); if(!context) { return NULL; } if(!context->encoder.indev) { context->encoder.state = LV_INDEV_STATE_RELEASED; context->encoder.enc_diff = 0; context->encoder.indev = lv_indev_create(); if(context->encoder.indev) { lv_indev_set_type( context->encoder.indev, LV_INDEV_TYPE_ENCODER); lv_indev_set_read_cb( context->encoder.indev, lv_windows_encoder_driver_read_callback); lv_indev_set_display( context->encoder.indev, context->display_device_object); lv_indev_add_event_cb( context->encoder.indev, lv_windows_release_encoder_device_event_callback, LV_EVENT_DELETE, context->encoder.indev); lv_indev_set_group( context->encoder.indev, lv_group_get_default()); } } return context->encoder.indev; } /********************** * STATIC FUNCTIONS **********************/ static void lv_windows_pointer_driver_read_callback( lv_indev_t * indev, lv_indev_data_t * data) { lv_windows_window_context_t * context = lv_windows_get_window_context( lv_windows_get_indev_window_handle(indev)); if(!context) { return; } data->state = context->pointer.state; data->point = context->pointer.point; } static void lv_windows_release_pointer_device_event_callback(lv_event_t * e) { lv_indev_t * indev = (lv_indev_t *)lv_event_get_user_data(e); if(!indev) { return; } HWND window_handle = lv_windows_get_indev_window_handle(indev); if(!window_handle) { return; } lv_windows_window_context_t * context = lv_windows_get_window_context( window_handle); if(!context) { return; } context->pointer.state = LV_INDEV_STATE_RELEASED; context->pointer.point.x = 0; context->pointer.point.y = 0; context->pointer.indev = NULL; } static BOOL lv_windows_get_touch_input_info( HTOUCHINPUT touch_input_handle, UINT input_count, PTOUCHINPUT inputs, int item_size) { HMODULE module_handle = GetModuleHandleW(L"user32.dll"); if(!module_handle) { return FALSE; } typedef BOOL(WINAPI * function_type)(HTOUCHINPUT, UINT, PTOUCHINPUT, int); function_type function = (function_type)( GetProcAddress(module_handle, "GetTouchInputInfo")); if(!function) { return FALSE; } return function(touch_input_handle, input_count, inputs, item_size); } static BOOL lv_windows_close_touch_input_handle( HTOUCHINPUT touch_input_handle) { HMODULE module_handle = GetModuleHandleW(L"user32.dll"); if(!module_handle) { return FALSE; } typedef BOOL(WINAPI * function_type)(HTOUCHINPUT); function_type function = (function_type)( GetProcAddress(module_handle, "CloseTouchInputHandle")); if(!function) { return FALSE; } return function(touch_input_handle); } bool lv_windows_pointer_device_window_message_handler( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT * plResult) { switch(uMsg) { case WM_MOUSEMOVE: { lv_windows_window_context_t * context = (lv_windows_window_context_t *)( lv_windows_get_window_context(hWnd)); if(context) { int32_t hor_res = lv_display_get_horizontal_resolution( context->display_device_object); int32_t ver_res = lv_display_get_vertical_resolution( context->display_device_object); context->pointer.point.x = lv_windows_zoom_to_logical( GET_X_LPARAM(lParam), context->zoom_level); context->pointer.point.y = lv_windows_zoom_to_logical( GET_Y_LPARAM(lParam), context->zoom_level); if(context->simulator_mode) { context->pointer.point.x = lv_windows_dpi_to_logical( context->pointer.point.x, context->window_dpi); context->pointer.point.y = lv_windows_dpi_to_logical( context->pointer.point.y, context->window_dpi); } if(context->pointer.point.x < 0) { context->pointer.point.x = 0; } if(context->pointer.point.x > hor_res - 1) { context->pointer.point.x = hor_res - 1; } if(context->pointer.point.y < 0) { context->pointer.point.y = 0; } if(context->pointer.point.y > ver_res - 1) { context->pointer.point.y = ver_res - 1; } } break; } case WM_LBUTTONDOWN: case WM_LBUTTONUP: { lv_windows_window_context_t * context = (lv_windows_window_context_t *)( lv_windows_get_window_context(hWnd)); if(context) { context->pointer.state = ( uMsg == WM_LBUTTONDOWN ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED); } break; } case WM_TOUCH: { lv_windows_window_context_t * context = (lv_windows_window_context_t *)( lv_windows_get_window_context(hWnd)); if(context) { UINT input_count = LOWORD(wParam); HTOUCHINPUT touch_input_handle = (HTOUCHINPUT)(lParam); PTOUCHINPUT inputs = malloc(input_count * sizeof(TOUCHINPUT)); if(inputs) { if(lv_windows_get_touch_input_info( touch_input_handle, input_count, inputs, sizeof(TOUCHINPUT))) { for(UINT i = 0; i < input_count; ++i) { POINT Point; Point.x = TOUCH_COORD_TO_PIXEL(inputs[i].x); Point.y = TOUCH_COORD_TO_PIXEL(inputs[i].y); if(!ScreenToClient(hWnd, &Point)) { continue; } context->pointer.point.x = lv_windows_zoom_to_logical( Point.x, context->zoom_level); context->pointer.point.y = lv_windows_zoom_to_logical( Point.y, context->zoom_level); if(context->simulator_mode) { context->pointer.point.x = lv_windows_dpi_to_logical( context->pointer.point.x, context->window_dpi); context->pointer.point.y = lv_windows_dpi_to_logical( context->pointer.point.y, context->window_dpi); } DWORD MousePressedMask = TOUCHEVENTF_MOVE | TOUCHEVENTF_DOWN; context->pointer.state = ( inputs[i].dwFlags & MousePressedMask ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED); } } free(inputs); } lv_windows_close_touch_input_handle(touch_input_handle); } break; } default: // Not Handled return false; } // Handled *plResult = 0; return true; } static void lv_windows_keypad_driver_read_callback( lv_indev_t * indev, lv_indev_data_t * data) { lv_windows_window_context_t * context = lv_windows_get_window_context( lv_windows_get_indev_window_handle(indev)); if(!context) { return; } lv_windows_keypad_queue_item_t * current = (lv_windows_keypad_queue_item_t *)( lv_ll_get_head(&context->keypad.queue)); if(current) { data->key = current->key; data->state = current->state; lv_ll_remove(&context->keypad.queue, current); lv_free(current); data->continue_reading = true; } } static void lv_windows_release_keypad_device_event_callback(lv_event_t * e) { lv_indev_t * indev = (lv_indev_t *)lv_event_get_user_data(e); if(!indev) { return; } HWND window_handle = lv_windows_get_indev_window_handle(indev); if(!window_handle) { return; } lv_windows_window_context_t * context = lv_windows_get_window_context( window_handle); if(!context) { return; } lv_ll_clear(&context->keypad.queue); context->keypad.utf16_high_surrogate = 0; context->keypad.utf16_low_surrogate = 0; context->keypad.indev = NULL; } static void lv_windows_push_key_to_keyboard_queue( lv_windows_window_context_t * context, uint32_t key, lv_indev_state_t state) { lv_windows_keypad_queue_item_t * current = (lv_windows_keypad_queue_item_t *)( lv_ll_ins_tail(&context->keypad.queue)); if(current) { current->key = key; current->state = state; } } static HIMC lv_windows_imm_get_context( HWND window_handle) { HMODULE module_handle = GetModuleHandleW(L"imm32.dll"); if(!module_handle) { return FALSE; } typedef HIMC(WINAPI * function_type)(HWND); function_type function = (function_type)( GetProcAddress(module_handle, "ImmGetContext")); if(!function) { return FALSE; } return function(window_handle); } static BOOL lv_windows_imm_release_context( HWND window_handle, HIMC imm_context_handle) { HMODULE module_handle = GetModuleHandleW(L"imm32.dll"); if(!module_handle) { return FALSE; } typedef BOOL(WINAPI * function_type)(HWND, HIMC); function_type function = (function_type)( GetProcAddress(module_handle, "ImmReleaseContext")); if(!function) { return FALSE; } return function(window_handle, imm_context_handle); } static HIMC lv_windows_imm_associate_context( HWND window_handle, HIMC imm_context_handle) { HMODULE module_handle = GetModuleHandleW(L"imm32.dll"); if(!module_handle) { return FALSE; } typedef HIMC(WINAPI * function_type)(HWND, HIMC); function_type function = (function_type)( GetProcAddress(module_handle, "ImmAssociateContext")); if(!function) { return FALSE; } return function(window_handle, imm_context_handle); } static BOOL lv_windows_imm_set_composition_window( HIMC imm_context_handle, LPCOMPOSITIONFORM composition_form) { HMODULE module_handle = GetModuleHandleW(L"imm32.dll"); if(!module_handle) { return FALSE; } typedef BOOL(WINAPI * function_type)(HIMC, LPCOMPOSITIONFORM); function_type function = (function_type)( GetProcAddress(module_handle, "ImmSetCompositionWindow")); if(!function) { return FALSE; } return function(imm_context_handle, composition_form); } bool lv_windows_keypad_device_window_message_handler( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT * plResult) { LV_UNUSED(lParam); switch(uMsg) { case WM_KEYDOWN: case WM_KEYUP: { lv_windows_window_context_t * context = (lv_windows_window_context_t *)( lv_windows_get_window_context(hWnd)); if(context) { 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_windows_push_key_to_keyboard_queue( context, translated_key, ((uMsg == WM_KEYUP) ? LV_INDEV_STATE_RELEASED : LV_INDEV_STATE_PRESSED)); } } break; } case WM_CHAR: { lv_windows_window_context_t * context = (lv_windows_window_context_t *)( lv_windows_get_window_context(hWnd)); if(context) { 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->keypad.utf16_high_surrogate = raw_code_point; } else if(IS_LOW_SURROGATE(raw_code_point)) { context->keypad.utf16_low_surrogate = raw_code_point; } uint32_t code_point = raw_code_point; if(context->keypad.utf16_high_surrogate && context->keypad.utf16_low_surrogate) { uint16_t high_surrogate = context->keypad.utf16_high_surrogate; uint16_t low_surrogate = context->keypad.utf16_low_surrogate; code_point = (low_surrogate & 0x03FF); code_point += (((high_surrogate & 0x03FF) + 0x40) << 10); context->keypad.utf16_high_surrogate = 0; context->keypad.utf16_low_surrogate = 0; } uint32_t lvgl_code_point = lv_text_unicode_to_encoded(code_point); lv_windows_push_key_to_keyboard_queue( context, lvgl_code_point, LV_INDEV_STATE_PRESSED); lv_windows_push_key_to_keyboard_queue( context, lvgl_code_point, LV_INDEV_STATE_RELEASED); } } break; } case WM_IME_SETCONTEXT: { if(wParam == TRUE) { HIMC imm_context_handle = lv_windows_imm_get_context(hWnd); if(imm_context_handle) { lv_windows_imm_associate_context( hWnd, imm_context_handle); lv_windows_imm_release_context( hWnd, imm_context_handle); } } *plResult = DefWindowProcW(hWnd, uMsg, wParam, wParam); break; } case WM_IME_STARTCOMPOSITION: { HIMC imm_context_handle = lv_windows_imm_get_context(hWnd); if(imm_context_handle) { lv_obj_t * textarea_object = NULL; lv_obj_t * focused_object = lv_group_get_focused( lv_group_get_default()); if(focused_object) { const lv_obj_class_t * object_class = lv_obj_get_class( focused_object); if(object_class == &lv_textarea_class) { textarea_object = focused_object; } else if(object_class == &lv_keyboard_class) { textarea_object = lv_keyboard_get_textarea(focused_object); } } COMPOSITIONFORM composition_form; composition_form.dwStyle = CFS_POINT; composition_form.ptCurrentPos.x = 0; composition_form.ptCurrentPos.y = 0; if(textarea_object) { lv_textarea_t * textarea = (lv_textarea_t *)(textarea_object); lv_obj_t * label_object = lv_textarea_get_label(textarea_object); composition_form.ptCurrentPos.x = label_object->coords.x1 + textarea->cursor.area.x1; composition_form.ptCurrentPos.y = label_object->coords.y1 + textarea->cursor.area.y1; } lv_windows_imm_set_composition_window( imm_context_handle, &composition_form); lv_windows_imm_release_context( hWnd, imm_context_handle); } *plResult = DefWindowProcW(hWnd, uMsg, wParam, wParam); break; } default: // Not Handled return false; } // Handled *plResult = 0; return true; } static void lv_windows_encoder_driver_read_callback( lv_indev_t * indev, lv_indev_data_t * data) { lv_windows_window_context_t * context = lv_windows_get_window_context( lv_windows_get_indev_window_handle(indev)); if(!context) { return; } data->state = context->encoder.state; data->enc_diff = context->encoder.enc_diff; context->encoder.enc_diff = 0; } static void lv_windows_release_encoder_device_event_callback(lv_event_t * e) { lv_indev_t * indev = (lv_indev_t *)lv_event_get_user_data(e); if(!indev) { return; } HWND window_handle = lv_windows_get_indev_window_handle(indev); if(!window_handle) { return; } lv_windows_window_context_t * context = lv_windows_get_window_context( window_handle); if(!context) { return; } context->encoder.state = LV_INDEV_STATE_RELEASED; context->encoder.enc_diff = 0; context->encoder.indev = NULL; } bool lv_windows_encoder_device_window_message_handler( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT * plResult) { LV_UNUSED(lParam); switch(uMsg) { case WM_MBUTTONDOWN: case WM_MBUTTONUP: { lv_windows_window_context_t * context = (lv_windows_window_context_t *)( lv_windows_get_window_context(hWnd)); if(context) { context->encoder.state = ( uMsg == WM_MBUTTONDOWN ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED); } break; } case WM_MOUSEWHEEL: { lv_windows_window_context_t * context = (lv_windows_window_context_t *)( lv_windows_get_window_context(hWnd)); if(context) { context->encoder.enc_diff = -(GET_WHEEL_DELTA_WPARAM(wParam) / WHEEL_DELTA); } break; } default: // Not Handled return false; } // Handled *plResult = 0; return true; } #endif // LV_USE_WINDOWS