1
0
mirror of https://github.com/lvgl/lvgl.git synced 2025-01-28 07:03:00 +08:00

feat(drivers): add Windows backend for LVGL v9 (#5313)

This commit is contained in:
Kenji Mouri (Qi Lu) 2024-01-14 23:35:35 +08:00 committed by GitHub
parent b4cf95e7fa
commit cab1336d8e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 2051 additions and 0 deletions

View File

@ -1489,6 +1489,10 @@ menu "LVGL configuration"
config LV_USE_ILI9341
bool "Use ILI9341 LCD driver"
default n
config LV_USE_WINDOWS
bool "Use LVGL Windows backend"
default n
endmenu
menu "Examples"

View File

@ -8,3 +8,4 @@ Drivers
display/index
touchpad/index
X11
windows

View File

@ -0,0 +1,105 @@
=============================
Windows Display/Inputs driver
=============================
Overview
-------------
The **Windows** display/input `driver <https://github.com/lvgl/lvgl/src/dev/windows>`__ offers support for simulating the LVGL display and keyboard/mouse inputs in a Windows Win32 window.
The main purpose for this driver is for testing/debugging the LVGL application in a **Windows** simulation window via **simulator mode**, or developing a standard **Windows** desktop application with LVGL via **application mode**.
Here are the **similarity** for simulator mode and application mode.
- Support LVGL pointer, keypad and encoder devices integration.
- Support Windows touch input.
- Support Windows input method integration input.
- Support Per-monitor DPI Aware (both V1 and V2).
Here are the **differences** for simulator mode and application mode.
Simulator Mode
^^^^^^^^^^^^^^
- Designed for LVGL simulation scenario.
- Keep the LVGL display resolution all time for trying best to simulate UI layout which will see in their production devices.
- When Windows DPI scaling setting is changed, Windows backend will stretch the display content.
Application Mode
^^^^^^^^^^^^^^^^
- Designed for Windows desktop application development scenario.
- Have the Window resizing support and LVGL display resolution will be changed.
- When Windows DPI scaling setting is changed, the LVGL display DPI value will also be changed.
Prerequisites
-------------
The minimum Windows OS version this driver supported is Windows Server 2003, or Windows XP with East Asian language support installed because the input method integration support need that.
Configure Windows driver
--------------------
Enable the Windows driver support in lv_conf.h, by cmake compiler define or by KConfig
.. code:: c
#define LV_USE_WINDOWS 1
Usage
-----
.. code:: c
#include <Windows.h>
#include "lvgl/lvgl.h"
#include "lvgl/examples/lv_examples.h"
#include "lvgl/demos/lv_demos.h"
int main()
{
lv_init();
int32_t zoom_level = 100;
bool allow_dpi_override = false;
bool simulator_mode = false;
lv_display_t* display = lv_windows_create_display(
L"LVGL Display Window",
800,
480,
zoom_level,
allow_dpi_override,
simulator_mode);
if (!display)
{
return -1;
}
lv_indev_t* pointer_device = lv_windows_acquire_pointer_indev(display);
if (!pointer_device)
{
return -1;
}
lv_indev_t* keypad_device = lv_windows_acquire_keypad_indev(display);
if (!keypad_device)
{
return -1;
}
lv_indev_t* encoder_device = lv_windows_acquire_encoder_indev(display);
if (!encoder_device)
{
return -1;
}
lv_demo_widgets();
while (1)
{
uint32_t time_till_next = lv_timer_handler();
Sleep(time_till_next);
}
return 0;
}

View File

@ -893,6 +893,8 @@
#define LV_USE_GENERIC_MIPI (LV_USE_ST7735 | LV_USE_ST7789 | LV_USE_ST7796 | LV_USE_ILI9341)
/* LVGL Windows backend */
#define LV_USE_WINDOWS 0
/*==================
* EXAMPLES

3
lvgl.h
View File

@ -133,6 +133,9 @@ extern "C" {
#include "src/dev/evdev/lv_evdev.h"
#include "src/dev/windows/lv_windows_input.h"
#include "src/dev/windows/lv_windows_display.h"
#include "src/core/lv_global.h"
/*********************
* DEFINES

View File

@ -0,0 +1,669 @@
/**
* @file lv_windows_context.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_windows_context.h"
#if LV_USE_WINDOWS
#include "lv_windows_display.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static uint32_t lv_windows_tick_count_callback(void);
static void lv_windows_check_display_existence_timer_callback(
lv_timer_t * timer);
static LRESULT CALLBACK lv_windows_window_message_callback(
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam);
bool lv_windows_pointer_device_window_message_handler(
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam,
LRESULT * plResult);
bool lv_windows_keypad_device_window_message_handler(
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam,
LRESULT * plResult);
bool lv_windows_encoder_device_window_message_handler(
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam,
LRESULT * plResult);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_windows_platform_init(void)
{
lv_tick_set_cb(lv_windows_tick_count_callback);
lv_timer_create(
lv_windows_check_display_existence_timer_callback,
200,
NULL);
// Try to ensure the default group exists.
{
lv_group_t * default_group = lv_group_get_default();
if(!default_group) {
default_group = lv_group_create();
if(default_group) {
lv_group_set_default(default_group);
}
}
}
WNDCLASSEXW window_class;
lv_memzero(&window_class, sizeof(WNDCLASSEXW));
window_class.cbSize = sizeof(WNDCLASSEXW);
window_class.style = 0;
window_class.lpfnWndProc = lv_windows_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 = L"LVGL.Window";
window_class.hIconSm = NULL;
LV_ASSERT_NULL(RegisterClassExW(&window_class));
}
lv_windows_window_context_t * lv_windows_get_window_context(
HWND window_handle)
{
return (lv_windows_window_context_t *)(
GetPropW(window_handle, L"LVGL.Window.Context"));
}
/**********************
* STATIC FUNCTIONS
**********************/
static uint32_t lv_windows_tick_count_callback(void)
{
return GetTickCount();
}
static void lv_windows_check_display_existence_timer_callback(
lv_timer_t * timer)
{
if(!lv_display_get_next(NULL)) {
// Don't use lv_deinit() due to it will cause exception when parallel
// rendering is enabled.
exit(0);
}
}
static HDC lv_windows_create_frame_buffer(
HWND window_handle,
LONG width,
LONG height,
UINT32 ** pixel_buffer,
SIZE_T * pixel_buffer_size)
{
HDC frame_buffer_dc_handle = NULL;
LV_ASSERT_NULL(pixel_buffer);
LV_ASSERT_NULL(pixel_buffer_size);
HDC window_dc_handle = GetDC(window_handle);
if(window_dc_handle) {
frame_buffer_dc_handle = CreateCompatibleDC(window_dc_handle);
ReleaseDC(window_handle, window_dc_handle);
}
if(frame_buffer_dc_handle) {
#if (LV_COLOR_DEPTH == 32) || (LV_COLOR_DEPTH == 24)
BITMAPINFO bitmap_info = { 0 };
#elif (LV_COLOR_DEPTH == 16)
typedef struct _BITMAPINFO_16BPP {
BITMAPINFOHEADER bmiHeader;
DWORD bmiColorMask[3];
} BITMAPINFO_16BPP, * PBITMAPINFO_16BPP;
BITMAPINFO_16BPP bitmap_info = { 0 };
#else
#error [lv_windows] Unsupported LV_COLOR_DEPTH.
#endif
bitmap_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmap_info.bmiHeader.biWidth = width;
bitmap_info.bmiHeader.biHeight = -height;
bitmap_info.bmiHeader.biPlanes = 1;
bitmap_info.bmiHeader.biBitCount = lv_color_format_get_bpp(
LV_COLOR_FORMAT_NATIVE);
#if (LV_COLOR_DEPTH == 32) || (LV_COLOR_DEPTH == 24)
bitmap_info.bmiHeader.biCompression = BI_RGB;
#elif (LV_COLOR_DEPTH == 16)
bitmap_info.bmiHeader.biCompression = BI_BITFIELDS;
bitmap_info.bmiColorMask[0] = 0xF800;
bitmap_info.bmiColorMask[1] = 0x07E0;
bitmap_info.bmiColorMask[2] = 0x001F;
#else
#error [lv_windows] Unsupported LV_COLOR_DEPTH.
#endif
HBITMAP hBitmap = CreateDIBSection(
frame_buffer_dc_handle,
(PBITMAPINFO)(&bitmap_info),
DIB_RGB_COLORS,
(void **)pixel_buffer,
NULL,
0);
if(hBitmap) {
*pixel_buffer_size = width * height;
*pixel_buffer_size *= lv_color_format_get_size(
LV_COLOR_FORMAT_NATIVE);
DeleteObject(SelectObject(frame_buffer_dc_handle, hBitmap));
DeleteObject(hBitmap);
}
else {
DeleteDC(frame_buffer_dc_handle);
frame_buffer_dc_handle = NULL;
}
}
return frame_buffer_dc_handle;
}
static void lv_windows_display_timer_callback(lv_timer_t * timer)
{
lv_windows_window_context_t * context = lv_timer_get_user_data(timer);
LV_ASSERT_NULL(context);
if(!context->display_resolution_changed) {
return;
}
lv_display_set_resolution(
context->display_device_object,
context->requested_display_resolution.x,
context->requested_display_resolution.y);
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);
HWND window_handle = lv_windows_get_display_window_handle(
context->display_device_object);
if(window_handle) {
if(context->display_framebuffer_context_handle) {
context->display_framebuffer_base = NULL;
context->display_framebuffer_size = 0;
DeleteDC(context->display_framebuffer_context_handle);
context->display_framebuffer_context_handle = NULL;
}
context->display_framebuffer_context_handle =
lv_windows_create_frame_buffer(
window_handle,
hor_res,
ver_res,
&context->display_framebuffer_base,
&context->display_framebuffer_size);
if(context->display_framebuffer_context_handle) {
lv_display_set_buffers(
context->display_device_object,
context->display_framebuffer_base,
NULL,
context->display_framebuffer_size,
LV_DISPLAY_RENDER_MODE_DIRECT);
}
}
context->display_resolution_changed = false;
context->requested_display_resolution.x = 0;
context->requested_display_resolution.y = 0;
}
static void lv_windows_display_driver_flush_callback(
lv_display_t * display,
const lv_area_t * area,
uint8_t * px_map)
{
HWND window_handle = lv_windows_get_display_window_handle(display);
if(!window_handle) {
lv_display_flush_ready(display);
return;
}
lv_windows_window_context_t * context = lv_windows_get_window_context(
window_handle);
if(!context) {
lv_display_flush_ready(display);
return;
}
if(lv_display_flush_is_last(display)) {
#if (LV_COLOR_DEPTH == 32) || \
(LV_COLOR_DEPTH == 24) || \
(LV_COLOR_DEPTH == 16)
UNREFERENCED_PARAMETER(px_map);
#else
#error [lv_windows] Unsupported LV_COLOR_DEPTH.
#endif
HDC hdc = GetDC(window_handle);
if(hdc) {
SetStretchBltMode(hdc, HALFTONE);
RECT client_rect;
GetClientRect(window_handle, &client_rect);
int32_t width = lv_windows_zoom_to_logical(
client_rect.right - client_rect.left,
context->zoom_level);
int32_t height = lv_windows_zoom_to_logical(
client_rect.bottom - client_rect.top,
context->zoom_level);
if(context->simulator_mode) {
width = lv_windows_dpi_to_logical(width, context->window_dpi);
height = lv_windows_dpi_to_logical(height, context->window_dpi);
}
StretchBlt(
hdc,
client_rect.left,
client_rect.top,
client_rect.right - client_rect.left,
client_rect.bottom - client_rect.top,
context->display_framebuffer_context_handle,
0,
0,
width,
height,
SRCCOPY);
ReleaseDC(window_handle, hdc);
}
}
lv_display_flush_ready(display);
}
static UINT lv_windows_get_dpi_for_window(HWND window_handle)
{
UINT result = (UINT)(-1);
HMODULE module_handle = LoadLibraryW(L"SHCore.dll");
if(module_handle) {
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 * function_type)(
HMONITOR, MONITOR_DPI_TYPE_PRIVATE, UINT *, UINT *);
function_type function = (function_type)(
GetProcAddress(module_handle, "GetDpiForMonitor"));
if(function) {
HMONITOR MonitorHandle = MonitorFromWindow(
window_handle,
MONITOR_DEFAULTTONEAREST);
UINT dpiX = 0;
UINT dpiY = 0;
if(SUCCEEDED(function(
MonitorHandle,
MDT_EFFECTIVE_DPI,
&dpiX,
&dpiY))) {
result = dpiX;
}
}
FreeLibrary(module_handle);
}
if(result == (UINT)(-1)) {
HDC hWindowDC = GetDC(window_handle);
if(hWindowDC) {
result = GetDeviceCaps(hWindowDC, LOGPIXELSX);
ReleaseDC(window_handle, hWindowDC);
}
}
if(result == (UINT)(-1)) {
result = USER_DEFAULT_SCREEN_DPI;
}
return result;
}
static BOOL lv_windows_register_touch_window(
HWND window_handle,
ULONG flags)
{
HMODULE module_handle = GetModuleHandleW(L"user32.dll");
if(!module_handle) {
return FALSE;
}
typedef BOOL(WINAPI * function_type)(HWND, ULONG);
function_type function = (function_type)(
GetProcAddress(module_handle, "RegisterTouchWindow"));
if(!function) {
return FALSE;
}
return function(window_handle, flags);
}
static BOOL lv_windows_enable_child_window_dpi_message(
HWND WindowHandle)
{
// The private Per-Monitor DPI Awareness support extension is Windows 10
// only. We don't need the private Per-Monitor DPI Awareness support
// extension if the Per-Monitor (V2) DPI Awareness exists.
OSVERSIONINFOEXW os_version_info_ex = { 0 };
os_version_info_ex.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
os_version_info_ex.dwMajorVersion = 10;
os_version_info_ex.dwMinorVersion = 0;
os_version_info_ex.dwBuildNumber = 14986;
if(!VerifyVersionInfoW(
&os_version_info_ex,
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 module_handle = GetModuleHandleW(L"user32.dll");
if(!module_handle) {
return FALSE;
}
typedef BOOL(WINAPI * function_type)(HWND, BOOL);
function_type function = (function_type)(
GetProcAddress(module_handle, "EnableChildWindowDpiMessage"));
if(!function) {
return FALSE;
}
return function(WindowHandle, TRUE);
}
static LRESULT CALLBACK lv_windows_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_windows_create_display_data_t * data =
(lv_windows_create_display_data_t *)(
((LPCREATESTRUCTW)(lParam))->lpCreateParams);
if(!data) {
return -1;
}
lv_windows_window_context_t * context =
(lv_windows_window_context_t *)(HeapAlloc(
GetProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof(lv_windows_window_context_t)));
if(!context) {
return -1;
}
if(!SetPropW(hWnd, L"LVGL.Window.Context", (HANDLE)(context))) {
return -1;
}
context->window_dpi = lv_windows_get_dpi_for_window(hWnd);
context->zoom_level = data->zoom_level;
context->allow_dpi_override = data->allow_dpi_override;
context->simulator_mode = data->simulator_mode;
context->display_timer_object = lv_timer_create(
lv_windows_display_timer_callback,
LV_DEF_REFR_PERIOD,
context);
context->display_resolution_changed = false;
context->requested_display_resolution.x = 0;
context->requested_display_resolution.y = 0;
context->display_device_object = lv_display_create(0, 0);
if(!context->display_device_object) {
return -1;
}
RECT request_content_size;
GetWindowRect(hWnd, &request_content_size);
lv_display_set_resolution(
context->display_device_object,
request_content_size.right - request_content_size.left,
request_content_size.bottom - request_content_size.top);
lv_display_set_flush_cb(
context->display_device_object,
lv_windows_display_driver_flush_callback);
lv_display_set_driver_data(
context->display_device_object,
hWnd);
if(!context->allow_dpi_override) {
lv_display_set_dpi(
context->display_device_object,
context->window_dpi);
}
if(context->simulator_mode) {
context->display_resolution_changed = true;
context->requested_display_resolution.x =
lv_display_get_horizontal_resolution(
context->display_device_object);
context->requested_display_resolution.y =
lv_display_get_vertical_resolution(
context->display_device_object);
}
lv_windows_register_touch_window(hWnd, 0);
lv_windows_enable_child_window_dpi_message(hWnd);
break;
}
case WM_SIZE: {
if(wParam != SIZE_MINIMIZED) {
lv_windows_window_context_t * context = (lv_windows_window_context_t *)(
lv_windows_get_window_context(hWnd));
if(context) {
if(!context->simulator_mode) {
context->display_resolution_changed = true;
context->requested_display_resolution.x = LOWORD(lParam);
context->requested_display_resolution.y = HIWORD(lParam);
}
else {
int32_t window_width = lv_windows_dpi_to_physical(
lv_windows_zoom_to_physical(
lv_display_get_horizontal_resolution(
context->display_device_object),
context->zoom_level),
context->window_dpi);
int32_t window_height = lv_windows_dpi_to_physical(
lv_windows_zoom_to_physical(
lv_display_get_vertical_resolution(
context->display_device_object),
context->zoom_level),
context->window_dpi);
RECT window_rect;
GetWindowRect(hWnd, &window_rect);
RECT client_rect;
GetClientRect(hWnd, &client_rect);
int32_t original_window_width =
window_rect.right - window_rect.left;
int32_t original_window_height =
window_rect.bottom - window_rect.top;
int32_t original_client_width =
client_rect.right - client_rect.left;
int32_t original_client_height =
client_rect.bottom - client_rect.top;
int32_t reserved_width =
original_window_width - original_client_width;
int32_t reserved_height =
original_window_height - original_client_height;
SetWindowPos(
hWnd,
NULL,
0,
0,
reserved_width + window_width,
reserved_height + window_height,
SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE);
}
}
}
break;
}
case WM_DPICHANGED: {
lv_windows_window_context_t * context = (lv_windows_window_context_t *)(
lv_windows_get_window_context(hWnd));
if(context) {
context->window_dpi = HIWORD(wParam);
if(!context->allow_dpi_override) {
lv_display_set_dpi(
context->display_device_object,
context->window_dpi);
}
LPRECT suggested_rect = (LPRECT)lParam;
SetWindowPos(
hWnd,
NULL,
suggested_rect->left,
suggested_rect->top,
suggested_rect->right,
suggested_rect->bottom,
SWP_NOZORDER | SWP_NOACTIVATE);
}
break;
}
case WM_ERASEBKGND: {
return TRUE;
}
case WM_DESTROY: {
lv_windows_window_context_t * context = (lv_windows_window_context_t *)(
RemovePropW(hWnd, L"LVGL.Window.Context"));
if(context) {
lv_display_t * display_device_object =
context->display_device_object;
context->display_device_object = NULL;
lv_display_delete(display_device_object);
DeleteDC(context->display_framebuffer_context_handle);
lv_timer_delete(context->display_timer_object);
HeapFree(GetProcessHeap(), 0, context);
}
PostQuitMessage(0);
break;
}
default: {
lv_windows_window_context_t * context = (lv_windows_window_context_t *)(
lv_windows_get_window_context(hWnd));
if(context) {
LRESULT lResult = 0;
if(context->pointer.indev &&
lv_windows_pointer_device_window_message_handler(
hWnd,
uMsg,
wParam,
lParam,
&lResult)) {
return lResult;
}
else if(context->keypad.indev &&
lv_windows_keypad_device_window_message_handler(
hWnd,
uMsg,
wParam,
lParam,
&lResult)) {
return lResult;
}
else if(context->encoder.indev &&
lv_windows_encoder_device_window_message_handler(
hWnd,
uMsg,
wParam,
lParam,
&lResult)) {
return lResult;
}
}
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
}
}
return 0;
}
#endif // LV_USE_WINDOWS

View File

@ -0,0 +1,123 @@
/**
* @file lv_windows_context.h
*
*/
#ifndef LV_WINDOWS_CONTEXT_H
#define LV_WINDOWS_CONTEXT_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../display/lv_display.h"
#include "../../indev/lv_indev.h"
#if LV_USE_WINDOWS
#include <windows.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct _lv_windows_pointer_context_t {
lv_indev_state_t state;
lv_point_t point;
lv_indev_t * indev;
} lv_windows_pointer_context_t;
typedef struct _lv_windows_keypad_queue_item_t {
uint32_t key;
lv_indev_state_t state;
} lv_windows_keypad_queue_item_t;
typedef struct _lv_windows_keypad_context_t {
CRITICAL_SECTION mutex;
lv_ll_t queue;
uint16_t utf16_high_surrogate;
uint16_t utf16_low_surrogate;
lv_indev_t * indev;
} lv_windows_keypad_context_t;
typedef struct _lv_windows_encoder_context_t {
lv_indev_state_t state;
int16_t enc_diff;
lv_indev_t * indev;
} lv_windows_encoder_context_t;
typedef struct _lv_windows_window_context_t {
lv_display_t * display_device_object;
lv_timer_t * display_timer_object;
int32_t window_dpi;
int32_t zoom_level;
bool allow_dpi_override;
bool simulator_mode;
bool display_resolution_changed;
lv_point_t requested_display_resolution;
HDC display_framebuffer_context_handle;
uint32_t * display_framebuffer_base;
size_t display_framebuffer_size;
lv_windows_pointer_context_t pointer;
lv_windows_keypad_context_t keypad;
lv_windows_encoder_context_t encoder;
} lv_windows_window_context_t;
typedef struct _lv_windows_create_display_data_t {
const wchar_t * title;
int32_t hor_res;
int32_t ver_res;
int32_t zoom_level;
bool allow_dpi_override;
bool simulator_mode;
HANDLE mutex;
lv_display_t * display;
} lv_windows_create_display_data_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* @brief Initialize the LVGL Windows backend.
* @remark This is a private API which is used for LVGL Windows backend
* implementation. LVGL users shouldn't use that because the
* LVGL has already used it in lv_init.
*/
void lv_windows_platform_init(void);
/**
* @brief Get the window context from specific LVGL display window.
* @param window_handle The window handle of specific LVGL display window.
* @return The window context from specific LVGL display window.
* @remark This is a private API which is used for LVGL Windows backend
* implementation. LVGL users shouldn't use that because the
* maintainer doesn't promise the application binary interface
* compatibility for this API.
*/
lv_windows_window_context_t * lv_windows_get_window_context(
HWND window_handle);
/**********************
* MACROS
**********************/
#endif // LV_USE_WINDOWS
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_WINDOWS_CONTEXT_H*/

View File

@ -0,0 +1,170 @@
/**
* @file lv_windows_display.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_windows_display.h"
#if LV_USE_WINDOWS
#include "lv_windows_context.h"
#include <process.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static unsigned int __stdcall lv_windows_display_thread_entrypoint(
void * parameter);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_display_t * lv_windows_create_display(
const wchar_t * title,
int32_t hor_res,
int32_t ver_res,
int32_t zoom_level,
bool allow_dpi_override,
bool simulator_mode)
{
lv_windows_create_display_data_t data;
lv_memzero(&data, sizeof(lv_windows_create_display_data_t));
data.title = title;
data.hor_res = hor_res;
data.ver_res = ver_res;
data.zoom_level = zoom_level;
data.allow_dpi_override = allow_dpi_override;
data.simulator_mode = simulator_mode;
data.mutex = CreateEventExW(NULL, NULL, 0, EVENT_ALL_ACCESS);
data.display = NULL;
if(!data.mutex) {
return NULL;
}
HANDLE thread = (HANDLE)_beginthreadex(
NULL,
0,
lv_windows_display_thread_entrypoint,
&data,
0,
NULL);
LV_ASSERT_NULL(thread);
WaitForSingleObjectEx(data.mutex, INFINITE, FALSE);
if(thread) {
CloseHandle(thread);
}
if(data.mutex) {
CloseHandle(data.mutex);
}
return data.display;
}
HWND lv_windows_get_display_window_handle(lv_display_t * display)
{
return (HWND)lv_display_get_driver_data(display);
}
int32_t lv_windows_zoom_to_logical(int32_t physical, int32_t zoom_level)
{
return MulDiv(physical, LV_WINDOWS_ZOOM_BASE_LEVEL, zoom_level);
}
int32_t lv_windows_zoom_to_physical(int32_t logical, int32_t zoom_level)
{
return MulDiv(logical, zoom_level, LV_WINDOWS_ZOOM_BASE_LEVEL);
}
int32_t lv_windows_dpi_to_logical(int32_t physical, int32_t dpi)
{
return MulDiv(physical, USER_DEFAULT_SCREEN_DPI, dpi);
}
int32_t lv_windows_dpi_to_physical(int32_t logical, int32_t dpi)
{
return MulDiv(logical, dpi, USER_DEFAULT_SCREEN_DPI);
}
/**********************
* STATIC FUNCTIONS
**********************/
static unsigned int __stdcall lv_windows_display_thread_entrypoint(
void * parameter)
{
lv_windows_create_display_data_t * data = parameter;
LV_ASSERT_NULL(data);
DWORD window_style = WS_OVERLAPPEDWINDOW;
if(data->simulator_mode) {
window_style &= ~(WS_SIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME);
}
HWND window_handle = CreateWindowExW(
WS_EX_CLIENTEDGE,
L"LVGL.Window",
data->title,
window_style,
CW_USEDEFAULT,
0,
data->hor_res,
data->ver_res,
NULL,
NULL,
NULL,
data);
if(!window_handle) {
return 0;
}
lv_windows_window_context_t * context = lv_windows_get_window_context(
window_handle);
if(!context) {
return 0;
}
data->display = context->display_device_object;
ShowWindow(window_handle, SW_SHOW);
UpdateWindow(window_handle);
LV_ASSERT_NULL(SetEvent(data->mutex));
data = NULL;
MSG message;
while(GetMessageW(&message, NULL, 0, 0)) {
TranslateMessage(&message);
DispatchMessageW(&message);
}
return 0;
}
#endif // LV_USE_WINDOWS

View File

@ -0,0 +1,127 @@
/**
* @file lv_windows_display.h
*
*/
#ifndef LV_WINDOWS_DISPLAY_H
#define LV_WINDOWS_DISPLAY_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../display/lv_display.h"
#include "../../indev/lv_indev.h"
#if LV_USE_WINDOWS
#include <windows.h>
/*********************
* DEFINES
*********************/
#define LV_WINDOWS_ZOOM_BASE_LEVEL 100
#ifndef USER_DEFAULT_SCREEN_DPI
#define USER_DEFAULT_SCREEN_DPI 96
#endif
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* @brief Create a LVGL display object.
* @param title The window title of LVGL display.
* @param hor_res The horizontal resolution value of LVGL display.
* @param ver_res The vertical resolution value of LVGL display.
* @param zoom_level The zoom level value. Base value is 100 a.k.a 100%.
* @param allow_dpi_override Allow DPI override if true, or follow the
* Windows DPI scaling setting dynamically.
* @param simulator_mode Create simulator mode display if true, or create
* application mode display.
* @return The created LVGL display object.
*/
lv_display_t * lv_windows_create_display(
const wchar_t * title,
int32_t hor_res,
int32_t ver_res,
int32_t zoom_level,
bool allow_dpi_override,
bool simulator_mode);
/**
* @brief Get the window handle from specific LVGL display object.
* @param display The specific LVGL display object.
* @return The window handle from specific LVGL display object.
*/
HWND lv_windows_get_display_window_handle(lv_display_t * display);
/**
* @brief Get logical pixel value from physical pixel value taken account
* with zoom level.
* @param physical The physical pixel value taken account with zoom level.
* @param zoom_level The zoom level value. Base value is 100 a.k.a 100%.
* @return The logical pixel value.
* @remark It uses the same calculation style as Windows OS implementation.
* It will be useful for integrate LVGL Windows backend to other
* Windows applications.
*/
int32_t lv_windows_zoom_to_logical(int32_t physical, int32_t zoom_level);
/**
* @brief Get physical pixel value taken account with zoom level from
* logical pixel value.
* @param logical The logical pixel value.
* @param zoom_level The zoom level value. Base value is 100 a.k.a 100%.
* @return The physical pixel value taken account with zoom level.
* @remark It uses the same calculation style as Windows OS implementation.
* It will be useful for integrate LVGL Windows backend to other
* Windows applications.
*/
int32_t lv_windows_zoom_to_physical(int32_t logical, int32_t zoom_level);
/**
* @brief Get logical pixel value from physical pixel value taken account
* with DPI scaling.
* @param physical The physical pixel value taken account with DPI scaling.
* @param dpi The DPI scaling value. Base value is USER_DEFAULT_SCREEN_DPI.
* @return The logical pixel value.
* @remark It uses the same calculation style as Windows OS implementation.
* It will be useful for integrate LVGL Windows backend to other
* Windows applications.
*/
int32_t lv_windows_dpi_to_logical(int32_t physical, int32_t dpi);
/**
* @brief Get physical pixel value taken account with DPI scaling from
* logical pixel value.
* @param logical The logical pixel value.
* @param dpi The DPI scaling value. Base value is USER_DEFAULT_SCREEN_DPI.
* @return The physical pixel value taken account with DPI scaling.
* @remark It uses the same calculation style as Windows OS implementation.
* It will be useful for integrate LVGL Windows backend to other
* Windows applications.
*/
int32_t lv_windows_dpi_to_physical(int32_t logical, int32_t dpi);
/**********************
* MACROS
**********************/
#endif // LV_USE_WINDOWS
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_WINDOWS_DISPLAY_H*/

View File

@ -0,0 +1,749 @@
/**
* @file lv_windows_input.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_windows_input.h"
#if LV_USE_WINDOWS
#include "lv_windows_context.h"
#include "lv_windows_display.h"
#include <windowsx.h>
#if _MSC_VER >= 1200
#pragma comment(lib, "Imm32.lib")
#endif
#include "../../widgets/textarea/lv_textarea.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) {
InitializeCriticalSection(&context->keypad.mutex);
_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;
}
EnterCriticalSection(&context->keypad.mutex);
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;
}
LeaveCriticalSection(&context->keypad.mutex);
}
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;
}
DeleteCriticalSection(&context->keypad.mutex);
_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;
}
}
bool lv_windows_keypad_device_window_message_handler(
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam,
LRESULT * plResult)
{
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) {
EnterCriticalSection(&context->keypad.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_windows_push_key_to_keyboard_queue(
context,
translated_key,
((uMsg == WM_KEYUP)
? LV_INDEV_STATE_RELEASED
: LV_INDEV_STATE_PRESSED));
}
LeaveCriticalSection(&context->keypad.mutex);
}
break;
}
case WM_CHAR: {
lv_windows_window_context_t * context = (lv_windows_window_context_t *)(
lv_windows_get_window_context(hWnd));
if(context) {
EnterCriticalSection(&context->keypad.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->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);
}
LeaveCriticalSection(&context->keypad.mutex);
}
break;
}
case WM_IME_SETCONTEXT: {
if(wParam == TRUE) {
HIMC input_method_context_handle = ImmGetContext(hWnd);
if(input_method_context_handle) {
ImmAssociateContext(hWnd, input_method_context_handle);
ImmReleaseContext(hWnd, input_method_context_handle);
}
}
*plResult = DefWindowProcW(hWnd, uMsg, wParam, wParam);
break;
}
case WM_IME_STARTCOMPOSITION: {
HIMC input_method_context_handle = ImmGetContext(hWnd);
if(input_method_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;
}
ImmSetCompositionWindow(
input_method_context_handle,
&composition_form);
ImmReleaseContext(
hWnd,
input_method_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)
{
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

View File

@ -0,0 +1,83 @@
/**
* @file lv_windows_input.h
*
*/
#ifndef LV_WINDOWS_INPUT_H
#define LV_WINDOWS_INPUT_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../display/lv_display.h"
#include "../../indev/lv_indev.h"
#if LV_USE_WINDOWS
#include <windows.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* @brief Get the window handle from specific LVGL input device object.
* @param indev The specific LVGL input device object.
* @return The window handle from specific LVGL input device object.
*/
HWND lv_windows_get_indev_window_handle(lv_indev_t * indev);
/**
* @brief Open a LVGL pointer input device object for the specific LVGL
* display object, or create it if the LVGL pointer input device
* object is not created or removed before.
* @param display The specific LVGL display object.
* @return The LVGL pointer input device object for the specific LVGL
* display object.
*/
lv_indev_t * lv_windows_acquire_pointer_indev(lv_display_t * display);
/**
* @brief Open a LVGL keypad input device object for the specific LVGL
* display object, or create it if the LVGL keypad input device
* object is not created or removed before.
* @param display The specific LVGL display object.
* @return The LVGL keypad input device object for the specific LVGL
* display object.
*/
lv_indev_t * lv_windows_acquire_keypad_indev(lv_display_t * display);
/**
* @brief Open a LVGL encoder input device object for the specific LVGL
* display object, or create it if the LVGL encoder input device
* object is not created or removed before.
* @param display The specific LVGL display object.
* @return The LVGL encoder input device object for the specific LVGL
* display object.
*/
lv_indev_t * lv_windows_acquire_encoder_indev(lv_display_t * display);
/**********************
* MACROS
**********************/
#endif // LV_USE_WINDOWS
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_WINDOWS_INPUT_H*/

View File

@ -2937,6 +2937,14 @@
#endif
#endif
/* LVGL Windows backend */
#ifndef LV_USE_WINDOWS
#ifdef CONFIG_LV_USE_WINDOWS
#define LV_USE_WINDOWS CONFIG_LV_USE_WINDOWS
#else
#define LV_USE_WINDOWS 0
#endif
#endif
/*==================
* EXAMPLES

View File

@ -39,6 +39,9 @@
#if LV_USE_DRAW_VG_LITE
#include "draw/vg_lite/lv_draw_vg_lite.h"
#endif
#if LV_USE_WINDOWS
#include "src/dev/windows/lv_windows_context.h"
#endif
/*********************
* DEFINES
@ -185,6 +188,10 @@ void lv_init(void)
lv_draw_sdl_init();
#endif
#if LV_USE_WINDOWS
lv_windows_platform_init();
#endif
_lv_obj_style_init();
/*Initialize the screen refresh system*/