Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
码匠许师傅
rt-thread
提交
e72eb1ae
R
rt-thread
项目概览
码匠许师傅
/
rt-thread
与 Fork 源项目一致
Fork自
RT-Thread / rt-thread
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
R
rt-thread
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
e72eb1ae
编写于
12月 15, 2021
作者:
mysterywolf
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
[simulator][win32] add lvgl driver
上级
a8796abd
变更
12
隐藏空白更改
内联
并排
Showing
12 changed file
with
1288 addition
and
3 deletion
+1288
-3
bsp/qemu-vexpress-a9/drivers/lvgl/lv_demo.c
bsp/qemu-vexpress-a9/drivers/lvgl/lv_demo.c
+0
-1
bsp/simulator/Kconfig
bsp/simulator/Kconfig
+17
-2
bsp/simulator/drivers/SConscript
bsp/simulator/drivers/SConscript
+6
-0
bsp/simulator/drivers/lvgl/SConscript
bsp/simulator/drivers/lvgl/SConscript
+17
-0
bsp/simulator/drivers/lvgl/lv_conf.h
bsp/simulator/drivers/lvgl/lv_conf.h
+30
-0
bsp/simulator/drivers/lvgl/lv_demo.c
bsp/simulator/drivers/lvgl/lv_demo.c
+68
-0
bsp/simulator/drivers/lvgl/lv_port_disp.c
bsp/simulator/drivers/lvgl/lv_port_disp.c
+14
-0
bsp/simulator/drivers/lvgl/lv_port_disp.h
bsp/simulator/drivers/lvgl/lv_port_disp.h
+25
-0
bsp/simulator/drivers/lvgl/lv_port_indev.c
bsp/simulator/drivers/lvgl/lv_port_indev.c
+14
-0
bsp/simulator/drivers/lvgl/lv_port_indev.h
bsp/simulator/drivers/lvgl/lv_port_indev.h
+23
-0
bsp/simulator/drivers/lvgl/win32drv.c
bsp/simulator/drivers/lvgl/win32drv.c
+1007
-0
bsp/simulator/drivers/lvgl/win32drv.h
bsp/simulator/drivers/lvgl/win32drv.h
+67
-0
未找到文件。
bsp/qemu-vexpress-a9/drivers/lvgl/lv_demo.c
浏览文件 @
e72eb1ae
...
...
@@ -9,7 +9,6 @@
*/
#include <rtthread.h>
#include <lvgl.h>
#include <lv_port_indev.h>
#define DBG_TAG "LVGL.demo"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
...
...
bsp/simulator/Kconfig
浏览文件 @
e72eb1ae
...
...
@@ -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
bsp/simulator/drivers/SConscript
浏览文件 @
e72eb1ae
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'
)
bsp/simulator/drivers/lvgl/SConscript
0 → 100644
浏览文件 @
e72eb1ae
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'
)
bsp/simulator/drivers/lvgl/lv_conf.h
0 → 100644
浏览文件 @
e72eb1ae
/*
* 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 <rtconfig.h>
#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
bsp/simulator/drivers/lvgl/lv_demo.c
0 → 100644
浏览文件 @
e72eb1ae
/*
* 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 <rtthread.h>
#define DBG_TAG "LVGL.demo"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#include <lvgl.h>
#include <lv_port_disp.h>
#include <win32drv.h>
#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
);
bsp/simulator/drivers/lvgl/lv_port_disp.c
0 → 100644
浏览文件 @
e72eb1ae
/*
* 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*/
}
bsp/simulator/drivers/lvgl/lv_port_disp.h
0 → 100644
浏览文件 @
e72eb1ae
/*
* 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 <win32drv.h>
void
lv_port_disp_init
(
void
);
#ifdef __cplusplus
}
/*extern "C"*/
#endif
#endif
bsp/simulator/drivers/lvgl/lv_port_indev.c
0 → 100644
浏览文件 @
e72eb1ae
/*
* 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 */
}
bsp/simulator/drivers/lvgl/lv_port_indev.h
0 → 100644
浏览文件 @
e72eb1ae
/*
* 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
bsp/simulator/drivers/lvgl/win32drv.c
0 → 100644
浏览文件 @
e72eb1ae
/**
* @file win32drv.c
*
*/
/*********************
* INCLUDES
*********************/
#include "win32drv.h"
#if USE_WIN32DRV
#include <windowsx.h>
#include <stdbool.h>
#include <stdint.h>
/*********************
* 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*/
bsp/simulator/drivers/lvgl/win32drv.h
0 → 100644
浏览文件 @
e72eb1ae
/**
* @file win32drv.h
*
*/
#ifndef LV_WIN32DRV_H
#define LV_WIN32DRV_H
/*********************
* INCLUDES
*********************/
#include <lvgl.h>
#if USE_WIN32DRV
#include <Windows.h>
#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*/
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录