diff --git a/bsp/qemu-vexpress-a9/drivers/lvgl/lv_demo.c b/bsp/qemu-vexpress-a9/drivers/lvgl/lv_demo.c index 17d01e09cc..44288c23f2 100644 --- a/bsp/qemu-vexpress-a9/drivers/lvgl/lv_demo.c +++ b/bsp/qemu-vexpress-a9/drivers/lvgl/lv_demo.c @@ -9,7 +9,6 @@ */ #include #include -#include #define DBG_TAG "LVGL.demo" #define DBG_LVL DBG_INFO #include diff --git a/bsp/simulator/Kconfig b/bsp/simulator/Kconfig index c5e4618d76..2222df6c93 100644 --- a/bsp/simulator/Kconfig +++ b/bsp/simulator/Kconfig @@ -26,8 +26,23 @@ config SOC_SIMULATOR if RT_USING_DFS config RT_USING_DFS_WINSHAREDIR - bool "Enable shared file system between windows" - default n + bool "Enable shared file system between windows" + default n endif +config BSP_USING_LVGL + bool "Enable LVGL for LCD" + select PKG_USING_LVGL + select PKG_USING_LV_MUSIC_DEMO + default n +if BSP_USING_LVGL + config BSP_LCD_WIDTH + int "LCD width" + default 800 + + config BSP_LCD_HEIGHT + int "LCD height" + default 480 + +endif diff --git a/bsp/simulator/drivers/SConscript b/bsp/simulator/drivers/SConscript index 87a79bd4c7..d5a82f93b7 100644 --- a/bsp/simulator/drivers/SConscript +++ b/bsp/simulator/drivers/SConscript @@ -1,4 +1,5 @@ import sys +import os from building import * cwd = GetCurrentDir() @@ -34,4 +35,9 @@ if sys.platform[0:5]=="linux": #check whether under linux group = DefineGroup('Drivers', src, depend = [''], CPPPATH = CPPPATH, LIBS=LIBS, LIBPATH=LIBPATH, CPPDEFINES = CPPDEFINES) +list = os.listdir(cwd) +for item in list: + if os.path.isfile(os.path.join(cwd, item, 'SConscript')): + group = group + SConscript(os.path.join(item, 'SConscript')) + Return('group') diff --git a/bsp/simulator/drivers/lvgl/SConscript b/bsp/simulator/drivers/lvgl/SConscript new file mode 100644 index 0000000000..5952cb2e9a --- /dev/null +++ b/bsp/simulator/drivers/lvgl/SConscript @@ -0,0 +1,17 @@ +from building import * +import os + +cwd = GetCurrentDir() +group = [] +src = Glob('*.c') +CPPPATH = [cwd] + +list = os.listdir(cwd) +for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + group = group + SConscript(os.path.join(d, 'SConscript')) + +group += DefineGroup('LVGL-port', src, depend = ['BSP_USING_LVGL'], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/simulator/drivers/lvgl/lv_conf.h b/bsp/simulator/drivers/lvgl/lv_conf.h new file mode 100644 index 0000000000..ee05e7a8a2 --- /dev/null +++ b/bsp/simulator/drivers/lvgl/lv_conf.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-10-18 Meco Man First version + */ + +#ifndef LV_CONF_H +#define LV_CONF_H + +#define LV_USE_PERF_MONITOR 1 +#define LV_COLOR_DEPTH 32 + +# define USE_WIN32DRV 1 +# define WIN32DRV_MONITOR_ZOOM 1 + +/* music player demo */ +#include +#define LV_DISP_DEF_REFR_PERIOD 10 +#define LV_HOR_RES_MAX BSP_LCD_WIDTH +#define LV_VER_RES_MAX BSP_LCD_HEIGHT +#define LV_USE_DEMO_RTT_MUSIC 1 +#define LV_DEMO_RTT_MUSIC_AUTO_PLAY 1 +#define LV_FONT_MONTSERRAT_12 1 +#define LV_FONT_MONTSERRAT_16 1 + +#endif diff --git a/bsp/simulator/drivers/lvgl/lv_demo.c b/bsp/simulator/drivers/lvgl/lv_demo.c new file mode 100644 index 0000000000..c66dd228e7 --- /dev/null +++ b/bsp/simulator/drivers/lvgl/lv_demo.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-10-17 Meco Man First version + */ +#include +#define DBG_TAG "LVGL.demo" +#define DBG_LVL DBG_INFO +#include +#include +#include +#include + +#define IDI_LVGL 101 + +#ifndef LV_THREAD_STACK_SIZE +#define LV_THREAD_STACK_SIZE 4096 +#endif + +#ifndef LV_THREAD_PRIO +#define LV_THREAD_PRIO (RT_THREAD_PRIORITY_MAX*2/3) +#endif + +static void lvgl_thread(void *parameter) +{ + /* initialize win32 driver; don't put this in lv_port_disp() */ + if (!lv_win32_init( + GetModuleHandleW(NULL), + SW_SHOW, + BSP_LCD_WIDTH, + BSP_LCD_HEIGHT, + LoadIconW(GetModuleHandleW(NULL), MAKEINTRESOURCE(IDI_LVGL)))) + { + LOG_E("lv_win32_init failure!"); + return; + } + + lv_win32_add_all_input_devices_to_group(NULL); + + /* display demo */ + extern void lv_demo_music(void); + lv_demo_music(); + + while (!lv_win32_quit_signal) + { + lv_task_handler(); + Sleep(1); + } +} + +static int lvgl_demo_init(void) +{ + rt_thread_t tid; + + tid = rt_thread_create("LVGL", lvgl_thread, RT_NULL, LV_THREAD_STACK_SIZE, LV_THREAD_PRIO, 10); + if(tid == RT_NULL) + { + LOG_E("Fail to create 'LVGL' thread"); + } + rt_thread_startup(tid); + + return 0; +} +INIT_APP_EXPORT(lvgl_demo_init); diff --git a/bsp/simulator/drivers/lvgl/lv_port_disp.c b/bsp/simulator/drivers/lvgl/lv_port_disp.c new file mode 100644 index 0000000000..220966f631 --- /dev/null +++ b/bsp/simulator/drivers/lvgl/lv_port_disp.c @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-10-18 Meco Man The first version + */ + +void lv_port_disp_init(void) +{ + /* do nothing*/ +} diff --git a/bsp/simulator/drivers/lvgl/lv_port_disp.h b/bsp/simulator/drivers/lvgl/lv_port_disp.h new file mode 100644 index 0000000000..9bcefe5260 --- /dev/null +++ b/bsp/simulator/drivers/lvgl/lv_port_disp.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-10-18 Meco Man The first version + */ +#ifndef LV_PORT_DISP_H +#define LV_PORT_DISP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +void lv_port_disp_init(void); + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif diff --git a/bsp/simulator/drivers/lvgl/lv_port_indev.c b/bsp/simulator/drivers/lvgl/lv_port_indev.c new file mode 100644 index 0000000000..882f10fa85 --- /dev/null +++ b/bsp/simulator/drivers/lvgl/lv_port_indev.c @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-10-18 Meco Man The first version + */ + +void lv_port_indev_init(void) +{ + /* do nothing */ +} diff --git a/bsp/simulator/drivers/lvgl/lv_port_indev.h b/bsp/simulator/drivers/lvgl/lv_port_indev.h new file mode 100644 index 0000000000..7599bb899d --- /dev/null +++ b/bsp/simulator/drivers/lvgl/lv_port_indev.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-10-18 Meco Man The first version + */ +#ifndef LV_PORT_INDEV_H +#define LV_PORT_INDEV_H + +#ifdef __cplusplus +extern "C" { +#endif + +void lv_port_indev_init(void); + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif diff --git a/bsp/simulator/drivers/lvgl/win32drv.c b/bsp/simulator/drivers/lvgl/win32drv.c new file mode 100644 index 0000000000..b37ec99f7d --- /dev/null +++ b/bsp/simulator/drivers/lvgl/win32drv.c @@ -0,0 +1,1007 @@ +/** + * @file win32drv.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "win32drv.h" + +#if USE_WIN32DRV + +#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 + **********************/ + +/********************** + * 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_display_driver_rounder_callback( + lv_disp_drv_t* disp_drv, + lv_area_t* area); + +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 LRESULT CALLBACK lv_win32_window_message_callback( + HWND hWnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam); + +static void lv_win32_message_handler( + lv_timer_t* param); + +/********************** + * 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 HINSTANCE g_instance_handle = NULL; +static HWND g_window_handle = NULL; + +static HDC g_buffer_dc_handle = NULL; +static UINT32* g_pixel_buffer = NULL; +static SIZE_T g_pixel_buffer_size = 0; + +static lv_disp_t* g_display = NULL; + +static bool volatile g_mouse_pressed = false; +static LPARAM volatile g_mouse_value = 0; + +static bool volatile g_mousewheel_pressed = false; +static int16_t volatile g_mousewheel_value = 0; + +static bool volatile g_keyboard_pressed = false; +static WPARAM volatile g_keyboard_value = 0; + +static int volatile g_dpi_value = USER_DEFAULT_SCREEN_DPI; + +/********************** + * 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 bool lv_win32_init( + HINSTANCE instance_handle, + int show_window_mode, + lv_coord_t hor_res, + lv_coord_t ver_res, + HICON icon_handle) +{ + WNDCLASSEXW WindowClass; + + WindowClass.cbSize = sizeof(WNDCLASSEX); + + WindowClass.style = 0; + WindowClass.lpfnWndProc = lv_win32_window_message_callback; + WindowClass.cbClsExtra = 0; + WindowClass.cbWndExtra = 0; + WindowClass.hInstance = instance_handle; + WindowClass.hIcon = icon_handle; + WindowClass.hCursor = LoadCursorW(NULL, IDC_ARROW); + WindowClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + WindowClass.lpszMenuName = NULL; + WindowClass.lpszClassName = L"lv_sim_visual_studio"; + WindowClass.hIconSm = icon_handle; + + if (!RegisterClassExW(&WindowClass)) + { + return false; + } + + g_instance_handle = instance_handle; + + g_window_handle = CreateWindowExW( + WINDOW_EX_STYLE, + WindowClass.lpszClassName, + L"LVGL Simulator for Windows Desktop", + WINDOW_STYLE, + CW_USEDEFAULT, + 0, + CW_USEDEFAULT, + 0, + NULL, + NULL, + instance_handle, + NULL); + + if (!g_window_handle) + { + return false; + } + + g_dpi_value = lv_win32_get_dpi_for_window(g_window_handle); + + RECT WindowSize; + + WindowSize.left = 0; + WindowSize.right = MulDiv( + hor_res * WIN32DRV_MONITOR_ZOOM, + g_dpi_value, + USER_DEFAULT_SCREEN_DPI); + WindowSize.top = 0; + WindowSize.bottom = MulDiv( + ver_res * WIN32DRV_MONITOR_ZOOM, + g_dpi_value, + USER_DEFAULT_SCREEN_DPI); + + AdjustWindowRectEx( + &WindowSize, + WINDOW_STYLE, + FALSE, + WINDOW_EX_STYLE); + OffsetRect( + &WindowSize, + -WindowSize.left, + -WindowSize.top); + + SetWindowPos( + g_window_handle, + NULL, + 0, + 0, + WindowSize.right, + WindowSize.bottom, + SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE); + + lv_win32_register_touch_window(g_window_handle, 0); + + lv_timer_create(lv_win32_message_handler, 0, NULL); + + lv_win32_enable_child_window_dpi_message(g_window_handle); + + HDC hNewBufferDC = lv_win32_create_frame_buffer( + g_window_handle, + hor_res, + ver_res, + &g_pixel_buffer, + &g_pixel_buffer_size); + + DeleteDC(g_buffer_dc_handle); + g_buffer_dc_handle = hNewBufferDC; + + static lv_disp_draw_buf_t display_buffer; + lv_disp_draw_buf_init( + &display_buffer, + (lv_color_t*)malloc(hor_res * ver_res * sizeof(lv_color_t)), + NULL, + hor_res * ver_res); + + static lv_disp_drv_t display_driver; + lv_disp_drv_init(&display_driver); + display_driver.hor_res = hor_res; + display_driver.ver_res = ver_res; + display_driver.flush_cb = lv_win32_display_driver_flush_callback; + display_driver.draw_buf = &display_buffer; + display_driver.rounder_cb = lv_win32_display_driver_rounder_callback; + g_display = lv_disp_drv_register(&display_driver); + + static lv_indev_drv_t pointer_driver; + lv_indev_drv_init(&pointer_driver); + pointer_driver.type = LV_INDEV_TYPE_POINTER; + pointer_driver.read_cb = lv_win32_pointer_driver_read_callback; + lv_win32_pointer_device_object = lv_indev_drv_register(&pointer_driver); + + static lv_indev_drv_t keypad_driver; + lv_indev_drv_init(&keypad_driver); + keypad_driver.type = LV_INDEV_TYPE_KEYPAD; + keypad_driver.read_cb = lv_win32_keypad_driver_read_callback; + lv_win32_keypad_device_object = lv_indev_drv_register(&keypad_driver); + + static lv_indev_drv_t encoder_driver; + lv_indev_drv_init(&encoder_driver); + encoder_driver.type = LV_INDEV_TYPE_ENCODER; + encoder_driver.read_cb = lv_win32_encoder_driver_read_callback; + lv_win32_encoder_device_object = lv_indev_drv_register(&encoder_driver); + + ShowWindow(g_window_handle, show_window_mode); + UpdateWindow(g_window_handle); + + 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) +{ +#if (LV_COLOR_DEPTH == 32) || \ + (LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP == 0) || \ + (LV_COLOR_DEPTH == 8) || \ + (LV_COLOR_DEPTH == 1) + UNREFERENCED_PARAMETER(area); + memcpy(g_pixel_buffer, color_p, g_pixel_buffer_size); +#elif (LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP != 0) + SIZE_T count = g_pixel_buffer_size / sizeof(UINT16); + PUINT16 source = (PUINT16)color_p; + PUINT16 destination = (PUINT16)g_pixel_buffer; + for (SIZE_T i = 0; i < count; ++i) + { + UINT16 current = *source; + *destination = (LOBYTE(current) << 8) | HIBYTE(current); + + ++source; + ++destination; + } +#else + for (int y = area->y1; y <= area->y2; ++y) + { + for (int x = area->x1; x <= area->x2; ++x) + { + g_pixel_buffer[y * disp_drv->hor_res + x] = lv_color_to32(*color_p); + color_p++; + } + } +#endif + + HDC hWindowDC = GetDC(g_window_handle); + if (hWindowDC) + { + int PreviousMode = SetStretchBltMode( + hWindowDC, + HALFTONE); + + StretchBlt( + hWindowDC, + 0, + 0, + MulDiv( + disp_drv->hor_res * WIN32DRV_MONITOR_ZOOM, + g_dpi_value, + USER_DEFAULT_SCREEN_DPI), + MulDiv( + disp_drv->ver_res * WIN32DRV_MONITOR_ZOOM, + g_dpi_value, + USER_DEFAULT_SCREEN_DPI), + g_buffer_dc_handle, + 0, + 0, + disp_drv->hor_res, + disp_drv->ver_res, + SRCCOPY); + + SetStretchBltMode( + hWindowDC, + PreviousMode); + + ReleaseDC(g_window_handle, hWindowDC); + } + + lv_disp_flush_ready(disp_drv); +} + +static void lv_win32_display_driver_rounder_callback( + lv_disp_drv_t* disp_drv, + lv_area_t* area) +{ + area->x1 = 0; + area->x2 = disp_drv->hor_res - 1; + area->y1 = 0; + area->y2 = disp_drv->ver_res - 1; +} + +static void lv_win32_pointer_driver_read_callback( + lv_indev_drv_t* indev_drv, + lv_indev_data_t* data) +{ + UNREFERENCED_PARAMETER(indev_drv); + + data->state = (lv_indev_state_t)( + g_mouse_pressed ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL); + + data->point.x = MulDiv( + GET_X_LPARAM(g_mouse_value), + USER_DEFAULT_SCREEN_DPI, + WIN32DRV_MONITOR_ZOOM * g_dpi_value); + data->point.y = MulDiv( + GET_Y_LPARAM(g_mouse_value), + USER_DEFAULT_SCREEN_DPI, + WIN32DRV_MONITOR_ZOOM * g_dpi_value); + + if (data->point.x < 0) + { + data->point.x = 0; + } + if (data->point.x > g_display->driver->hor_res - 1) + { + data->point.x = g_display->driver->hor_res - 1; + } + if (data->point.y < 0) + { + data->point.y = 0; + } + if (data->point.y > g_display->driver->ver_res - 1) + { + data->point.y = g_display->driver->ver_res - 1; + } +} + +static void lv_win32_keypad_driver_read_callback( + lv_indev_drv_t* indev_drv, + lv_indev_data_t* data) +{ + UNREFERENCED_PARAMETER(indev_drv); + + data->state = (lv_indev_state_t)( + g_keyboard_pressed ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL); + + WPARAM KeyboardValue = g_keyboard_value; + + switch (KeyboardValue) + { + case VK_UP: + data->key = LV_KEY_UP; + break; + case VK_DOWN: + data->key = LV_KEY_DOWN; + break; + case VK_LEFT: + data->key = LV_KEY_LEFT; + break; + case VK_RIGHT: + data->key = LV_KEY_RIGHT; + break; + case VK_ESCAPE: + data->key = LV_KEY_ESC; + break; + case VK_DELETE: + data->key = LV_KEY_DEL; + break; + case VK_BACK: + data->key = LV_KEY_BACKSPACE; + break; + case VK_RETURN: + data->key = LV_KEY_ENTER; + break; + case VK_NEXT: + data->key = LV_KEY_NEXT; + break; + case VK_PRIOR: + data->key = LV_KEY_PREV; + break; + case VK_HOME: + data->key = LV_KEY_HOME; + break; + case VK_END: + data->key = LV_KEY_END; + break; + default: + if (KeyboardValue >= 'A' && KeyboardValue <= 'Z') + { + KeyboardValue += 0x20; + } + + data->key = (uint32_t)KeyboardValue; + + break; + } +} + +static void lv_win32_encoder_driver_read_callback( + lv_indev_drv_t* indev_drv, + lv_indev_data_t* data) +{ + UNREFERENCED_PARAMETER(indev_drv); + + data->state = (lv_indev_state_t)( + g_mousewheel_pressed ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL); + data->enc_diff = g_mousewheel_value; + g_mousewheel_value = 0; +} + +static LRESULT CALLBACK lv_win32_window_message_callback( + HWND hWnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam) +{ + switch (uMsg) + { + case WM_MOUSEMOVE: + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + { + g_mouse_value = lParam; + if (uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONUP) + { + g_mouse_pressed = (uMsg == WM_LBUTTONDOWN); + } + else if (uMsg == WM_MBUTTONDOWN || uMsg == WM_MBUTTONUP) + { + g_mousewheel_pressed = (uMsg == WM_MBUTTONDOWN); + } + return 0; + } + case WM_KEYDOWN: + case WM_KEYUP: + { + g_keyboard_pressed = (uMsg == WM_KEYDOWN); + g_keyboard_value = wParam; + break; + } + case WM_MOUSEWHEEL: + { + g_mousewheel_value = -(GET_WHEEL_DELTA_WPARAM(wParam) / WHEEL_DELTA); + break; + } + case WM_TOUCH: + { + 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; + } + + uint16_t x = (uint16_t)(Point.x & 0xffff); + uint16_t y = (uint16_t)(Point.y & 0xffff); + + DWORD MousePressedMask = + TOUCHEVENTF_MOVE | TOUCHEVENTF_DOWN; + + g_mouse_value = (y << 16) | x; + g_mouse_pressed = (pInputs[i].dwFlags & MousePressedMask); + } + } + + free(pInputs); + } + + lv_win32_close_touch_input_handle(hTouchInput); + + break; + } + case WM_DPICHANGED: + { + g_dpi_value = 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( + g_display->driver->hor_res * WIN32DRV_MONITOR_ZOOM, + g_dpi_value, + USER_DEFAULT_SCREEN_DPI); + int WindowHeight = MulDiv( + g_display->driver->ver_res * WIN32DRV_MONITOR_ZOOM, + g_dpi_value, + 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_DESTROY: + PostQuitMessage(0); + break; + default: + return DefWindowProcW(hWnd, uMsg, wParam, lParam); + } + + return 0; +} + +static void lv_win32_message_handler( + lv_timer_t* param) +{ + UNREFERENCED_PARAMETER(param); + + MSG Message; + BOOL Result = PeekMessageW(&Message, NULL, 0, 0, TRUE); + if (Result != 0 && Result != -1) + { + TranslateMessage(&Message); + DispatchMessageW(&Message); + + if (Message.message == WM_QUIT) + { + lv_win32_quit_signal = true; + } + } +} + +#endif /*USE_WIN32DRV*/ diff --git a/bsp/simulator/drivers/lvgl/win32drv.h b/bsp/simulator/drivers/lvgl/win32drv.h new file mode 100644 index 0000000000..8bd5972fb5 --- /dev/null +++ b/bsp/simulator/drivers/lvgl/win32drv.h @@ -0,0 +1,67 @@ +/** + * @file win32drv.h + * + */ + +#ifndef LV_WIN32DRV_H +#define LV_WIN32DRV_H + +/********************* + * INCLUDES + *********************/ + +#include + +#if USE_WIN32DRV + +#include + +#if _MSC_VER >= 1200 + // Disable compilation warnings. +#pragma warning(push) +// nonstandard extension used : bit field types other than int +#pragma warning(disable:4214) +// 'conversion' conversion from 'type1' to 'type2', possible loss of data +#pragma warning(disable:4244) +#endif + +#if _MSC_VER >= 1200 +// Restore compilation warnings. +#pragma warning(pop) +#endif + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +EXTERN_C bool lv_win32_quit_signal; + +EXTERN_C lv_indev_t* lv_win32_pointer_device_object; +EXTERN_C lv_indev_t* lv_win32_keypad_device_object; +EXTERN_C lv_indev_t* lv_win32_encoder_device_object; + +EXTERN_C void lv_win32_add_all_input_devices_to_group( + lv_group_t* group); + +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); + +/********************** + * MACROS + **********************/ + +#endif /*USE_WIN32DRV*/ + +#endif /*LV_WIN32DRV_H*/