LVGL 库提交
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* @file lv_x11.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_X11_H
|
||||
#define LV_X11_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
#include "../../display/lv_display.h"
|
||||
#include "../../indev/lv_indev.h"
|
||||
#include "../../draw/lv_image_dsc.h"
|
||||
|
||||
#if LV_USE_X11
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/** Header of private display driver user data - for internal use only */
|
||||
typedef struct {
|
||||
struct _XDisplay * display; /**< X11 display object */
|
||||
struct _x11_inp_data * inp_data; /**< input user data object */
|
||||
} _x11_user_hdr_t;
|
||||
|
||||
/** optional window close callback function type
|
||||
* @see lv_x11_window_set_close_cb
|
||||
*/
|
||||
typedef void(*lv_x11_close_cb)(void * user_data);
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* create and add keyboard, mouse and scrollwheel objects and connect them to x11 display.
|
||||
*
|
||||
* This is a convenience method handling the typical input initialisation of an X11 window:
|
||||
* - create keyboard (lv_x11_keyboard_create)
|
||||
* - create mouse (with scrollwheel, lv_x11_mouse_create lv_x11_mousewheel_create)
|
||||
*
|
||||
* @param[in] disp the created X11 display object from @ref lv_x11_window_create
|
||||
* @param[in] mouse_img optional image description for the mouse cursor (NULL for no/invisible mouse cursor)
|
||||
*/
|
||||
void lv_x11_inputs_create(lv_display_t * disp, lv_image_dsc_t const * mouse_img);
|
||||
|
||||
/**
|
||||
* create the X11 display
|
||||
*
|
||||
* The minimal initialisation for initializing the X11 display driver with keyboard/mouse support:
|
||||
* @code
|
||||
* lv_display_t* disp = lv_x11_window_create("My Window Title", window_width, window_width);
|
||||
* lv_x11_inputs_create(disp, NULL);
|
||||
* @endcode
|
||||
* or with mouse cursor icon:
|
||||
* @code
|
||||
* lv_image_dsc_t mouse_symbol = {.....};
|
||||
* lv_display_t* disp = lv_x11_window_create("My Window Title", window_width, window_width);
|
||||
* lv_x11_inputs_create(disp, &mouse_symbol);
|
||||
* @endcode
|
||||
*
|
||||
* @param[in] title title of the created X11 window
|
||||
* @param[in] hor_res horizontal resolution (=width) of the X11 window
|
||||
* @param[in] ver_res vertical resolution (=height) of the X11 window
|
||||
* @return pointer to the display object
|
||||
*/
|
||||
lv_display_t * lv_x11_window_create(char const * title, int32_t hor_res, int32_t ver_res);
|
||||
|
||||
#endif /* LV_USE_X11 */
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* LV_X11_H */
|
||||
@@ -0,0 +1,395 @@
|
||||
/**
|
||||
* @file lv_x11_display.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_x11.h"
|
||||
|
||||
#if LV_USE_X11
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include "../../core/lv_obj_pos.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
#define MIN(A, B) ((A) < (B) ? (A) : (B))
|
||||
#define MAX(A, B) ((A) > (B) ? (A) : (B))
|
||||
|
||||
#if LV_X11_RENDER_MODE_PARTIAL
|
||||
#define LV_X11_RENDER_MODE LV_DISPLAY_RENDER_MODE_PARTIAL
|
||||
#elif defined LV_X11_RENDER_MODE_DIRECT
|
||||
#define LV_X11_RENDER_MODE LV_DISPLAY_RENDER_MODE_DIRECT
|
||||
#elif defined LV_X11_RENDER_MODE_FULL
|
||||
#define LV_X11_RENDER_MODE LV_DISPLAY_RENDER_MODE_FULL
|
||||
#endif
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
typedef struct {
|
||||
/* header (containing X Display + input user data pointer - keep aligned with x11_input module!) */
|
||||
_x11_user_hdr_t hdr;
|
||||
/* X11 related information */
|
||||
Window window; /**< X11 window object */
|
||||
GC gc; /**< X11 graphics context object */
|
||||
Visual * visual; /**< X11 visual */
|
||||
int dplanes; /**< X11 display depth */
|
||||
XImage * ximage; /**< X11 XImage cache object for updating window content */
|
||||
Atom wmDeleteMessage; /**< X11 atom to window object */
|
||||
void * xdata; /**< allocated data for XImage */
|
||||
/* LVGL related information */
|
||||
lv_timer_t * timer; /**< timer object for @ref x11_event_handler */
|
||||
uint8_t * buffer[2]; /**< (double) lv display buffers, depending on @ref LV_X11_RENDER_MODE */
|
||||
lv_area_t flush_area; /**< integrated area for a display update */
|
||||
/* systemtick by thread related information */
|
||||
pthread_t thr_tick; /**< pthread for SysTick simulation */
|
||||
bool terminated; /**< flag to germinate SysTick simulation thread */
|
||||
} x11_disp_data_t;
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
#if LV_X11_DIRECT_EXIT
|
||||
static unsigned int count_windows = 0;
|
||||
#endif
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
#if LV_COLOR_DEPTH == 32
|
||||
typedef lv_color32_t color_t;
|
||||
static inline lv_color32_t get_px(color_t p)
|
||||
{
|
||||
return (lv_color32_t)p;
|
||||
}
|
||||
#elif LV_COLOR_DEPTH == 24
|
||||
typedef lv_color_t color_t;
|
||||
static inline lv_color32_t get_px(color_t p)
|
||||
{
|
||||
lv_color32_t out = { .red = p.red, .green = p.green, .blue = p.blue };
|
||||
return out;
|
||||
}
|
||||
#elif LV_COLOR_DEPTH == 16
|
||||
typedef lv_color16_t color_t;
|
||||
static inline lv_color32_t get_px(color_t p)
|
||||
{
|
||||
lv_color32_t out = { .red = p.red << 3, .green = p.green << 2, .blue = p.blue << 3 };
|
||||
return out;
|
||||
}
|
||||
#elif LV_COLOR_DEPTH == 8
|
||||
typedef uint8_t color_t;
|
||||
static inline lv_color32_t get_px(color_t p)
|
||||
{
|
||||
lv_color32_t out = { .red = p, .green = p, .blue = p };
|
||||
return out;
|
||||
}
|
||||
#warning ("LV_COLOR_DEPTH=8 delivers black data only - open issue in lvgl?")
|
||||
#else
|
||||
#error ("Unsupported LV_COLOR_DEPTH")
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Flush the content of the internal buffer the specific area on the display.
|
||||
* @param[in] disp the created X11 display object from @lv_x11_window_create
|
||||
* @param[in] area area to be updated
|
||||
* @param[in] px_map contains the rendered image as raw pixel map and it should be copied to `area` on the display.
|
||||
* @note @ref lv_display_flush_ready has to be called when it's finished.
|
||||
*/
|
||||
static void x11_flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map)
|
||||
{
|
||||
x11_disp_data_t * xd = lv_display_get_driver_data(disp);
|
||||
LV_ASSERT_NULL(xd);
|
||||
|
||||
static const lv_area_t inv_area = { .x1 = 0xFFFF,
|
||||
.x2 = 0,
|
||||
.y1 = 0xFFFF,
|
||||
.y2 = 0
|
||||
};
|
||||
|
||||
/* build display update area until lv_display_flush_is_last */
|
||||
xd->flush_area.x1 = MIN(xd->flush_area.x1, area->x1);
|
||||
xd->flush_area.x2 = MAX(xd->flush_area.x2, area->x2);
|
||||
xd->flush_area.y1 = MIN(xd->flush_area.y1, area->y1);
|
||||
xd->flush_area.y2 = MAX(xd->flush_area.y2, area->y2);
|
||||
|
||||
int32_t hor_res = lv_display_get_horizontal_resolution(disp);
|
||||
|
||||
uint32_t dst_offs;
|
||||
lv_color32_t * dst_data;
|
||||
color_t * src_data = (color_t *)px_map + (LV_X11_RENDER_MODE == LV_DISPLAY_RENDER_MODE_PARTIAL ? 0 : hor_res *
|
||||
area->y1 + area->x1);
|
||||
for(int16_t y = area->y1; y <= area->y2; y++) {
|
||||
dst_offs = area->x1 + y * hor_res;
|
||||
dst_data = &((lv_color32_t *)(xd->xdata))[dst_offs];
|
||||
for(int16_t x = area->x1; x <= area->x2; x++, src_data++, dst_data++) {
|
||||
*dst_data = get_px(*src_data);
|
||||
}
|
||||
src_data += (LV_X11_RENDER_MODE == LV_DISPLAY_RENDER_MODE_PARTIAL ? 0 : hor_res - (area->x2 - area->x1 + 1));
|
||||
}
|
||||
|
||||
if(lv_display_flush_is_last(disp)) {
|
||||
LV_LOG_TRACE("(%d/%d), %dx%d)", xd->flush_area.x1, xd->flush_area.y1, xd->flush_area.x2 + 1 - xd->flush_area.x1,
|
||||
xd->flush_area.y2 + 1 - xd->flush_area.y1);
|
||||
|
||||
/* refresh collected display update area only */
|
||||
int16_t upd_w = xd->flush_area.x2 - xd->flush_area.x1 + 1;
|
||||
int16_t upd_h = xd->flush_area.y2 - xd->flush_area.y1 + 1;
|
||||
XPutImage(xd->hdr.display, xd->window, xd->gc, xd->ximage, xd->flush_area.x1, xd->flush_area.y1, xd->flush_area.x1,
|
||||
xd->flush_area.y1, upd_w, upd_h);
|
||||
|
||||
/* invalidate collected area */
|
||||
xd->flush_area = inv_area;
|
||||
}
|
||||
/* Inform the graphics library that you are ready with the flushing */
|
||||
lv_display_flush_ready(disp);
|
||||
}
|
||||
|
||||
/**
|
||||
* event called by lvgl display if resolution has been changed (@ref lv_display_set_resolution has been called)
|
||||
* @param[in] e event data, containing lv_display_t object
|
||||
*/
|
||||
static void x11_resolution_evt_cb(lv_event_t * e)
|
||||
{
|
||||
lv_display_t * disp = lv_event_get_user_data(e);
|
||||
x11_disp_data_t * xd = lv_display_get_driver_data(disp);
|
||||
LV_ASSERT_NULL(xd);
|
||||
|
||||
int32_t hor_res = lv_display_get_horizontal_resolution(disp);
|
||||
int32_t ver_res = lv_display_get_vertical_resolution(disp);
|
||||
|
||||
if(LV_X11_RENDER_MODE != LV_DISPLAY_RENDER_MODE_PARTIAL) {
|
||||
/* update lvgl full-screen display draw buffers for new display size */
|
||||
int sz_buffers = (hor_res * ver_res * (LV_COLOR_DEPTH + 7) / 8);
|
||||
xd->buffer[0] = realloc(xd->buffer[0], sz_buffers);
|
||||
xd->buffer[1] = (LV_X11_DOUBLE_BUFFER ? realloc(xd->buffer[1], sz_buffers) : NULL);
|
||||
lv_display_set_buffers(disp, xd->buffer[0], xd->buffer[1], sz_buffers, LV_X11_RENDER_MODE);
|
||||
}
|
||||
|
||||
/* re-create cache image with new size */
|
||||
XDestroyImage(xd->ximage);
|
||||
size_t sz_buffers = hor_res * ver_res * sizeof(lv_color32_t);
|
||||
xd->xdata = malloc(sz_buffers); /* use clib method here, x11 memory not part of device footprint */
|
||||
xd->ximage = XCreateImage(xd->hdr.display, xd->visual, xd->dplanes, ZPixmap, 0, xd->xdata,
|
||||
hor_res, ver_res, lv_color_format_get_bpp(LV_COLOR_FORMAT_ARGB8888), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* event called by lvgl display if display has been closed (@ref lv_display_delete has been called)
|
||||
* @param[in] e event data, containing lv_display_t object
|
||||
*/
|
||||
static void x11_disp_delete_evt_cb(lv_event_t * e)
|
||||
{
|
||||
lv_display_t * disp = lv_event_get_user_data(e);
|
||||
x11_disp_data_t * xd = lv_display_get_driver_data(disp);
|
||||
|
||||
lv_timer_delete(xd->timer);
|
||||
|
||||
free(xd->buffer[0]);
|
||||
if(LV_X11_DOUBLE_BUFFER) {
|
||||
free(xd->buffer[1]);
|
||||
}
|
||||
|
||||
XDestroyImage(xd->ximage);
|
||||
XFreeGC(xd->hdr.display, xd->gc);
|
||||
XUnmapWindow(xd->hdr.display, xd->window);
|
||||
XDestroyWindow(xd->hdr.display, xd->window);
|
||||
XFlush(xd->hdr.display);
|
||||
XCloseDisplay(xd->hdr.display);
|
||||
|
||||
lv_free(xd);
|
||||
#if LV_X11_DIRECT_EXIT
|
||||
if(0 == --count_windows) {
|
||||
exit(0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void x11_hide_cursor(lv_display_t * disp)
|
||||
{
|
||||
x11_disp_data_t * xd = lv_display_get_driver_data(disp);
|
||||
LV_ASSERT_NULL(xd);
|
||||
|
||||
XColor black = { .red = 0, .green = 0, .blue = 0 };
|
||||
char empty_data[] = { 0 };
|
||||
|
||||
Pixmap empty_bitmap = XCreateBitmapFromData(xd->hdr.display, xd->window, empty_data, 1, 1);
|
||||
Cursor inv_cursor = XCreatePixmapCursor(xd->hdr.display, empty_bitmap, empty_bitmap, &black, &black, 0, 0);
|
||||
XDefineCursor(xd->hdr.display, xd->window, inv_cursor);
|
||||
XFreeCursor(xd->hdr.display, inv_cursor);
|
||||
XFreePixmap(xd->hdr.display, empty_bitmap);
|
||||
}
|
||||
|
||||
/**
|
||||
* X11 input event handler, predicated to fetch and handle only display related events
|
||||
* (Window changes)
|
||||
*/
|
||||
static int is_disp_event(Display * disp, XEvent * event, XPointer arg)
|
||||
{
|
||||
LV_UNUSED(disp);
|
||||
LV_UNUSED(arg);
|
||||
return (event->type == Expose
|
||||
|| (event->type >= DestroyNotify && event->type <= CirculateNotify) /* events from StructureNotifyMask */
|
||||
|| event->type == ClientMessage);
|
||||
}
|
||||
static void x11_event_handler(lv_timer_t * t)
|
||||
{
|
||||
lv_display_t * disp = lv_timer_get_user_data(t);
|
||||
x11_disp_data_t * xd = lv_display_get_driver_data(disp);
|
||||
LV_ASSERT_NULL(xd);
|
||||
|
||||
/* handle all outstanding X events */
|
||||
XEvent event;
|
||||
while(XCheckIfEvent(xd->hdr.display, &event, is_disp_event, NULL)) {
|
||||
LV_LOG_TRACE("Display Event %d", event.type);
|
||||
switch(event.type) {
|
||||
case Expose:
|
||||
if(event.xexpose.count == 0) {
|
||||
XPutImage(xd->hdr.display, xd->window, xd->gc, xd->ximage, 0, 0, 0, 0, event.xexpose.width, event.xexpose.height);
|
||||
}
|
||||
break;
|
||||
case ConfigureNotify:
|
||||
if(event.xconfigure.width != lv_display_get_horizontal_resolution(disp)
|
||||
|| event.xconfigure.height != lv_display_get_vertical_resolution(disp)) {
|
||||
lv_display_set_resolution(disp, event.xconfigure.width, event.xconfigure.height);
|
||||
}
|
||||
break;
|
||||
case ClientMessage:
|
||||
if(event.xclient.data.l[0] == (long)xd->wmDeleteMessage) {
|
||||
xd->terminated = true;
|
||||
void * ret = NULL;
|
||||
pthread_join(xd->thr_tick, &ret);
|
||||
lv_display_delete(disp);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case MapNotify:
|
||||
case ReparentNotify:
|
||||
/*suppress unhandled warning*/
|
||||
break;
|
||||
default:
|
||||
LV_LOG_WARN("unhandled x11 event: %d", event.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void * x11_tick_thread(void * data)
|
||||
{
|
||||
x11_disp_data_t * xd = data;
|
||||
LV_ASSERT_NULL(xd);
|
||||
|
||||
while(!xd->terminated) {
|
||||
usleep(5000);
|
||||
lv_tick_inc(5);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void x11_window_create(lv_display_t * disp, char const * title)
|
||||
{
|
||||
x11_disp_data_t * xd = lv_display_get_driver_data(disp);
|
||||
LV_ASSERT_NULL(xd);
|
||||
|
||||
/* setup display/screen */
|
||||
xd->hdr.display = XOpenDisplay(NULL);
|
||||
int screen = XDefaultScreen(xd->hdr.display);
|
||||
xd->visual = XDefaultVisual(xd->hdr.display, screen);
|
||||
|
||||
/* create window */
|
||||
int32_t hor_res = lv_display_get_horizontal_resolution(disp);
|
||||
int32_t ver_res = lv_display_get_vertical_resolution(disp);
|
||||
#if 0
|
||||
/* drawing contexts for an window */
|
||||
unsigned long col_fg = BlackPixel(xd->hdr.display, screen);
|
||||
unsigned long col_bg = WhitePixel(xd->hdr.display, screen);
|
||||
|
||||
xd->window = XCreateSimpleWindow(xd->hdr.display, DefaultRootWindow(xd->hdr.display),
|
||||
0, 0, hor_res, ver_res, 0, col_fg, col_bg);
|
||||
#else
|
||||
xd->window = XCreateWindow(xd->hdr.display, XDefaultRootWindow(xd->hdr.display),
|
||||
0, 0, hor_res, ver_res, 0,
|
||||
XDefaultDepth(xd->hdr.display, screen), InputOutput,
|
||||
xd->visual, 0, NULL);
|
||||
#endif
|
||||
/* window manager properties (yes, use of StdProp is obsolete) */
|
||||
XSetStandardProperties(xd->hdr.display, xd->window, title, NULL, None, NULL, 0, NULL);
|
||||
xd->gc = XCreateGC(xd->hdr.display, xd->window, 0, 0);
|
||||
|
||||
/* allow receiving mouse, keyboard and window change/close events */
|
||||
XSelectInput(xd->hdr.display, xd->window,
|
||||
PointerMotionMask | ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask | ExposureMask |
|
||||
StructureNotifyMask);
|
||||
xd->wmDeleteMessage = XInternAtom(xd->hdr.display, "WM_DELETE_WINDOW", False);
|
||||
XSetWMProtocols(xd->hdr.display, xd->window, &xd->wmDeleteMessage, 1);
|
||||
|
||||
x11_hide_cursor(disp);
|
||||
|
||||
/* create cache XImage */
|
||||
size_t sz_buffers = hor_res * ver_res * sizeof(lv_color32_t);
|
||||
xd->dplanes = XDisplayPlanes(xd->hdr.display, screen);
|
||||
xd->xdata = malloc(sz_buffers); /* use clib method here, x11 memory not part of device footprint */
|
||||
xd->ximage = XCreateImage(xd->hdr.display, xd->visual, xd->dplanes, ZPixmap, 0, xd->xdata,
|
||||
hor_res, ver_res, lv_color_format_get_bpp(LV_COLOR_FORMAT_ARGB8888), 0);
|
||||
|
||||
/* finally bring window on top of the other windows */
|
||||
XMapRaised(xd->hdr.display, xd->window);
|
||||
|
||||
#if LV_X11_DIRECT_EXIT
|
||||
count_windows++;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
lv_display_t * lv_x11_window_create(char const * title, int32_t hor_res, int32_t ver_res)
|
||||
{
|
||||
x11_disp_data_t * xd = lv_malloc_zeroed(sizeof(x11_disp_data_t));
|
||||
LV_ASSERT_MALLOC(xd);
|
||||
if(NULL == xd) return NULL;
|
||||
|
||||
lv_display_t * disp = lv_display_create(hor_res, ver_res);
|
||||
if(NULL == disp) {
|
||||
lv_free(xd);
|
||||
return NULL;
|
||||
}
|
||||
lv_display_set_driver_data(disp, xd);
|
||||
lv_display_set_flush_cb(disp, x11_flush_cb);
|
||||
lv_display_add_event_cb(disp, x11_resolution_evt_cb, LV_EVENT_RESOLUTION_CHANGED, disp);
|
||||
lv_display_add_event_cb(disp, x11_disp_delete_evt_cb, LV_EVENT_DELETE, disp);
|
||||
|
||||
x11_window_create(disp, title);
|
||||
|
||||
int sz_buffers = (hor_res * ver_res * (LV_COLOR_DEPTH + 7) / 8);
|
||||
if(LV_X11_RENDER_MODE == LV_DISPLAY_RENDER_MODE_PARTIAL) {
|
||||
sz_buffers /= 10;
|
||||
}
|
||||
xd->buffer[0] = malloc(sz_buffers);
|
||||
xd->buffer[1] = (LV_X11_DOUBLE_BUFFER ? malloc(sz_buffers) : NULL);
|
||||
lv_display_set_buffers(disp, xd->buffer[0], xd->buffer[1], sz_buffers, LV_X11_RENDER_MODE);
|
||||
|
||||
xd->timer = lv_timer_create(x11_event_handler, 5, disp);
|
||||
|
||||
/* initialize Tick simulation */
|
||||
xd->terminated = false;
|
||||
pthread_create(&xd->thr_tick, NULL, x11_tick_thread, xd);
|
||||
|
||||
return disp;
|
||||
}
|
||||
|
||||
#endif /*LV_USE_X11*/
|
||||
@@ -0,0 +1,324 @@
|
||||
/**
|
||||
* @file lv_x11_input.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_x11.h"
|
||||
|
||||
#if LV_USE_X11
|
||||
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include "../../widgets/image/lv_image.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
#define MIN(A, B) ((A) < (B) ? (A) : (B))
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
typedef struct _x11_inp_data {
|
||||
/* LVGL related information */
|
||||
lv_group_t * inp_group; /**< input group for X input elements */
|
||||
lv_indev_t * keyboard; /**< keyboard input device object */
|
||||
lv_indev_t * mousepointer; /**< mouse input device object */
|
||||
lv_indev_t * mousewheel; /**< encoder input device object */
|
||||
lv_timer_t * timer; /**< timer object for @ref x11_event_handler */
|
||||
/* user input related information */
|
||||
char kb_buffer[32]; /**< keyboard buffer for X keyboard inputs */
|
||||
lv_point_t mouse_pos; /**< current reported mouse position */
|
||||
bool left_mouse_btn; /**< current state of left mouse button */
|
||||
bool right_mouse_btn; /**< current state of right mouse button */
|
||||
bool wheel_mouse_btn; /**< current state of wheel (=middle) mouse button */
|
||||
int16_t wheel_cnt; /**< mouse wheel increments */
|
||||
} x11_inp_data_t;
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* X11 input event handler, predicated to fetch and handle only input related events
|
||||
* (MotionNotify, ButtonPress/Release, KeyPress/Release)
|
||||
*/
|
||||
static int is_inp_event(Display * disp, XEvent * event, XPointer arg)
|
||||
{
|
||||
LV_UNUSED(disp);
|
||||
LV_UNUSED(arg);
|
||||
return !(event->type == Expose
|
||||
|| (event->type >= DestroyNotify && event->type <= CirculateNotify) /* events from StructureNotifyMask */
|
||||
|| event->type == ClientMessage);
|
||||
}
|
||||
static void x11_inp_event_handler(lv_timer_t * t)
|
||||
{
|
||||
lv_display_t * disp = lv_timer_get_user_data(t);
|
||||
_x11_user_hdr_t * disp_hdr = lv_display_get_driver_data(disp);
|
||||
x11_inp_data_t * xd = disp_hdr->inp_data;
|
||||
|
||||
/* handle all outstanding X events */
|
||||
XEvent event;
|
||||
while(XCheckIfEvent(disp_hdr->display, &event, is_inp_event, NULL)) {
|
||||
LV_LOG_TRACE("Input Event %d", event.type);
|
||||
switch(event.type) {
|
||||
case MotionNotify:
|
||||
xd->mouse_pos.x = event.xmotion.x;
|
||||
xd->mouse_pos.y = event.xmotion.y;
|
||||
break;
|
||||
case ButtonPress:
|
||||
switch(event.xbutton.button) {
|
||||
case Button1:
|
||||
xd->left_mouse_btn = true;
|
||||
break;
|
||||
case Button2:
|
||||
xd->wheel_mouse_btn = true;
|
||||
break;
|
||||
case Button3:
|
||||
xd->right_mouse_btn = true;
|
||||
break;
|
||||
case Button4: /* Scrolled up */
|
||||
xd->wheel_cnt--;
|
||||
break;
|
||||
case Button5: /* Scrolled down */
|
||||
xd->wheel_cnt++;
|
||||
break;
|
||||
default:
|
||||
LV_LOG_WARN("unhandled button press : %d", event.xbutton.button);
|
||||
}
|
||||
break;
|
||||
case ButtonRelease:
|
||||
switch(event.xbutton.button) {
|
||||
case Button1:
|
||||
xd->left_mouse_btn = false;
|
||||
break;
|
||||
case Button2:
|
||||
xd->wheel_mouse_btn = false;
|
||||
break;
|
||||
case Button3:
|
||||
xd->right_mouse_btn = false;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case KeyPress: {
|
||||
size_t len = strlen(xd->kb_buffer);
|
||||
if(len < (sizeof(xd->kb_buffer) - 2 /* space for 1 char + '\0' */)) {
|
||||
KeySym key;
|
||||
int n = XLookupString(&event.xkey, &xd->kb_buffer[len], sizeof(xd->kb_buffer) - (len + 1), &key, NULL);
|
||||
n += !!key;
|
||||
switch(key) {
|
||||
case XK_Home:
|
||||
case XK_KP_Home:
|
||||
xd->kb_buffer[len] = LV_KEY_HOME;
|
||||
break;
|
||||
case XK_Left:
|
||||
case XK_KP_Left:
|
||||
xd->kb_buffer[len] = LV_KEY_LEFT;
|
||||
break;
|
||||
case XK_Up:
|
||||
case XK_KP_Up:
|
||||
xd->kb_buffer[len] = LV_KEY_UP;
|
||||
break;
|
||||
case XK_Right:
|
||||
case XK_KP_Right:
|
||||
xd->kb_buffer[len] = LV_KEY_RIGHT;
|
||||
break;
|
||||
case XK_Down:
|
||||
case XK_KP_Down:
|
||||
xd->kb_buffer[len] = LV_KEY_DOWN;
|
||||
break;
|
||||
case XK_Prior:
|
||||
case XK_KP_Prior:
|
||||
xd->kb_buffer[len] = LV_KEY_PREV;
|
||||
break;
|
||||
case XK_Next:
|
||||
case XK_KP_Next:
|
||||
xd->kb_buffer[len] = LV_KEY_NEXT;
|
||||
break;
|
||||
case XK_End:
|
||||
case XK_KP_End:
|
||||
xd->kb_buffer[len] = LV_KEY_END;
|
||||
break;
|
||||
case XK_BackSpace:
|
||||
xd->kb_buffer[len] = LV_KEY_BACKSPACE;
|
||||
break;
|
||||
case XK_Escape:
|
||||
xd->kb_buffer[len] = LV_KEY_ESC;
|
||||
break;
|
||||
case XK_Delete:
|
||||
case XK_KP_Delete:
|
||||
xd->kb_buffer[len] = LV_KEY_DEL;
|
||||
break;
|
||||
case XK_KP_Enter:
|
||||
xd->kb_buffer[len] = LV_KEY_ENTER;
|
||||
break;
|
||||
}
|
||||
xd->kb_buffer[len + n] = '\0';
|
||||
}
|
||||
}
|
||||
break;
|
||||
case KeyRelease:
|
||||
break;
|
||||
default:
|
||||
LV_LOG_WARN("unhandled x11 event: %d", event.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* event called by lvgl display if display has been closed (@ref lv_display_delete has been called)
|
||||
* @param[in] e event data, containing lv_display_t object
|
||||
*/
|
||||
static void x11_inp_delete_evt_cb(lv_event_t * e)
|
||||
{
|
||||
x11_inp_data_t * xd = (x11_inp_data_t *)lv_event_get_user_data(e);
|
||||
|
||||
lv_timer_delete(xd->timer);
|
||||
lv_free(xd);
|
||||
}
|
||||
|
||||
/**
|
||||
* create the local data/timers for the X11 input functionality.
|
||||
* extracts the user data information from lv_display_t object and initializes the input user object on 1st use.
|
||||
* @param[in] disp the created X11 display object from @lv_x11_window_create
|
||||
* @return pointer to the local user data object @x11_inp_data_t
|
||||
*/
|
||||
static x11_inp_data_t * x11_input_get_user_data(lv_display_t * disp)
|
||||
{
|
||||
_x11_user_hdr_t * disp_hdr = lv_display_get_driver_data(disp);
|
||||
LV_ASSERT_NULL(disp_hdr);
|
||||
x11_inp_data_t ** inp_data = &disp_hdr->inp_data;
|
||||
|
||||
/* create input data set if initial call */
|
||||
if(NULL == *inp_data) {
|
||||
*inp_data = lv_malloc_zeroed(sizeof(x11_inp_data_t));
|
||||
LV_ASSERT_MALLOC(*inp_data);
|
||||
if(NULL != *inp_data) {
|
||||
/* initialize timer callback for X11 kb/mouse input event reading */
|
||||
(*inp_data)->timer = lv_timer_create(x11_inp_event_handler, 1, disp);
|
||||
lv_display_add_event_cb(disp, x11_inp_delete_evt_cb, LV_EVENT_DELETE, *inp_data);
|
||||
}
|
||||
}
|
||||
return *inp_data;
|
||||
}
|
||||
|
||||
static void x11_keyboard_read_cb(lv_indev_t * indev, lv_indev_data_t * data)
|
||||
{
|
||||
lv_display_t * disp = lv_indev_get_driver_data(indev);
|
||||
x11_inp_data_t * xd = x11_input_get_user_data(disp);
|
||||
|
||||
size_t len = strlen(xd->kb_buffer);
|
||||
if(len > 0) {
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
data->key = xd->kb_buffer[0];
|
||||
memmove(xd->kb_buffer, xd->kb_buffer + 1, len);
|
||||
data->continue_reading = (len > 0);
|
||||
}
|
||||
else {
|
||||
data->state = LV_INDEV_STATE_RELEASED;
|
||||
}
|
||||
}
|
||||
|
||||
static void x11_mouse_read_cb(lv_indev_t * indev, lv_indev_data_t * data)
|
||||
{
|
||||
lv_display_t * disp = lv_indev_get_driver_data(indev);
|
||||
x11_inp_data_t * xd = x11_input_get_user_data(disp);
|
||||
|
||||
int32_t hor_res = lv_display_get_horizontal_resolution(disp);
|
||||
int32_t ver_res = lv_display_get_vertical_resolution(disp);
|
||||
|
||||
xd->mouse_pos.x = MIN(xd->mouse_pos.x, hor_res - 1);
|
||||
xd->mouse_pos.y = MIN(xd->mouse_pos.y, ver_res - 1);
|
||||
|
||||
data->point = xd->mouse_pos;
|
||||
data->state = xd->left_mouse_btn ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED;
|
||||
}
|
||||
|
||||
static void x11_mousewheel_read_cb(lv_indev_t * indev, lv_indev_data_t * data)
|
||||
{
|
||||
lv_display_t * disp = lv_indev_get_driver_data(indev);
|
||||
x11_inp_data_t * xd = x11_input_get_user_data(disp);
|
||||
|
||||
data->state = xd->wheel_mouse_btn ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED;
|
||||
data->enc_diff = xd->wheel_cnt;
|
||||
xd->wheel_cnt = 0;
|
||||
}
|
||||
|
||||
static lv_indev_t * lv_x11_keyboard_create(lv_display_t * disp)
|
||||
{
|
||||
lv_indev_t * indev = lv_indev_create();
|
||||
LV_ASSERT_NULL(indev);
|
||||
if(NULL != indev) {
|
||||
lv_indev_set_type(indev, LV_INDEV_TYPE_KEYPAD);
|
||||
lv_indev_set_read_cb(indev, x11_keyboard_read_cb);
|
||||
lv_indev_set_driver_data(indev, disp);
|
||||
}
|
||||
return indev;
|
||||
}
|
||||
|
||||
static lv_indev_t * lv_x11_mouse_create(lv_display_t * disp, lv_image_dsc_t const * symb)
|
||||
{
|
||||
lv_indev_t * indev = lv_indev_create();
|
||||
if(NULL != indev) {
|
||||
lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);
|
||||
lv_indev_set_read_cb(indev, x11_mouse_read_cb);
|
||||
lv_indev_set_driver_data(indev, disp);
|
||||
|
||||
/* optional mouse cursor symbol */
|
||||
if(NULL != symb) {
|
||||
lv_obj_t * mouse_cursor = lv_image_create(lv_screen_active());
|
||||
lv_image_set_src(mouse_cursor, symb);
|
||||
lv_indev_set_cursor(indev, mouse_cursor);
|
||||
}
|
||||
}
|
||||
return indev;
|
||||
}
|
||||
|
||||
static lv_indev_t * lv_x11_mousewheel_create(lv_display_t * disp)
|
||||
{
|
||||
lv_indev_t * indev = lv_indev_create();
|
||||
if(NULL != indev) {
|
||||
lv_indev_set_type(indev, LV_INDEV_TYPE_ENCODER);
|
||||
lv_indev_set_read_cb(indev, x11_mousewheel_read_cb);
|
||||
lv_indev_set_driver_data(indev, disp);
|
||||
}
|
||||
return indev;
|
||||
}
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
void lv_x11_inputs_create(lv_display_t * disp, lv_image_dsc_t const * mouse_img)
|
||||
{
|
||||
x11_inp_data_t * xd = x11_input_get_user_data(disp);
|
||||
LV_ASSERT_NULL(xd);
|
||||
|
||||
xd->inp_group = lv_group_create();
|
||||
lv_group_set_default(xd->inp_group);
|
||||
|
||||
xd->mousepointer = lv_x11_mouse_create(disp, mouse_img);
|
||||
lv_indev_set_group(xd->mousepointer, xd->inp_group);
|
||||
|
||||
xd->mousewheel = lv_x11_mousewheel_create(disp);
|
||||
lv_indev_set_group(xd->mousewheel, xd->inp_group);
|
||||
|
||||
xd->keyboard = lv_x11_keyboard_create(disp);
|
||||
lv_indev_set_group(xd->keyboard, xd->inp_group);
|
||||
}
|
||||
|
||||
#endif /*LV_USE_X11*/
|
||||
Reference in New Issue
Block a user