Squashed 'lib/lv_drivers/' changes from 718302577..0091dc612

0091dc612 fix(wayland): fix compile error wrt LV_WAYLAND_CLIENT_SIDE_DECORATIONS (#307)
451e659cf Add pkgconfig file (#301)
5a5b4a1a3 added x11 display driver port (#300)
4391dbf32 perf(drm): use wait_cb for direct double-buffered rendering (#299)
7e18481ce fix(drm): backport drm driver fixes from lvgl 9 (#298)
f5b58b947 feat(evdev): backport multi device support from lvgl 9 (#296)
ad66ff155 add return value to drm_init
c239cc465 Removed clamp from Calibrate section as it has map in it
e44e7f043 fix(build): Update xpt2046_read to match signature required by lv_indev_drv_t's read callback. (#290)
7b9dee11c Fix crash during startup in certain conditions (#286)
9d153fb65 Add USE_WAYLAND to smm.c (#285)
8669c6fc8 libInput: Add POINTER_MOTION_ABSOLUTE support (#284)
c71e5f84b drm: Default to XRGB8888 framebuffer (#282)
57494ff8e Add a way to set SDL window title from lv_drv_conf.h (#277)
b03c9dac9 fix(xkb): Fix memory leak by avoiding double reference (#276)
ec0ad8296 feature(libinput): Expose function for querying capability (#275)
0d5de84e2 feature(libinput): Add function to reset driver states (#274)
94dc4ce06 Use win32 singly linked list instead of LVGL linked list for fixing memory access conflict issues for win32drv. (#273)
1792ab20a feature(wayland): add a shared memory manager, allowing backing buffers to be allocated on demand. (#270)
f7935569f use SDL_RES instead of LV_RES for touch events (#268)
ba9c3cc4f Update README.md (#261)
f261225d9 follow lvgl changes
ab5e30ccb Add the multiple display support for win32drv. (#259)
820341ea1 Improve the keyboard support for win32drv. (#258)
b13361a1f fix: The mouse moves outside the screen area (#251)
829e0ffc3 chore(wayland) : accelerate damage updates, reduce unnecessary cycles (#249)
5523f9974 fix(wayland-protocols): minimum version (#246)
fe9de86e9 Wayland api fixes (#243)
59e698ce1 fix(wayland) : fix possible memory leak (#238)
2ed01feab fix(drm): Fix compiler warnings (#237)
cf4e6d75a make windows.h lower case (#236)
dc0d71a3a chore(sdl): fix warning (#232)
c68b59e42 fix(fbdev): Fix rendering for 24 bits per pixel (#231)
4f98fddd2 hide the SDL2 include from public LVGL functions (#227)
ff01834db fix(fbdev): Gracefully handle FBIOBLANK errors (#229)
73219786a bump version number to v9.0.0-dev

git-subtree-dir: lib/lv_drivers
git-subtree-split: 0091dc612facc94dce1061a9b78d641c77f1791a
master
alex 9 months ago
parent 8e64914235
commit 20ee2f8220

1
.gitignore vendored

@ -1,2 +1,3 @@
**/*.o **/*.o
**/*.d **/*.d
build/*

@ -20,6 +20,7 @@ target_include_directories(lv_drivers SYSTEM PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
find_package(PkgConfig) find_package(PkgConfig)
pkg_check_modules(PKG_WAYLAND wayland-client wayland-cursor wayland-protocols xkbcommon) pkg_check_modules(PKG_WAYLAND wayland-client wayland-cursor wayland-protocols xkbcommon)
pkg_check_modules(PKG_LVGL lvgl)
target_link_libraries(lv_drivers PUBLIC lvgl ${PKG_WAYLAND_LIBRARIES}) target_link_libraries(lv_drivers PUBLIC lvgl ${PKG_WAYLAND_LIBRARIES})
if("${LIB_INSTALL_DIR}" STREQUAL "") if("${LIB_INSTALL_DIR}" STREQUAL "")
@ -38,7 +39,16 @@ install(
PATTERN ".git*" EXCLUDE PATTERN ".git*" EXCLUDE
PATTERN "CMakeFiles" EXCLUDE PATTERN "CMakeFiles" EXCLUDE
PATTERN "docs" EXCLUDE PATTERN "docs" EXCLUDE
PATTERN "lib" EXCLUDE) PATTERN "lib" EXCLUDE
PATTERN "*.pc.in" EXCLUDE)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${PKG_LVGL_CFLAGS}")
configure_file("${CMAKE_SOURCE_DIR}/lv-drivers.pc.in" lv-drivers.pc @ONLY)
install(
FILES "${CMAKE_BINARY_DIR}/lv-drivers.pc"
DESTINATION "${LIB_INSTALL_DIR}/pkgconfig/")
file(GLOB LV_DRIVERS_PUBLIC_HEADERS "${CMAKE_SOURCE_DIR}/lv_drv_conf.h") file(GLOB LV_DRIVERS_PUBLIC_HEADERS "${CMAKE_SOURCE_DIR}/lv_drv_conf.h")

@ -1,6 +1,6 @@
# Display and Touch pad drivers # Display and Touch pad drivers
Display controller and touchpad driver to can be directly used with [LittlevGL](https://littlevgl.com). Display controller and touchpad driver to can be directly used with [LVGL](https://lvgl.io/).
To learn more about using drivers in LittlevGL visit the [Porting guide](https://docs.lvgl.io/latest/en/html/porting/index.html). To learn more about using drivers in LittlevGL visit the [Porting guide](https://docs.lvgl.io/latest/en/html/porting/index.html).

@ -9,19 +9,14 @@
#include "drm.h" #include "drm.h"
#if USE_DRM #if USE_DRM
#include <unistd.h> #include <errno.h>
#include <pthread.h> #include <fcntl.h>
#include <time.h> #include <poll.h>
#include <sys/time.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <inttypes.h> #include <unistd.h>
#include <xf86drm.h> #include <xf86drm.h>
#include <xf86drmMode.h> #include <xf86drmMode.h>
@ -41,7 +36,7 @@ struct drm_buffer {
uint32_t pitch; uint32_t pitch;
uint32_t offset; uint32_t offset;
unsigned long int size; unsigned long int size;
void * map; uint8_t * map;
uint32_t fb_handle; uint32_t fb_handle;
}; };
@ -66,7 +61,8 @@ struct drm_dev {
drmModePropertyPtr crtc_props[128]; drmModePropertyPtr crtc_props[128];
drmModePropertyPtr conn_props[128]; drmModePropertyPtr conn_props[128];
struct drm_buffer drm_bufs[2]; /*DUMB buffers*/ struct drm_buffer drm_bufs[2]; /*DUMB buffers*/
struct drm_buffer *cur_bufs[2]; /* double buffering handling */ uint8_t active_drm_buf_idx; /*Double buffering handling*/
lv_disp_draw_buf_t draw_buf;
} drm_dev; } drm_dev;
static uint32_t get_plane_property_id(const char *name) static uint32_t get_plane_property_id(const char *name)
@ -117,7 +113,18 @@ static uint32_t get_conn_property_id(const char *name)
static void page_flip_handler(int fd, unsigned int sequence, unsigned int tv_sec, static void page_flip_handler(int fd, unsigned int sequence, unsigned int tv_sec,
unsigned int tv_usec, void *user_data) unsigned int tv_usec, void *user_data)
{ {
LV_UNUSED(fd);
LV_UNUSED(sequence);
LV_UNUSED(tv_sec);
LV_UNUSED(tv_usec);
LV_UNUSED(user_data);
dbg("flip"); dbg("flip");
if(drm_dev.req) {
drmModeAtomicFree(drm_dev.req);
drm_dev.req = NULL;
}
} }
static int drm_get_plane_props(void) static int drm_get_plane_props(void)
@ -244,7 +251,7 @@ static int drm_dmabuf_set_plane(struct drm_buffer *buf)
{ {
int ret; int ret;
static int first = 1; static int first = 1;
uint32_t flags = DRM_MODE_PAGE_FLIP_EVENT; uint32_t flags = DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_NONBLOCK;
drm_dev.req = drmModeAtomicAlloc(); drm_dev.req = drmModeAtomicAlloc();
@ -283,6 +290,7 @@ static int drm_dmabuf_set_plane(struct drm_buffer *buf)
static int find_plane(unsigned int fourcc, uint32_t *plane_id, uint32_t crtc_id, uint32_t crtc_idx) static int find_plane(unsigned int fourcc, uint32_t *plane_id, uint32_t crtc_id, uint32_t crtc_idx)
{ {
LV_UNUSED(crtc_id);
drmModePlaneResPtr planes; drmModePlaneResPtr planes;
drmModePlanePtr plane; drmModePlanePtr plane;
unsigned int i; unsigned int i;
@ -393,7 +401,7 @@ static int drm_find_connector(void)
drm_dev.mmWidth = conn->mmWidth; drm_dev.mmWidth = conn->mmWidth;
drm_dev.mmHeight = conn->mmHeight; drm_dev.mmHeight = conn->mmHeight;
memcpy(&drm_dev.mode, &conn->modes[0], sizeof(drmModeModeInfo)); lv_memcpy(&drm_dev.mode, &conn->modes[0], sizeof(drmModeModeInfo));
if (drmModeCreatePropertyBlob(drm_dev.fd, &drm_dev.mode, sizeof(drm_dev.mode), if (drmModeCreatePropertyBlob(drm_dev.fd, &drm_dev.mode, sizeof(drm_dev.mode),
&drm_dev.blob_id)) { &drm_dev.blob_id)) {
@ -464,7 +472,7 @@ static int drm_find_connector(void)
drmModeFreeEncoder(enc); drmModeFreeEncoder(enc);
} }
drm_dev.crtc_idx = -1; drm_dev.crtc_idx = UINT32_MAX;
for (i = 0; i < res->count_crtcs; ++i) { for (i = 0; i < res->count_crtcs; ++i) {
if (drm_dev.crtc_id == res->crtcs[i]) { if (drm_dev.crtc_id == res->crtcs[i]) {
@ -473,7 +481,7 @@ static int drm_find_connector(void)
} }
} }
if (drm_dev.crtc_idx == -1) { if (drm_dev.crtc_idx == UINT32_MAX) {
err("drm: CRTC not found"); err("drm: CRTC not found");
goto free_res; goto free_res;
} }
@ -614,7 +622,7 @@ static int drm_allocate_dumb(struct drm_buffer *buf)
int ret; int ret;
/* create dumb buffer */ /* create dumb buffer */
memset(&creq, 0, sizeof(creq)); lv_memset(&creq, 0, sizeof(creq));
creq.width = drm_dev.width; creq.width = drm_dev.width;
creq.height = drm_dev.height; creq.height = drm_dev.height;
creq.bpp = LV_COLOR_DEPTH; creq.bpp = LV_COLOR_DEPTH;
@ -626,12 +634,10 @@ static int drm_allocate_dumb(struct drm_buffer *buf)
buf->handle = creq.handle; buf->handle = creq.handle;
buf->pitch = creq.pitch; buf->pitch = creq.pitch;
dbg("pitch %d", buf->pitch);
buf->size = creq.size; buf->size = creq.size;
dbg("size %d", buf->size);
/* prepare buffer for memory mapping */ /* prepare buffer for memory mapping */
memset(&mreq, 0, sizeof(mreq)); lv_memset(&mreq, 0, sizeof(mreq));
mreq.handle = creq.handle; mreq.handle = creq.handle;
ret = drmIoctl(drm_dev.fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq); ret = drmIoctl(drm_dev.fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq);
if (ret) { if (ret) {
@ -640,6 +646,7 @@ static int drm_allocate_dumb(struct drm_buffer *buf)
} }
buf->offset = mreq.offset; buf->offset = mreq.offset;
info("size %lu pitch %u offset %u", buf->size, buf->pitch, buf->offset);
/* perform actual memory mapping */ /* perform actual memory mapping */
buf->map = mmap(0, creq.size, PROT_READ | PROT_WRITE, MAP_SHARED, drm_dev.fd, mreq.offset); buf->map = mmap(0, creq.size, PROT_READ | PROT_WRITE, MAP_SHARED, drm_dev.fd, mreq.offset);
@ -649,7 +656,7 @@ static int drm_allocate_dumb(struct drm_buffer *buf)
} }
/* clear the framebuffer to 0 (= full transparency in ARGB8888) */ /* clear the framebuffer to 0 (= full transparency in ARGB8888) */
memset(buf->map, 0, creq.size); lv_memset(buf->map, 0, creq.size);
/* create framebuffer object for the dumb-buffer */ /* create framebuffer object for the dumb-buffer */
handles[0] = creq.handle; handles[0] = creq.handle;
@ -678,61 +685,48 @@ static int drm_setup_buffers(void)
if (ret) if (ret)
return ret; return ret;
/* Set buffering handling */
drm_dev.cur_bufs[0] = NULL;
drm_dev.cur_bufs[1] = &drm_dev.drm_bufs[0];
return 0; return 0;
} }
void drm_wait_vsync(lv_disp_drv_t * disp_drv) void drm_wait_vsync(lv_disp_drv_t * disp_drv)
{ {
int ret; while(drm_dev.req) {
fd_set fds; struct pollfd pfd;
FD_ZERO(&fds); pfd.fd = drm_dev.fd;
FD_SET(drm_dev.fd, &fds); pfd.events = POLLIN;
int ret;
do { do {
ret = select(drm_dev.fd + 1, &fds, NULL, NULL, NULL); ret = poll(&pfd, 1, -1);
} while (ret == -1 && errno == EINTR); } while (ret == -1 && errno == EINTR);
if (ret < 0) { if(ret > 0)
err("select failed: %s", strerror(errno)); drmHandleEvent(drm_dev.fd, &drm_dev.drm_event_ctx);
drmModeAtomicFree(drm_dev.req); else {
drm_dev.req = NULL; err("poll failed: %s", strerror(errno));
return; return;
} }
}
if (FD_ISSET(drm_dev.fd, &fds)) lv_disp_flush_ready(disp_drv);
drmHandleEvent(drm_dev.fd, &drm_dev.drm_event_ctx);
drmModeAtomicFree(drm_dev.req);
drm_dev.req = NULL;
} }
void drm_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p) void drm_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{ {
struct drm_buffer *fbuf = drm_dev.cur_bufs[1]; struct drm_buffer *fbuf = &drm_dev.drm_bufs[drm_dev.active_drm_buf_idx ^ 1];
lv_coord_t w = (area->x2 - area->x1 + 1);
lv_coord_t h = (area->y2 - area->y1 + 1);
int i, y;
dbg("x %d:%d y %d:%d w %d h %d", area->x1, area->x2, area->y1, area->y2, w, h); if(!disp_drv->direct_mode) {
/*Backwards compatibility: Non-direct flush */
/* Partial update */ uint32_t w = (area->x2 - area->x1) + 1;
if ((w != drm_dev.width || h != drm_dev.height) && drm_dev.cur_bufs[0]) for (int y = 0, i = area->y1; i <= area->y2 ; ++i, ++y) {
memcpy(fbuf->map, drm_dev.cur_bufs[0]->map, fbuf->size); lv_memcpy(fbuf->map + (area->x1 * (LV_COLOR_SIZE / 8)) + (fbuf->pitch * i),
for (y = 0, i = area->y1 ; i <= area->y2 ; ++i, ++y) {
memcpy((uint8_t *)fbuf->map + (area->x1 * (LV_COLOR_SIZE/8)) + (fbuf->pitch * i),
(uint8_t *)color_p + (w * (LV_COLOR_SIZE / 8) * y), (uint8_t *)color_p + (w * (LV_COLOR_SIZE / 8) * y),
w * (LV_COLOR_SIZE / 8)); w * (LV_COLOR_SIZE / 8));
} }
}
if (drm_dev.req) if(lv_disp_flush_is_last(disp_drv)) {
drm_wait_vsync(disp_drv); /*Request buffer swap*/
/* show fbuf plane */
if(drm_dmabuf_set_plane(fbuf)) { if(drm_dmabuf_set_plane(fbuf)) {
err("Flush fail"); err("Flush fail");
return; return;
@ -740,18 +734,12 @@ void drm_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color
else else
dbg("Flush done"); dbg("Flush done");
if (!drm_dev.cur_bufs[0]) drm_dev.active_drm_buf_idx ^= 1;
drm_dev.cur_bufs[1] = &drm_dev.drm_bufs[1]; }
else
drm_dev.cur_bufs[1] = drm_dev.cur_bufs[0];
drm_dev.cur_bufs[0] = fbuf;
lv_disp_flush_ready(disp_drv);
} }
#if LV_COLOR_DEPTH == 32 #if LV_COLOR_DEPTH == 32
#define DRM_FOURCC DRM_FORMAT_ARGB8888 #define DRM_FOURCC DRM_FORMAT_XRGB8888
#elif LV_COLOR_DEPTH == 16 #elif LV_COLOR_DEPTH == 16
#define DRM_FOURCC DRM_FORMAT_RGB565 #define DRM_FOURCC DRM_FORMAT_RGB565
#else #else
@ -770,7 +758,7 @@ void drm_get_sizes(lv_coord_t *width, lv_coord_t *height, uint32_t *dpi)
*dpi = DIV_ROUND_UP(drm_dev.width * 25400, drm_dev.mmWidth * 1000); *dpi = DIV_ROUND_UP(drm_dev.width * 25400, drm_dev.mmWidth * 1000);
} }
void drm_init(void) int drm_init(void)
{ {
int ret; int ret;
@ -778,7 +766,7 @@ void drm_init(void)
if (ret) { if (ret) {
close(drm_dev.fd); close(drm_dev.fd);
drm_dev.fd = -1; drm_dev.fd = -1;
return; return -1;
} }
ret = drm_setup_buffers(); ret = drm_setup_buffers();
@ -786,10 +774,28 @@ void drm_init(void)
err("DRM buffer allocation failed"); err("DRM buffer allocation failed");
close(drm_dev.fd); close(drm_dev.fd);
drm_dev.fd = -1; drm_dev.fd = -1;
return; return -1;
} }
info("DRM subsystem and buffer mapped successfully"); info("DRM subsystem and buffer mapped successfully");
return 0;
}
int drm_disp_drv_init(lv_disp_drv_t * disp_drv)
{
lv_disp_drv_init(disp_drv);
int ret = drm_init();
if(ret) return ret;
lv_disp_draw_buf_init(&drm_dev.draw_buf, drm_dev.drm_bufs[1].map, drm_dev.drm_bufs[0].map, drm_dev.width * drm_dev.height);
disp_drv->draw_buf = &drm_dev.draw_buf;
disp_drv->direct_mode = true;
disp_drv->hor_res = drm_dev.width;
disp_drv->ver_res = drm_dev.height;
disp_drv->flush_cb = drm_flush;
disp_drv->wait_cb = drm_wait_vsync;
return 0;
} }
void drm_exit(void) void drm_exit(void)

@ -40,13 +40,14 @@ extern "C" {
/********************** /**********************
* GLOBAL PROTOTYPES * GLOBAL PROTOTYPES
**********************/ **********************/
void drm_init(void);
int drm_init(void); /*Deprecated: Use drm_disp_drv_init instead*/
int drm_disp_drv_init(lv_disp_drv_t * disp_drv);
void drm_get_sizes(lv_coord_t * width, lv_coord_t * height, uint32_t * dpi); void drm_get_sizes(lv_coord_t * width, lv_coord_t * height, uint32_t * dpi);
void drm_exit(void); void drm_exit(void);
void drm_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_p); void drm_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_p);
void drm_wait_vsync(lv_disp_drv_t * drv); void drm_wait_vsync(lv_disp_drv_t * drv);
/********************** /**********************
* MACROS * MACROS
**********************/ **********************/

@ -101,7 +101,7 @@ void fbdev_init(void)
// Make sure that the display is on. // Make sure that the display is on.
if (ioctl(fbfd, FBIOBLANK, FB_BLANK_UNBLANK) != 0) { if (ioctl(fbfd, FBIOBLANK, FB_BLANK_UNBLANK) != 0) {
perror("ioctl(FBIOBLANK)"); perror("ioctl(FBIOBLANK)");
return; // Don't return. Some framebuffer drivers like efifb or simplefb don't implement FBIOBLANK.
} }
#if USE_BSD_FBDEV #if USE_BSD_FBDEV
@ -195,8 +195,8 @@ void fbdev_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color
long int byte_location = 0; long int byte_location = 0;
unsigned char bit_location = 0; unsigned char bit_location = 0;
/*32 or 24 bit per pixel*/ /*32 bit per pixel*/
if(vinfo.bits_per_pixel == 32 || vinfo.bits_per_pixel == 24) { if(vinfo.bits_per_pixel == 32) {
uint32_t * fbp32 = (uint32_t *)fbp; uint32_t * fbp32 = (uint32_t *)fbp;
int32_t y; int32_t y;
for(y = act_y1; y <= act_y2; y++) { for(y = act_y1; y <= act_y2; y++) {
@ -205,6 +205,23 @@ void fbdev_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color
color_p += w; color_p += w;
} }
} }
/*24 bit per pixel*/
else if(vinfo.bits_per_pixel == 24 && LV_COLOR_DEPTH == 32) {
uint8_t * fbp8 = (uint8_t *)fbp;
lv_coord_t x;
int32_t y;
uint8_t *pixel;
for(y = act_y1; y <= act_y2; y++) {
location = (act_x1 + vinfo.xoffset) + (y + vinfo.yoffset) * finfo.line_length / 3;
for (x = 0; x < w; ++x) {
pixel = (uint8_t *)(&color_p[x]);
fbp8[3 * (location + x)] = pixel[0];
fbp8[3 * (location + x) + 1] = pixel[1];
fbp8[3 * (location + x) + 2] = pixel[2];
}
color_p += w;
}
}
/*16 bit per pixel*/ /*16 bit per pixel*/
else if(vinfo.bits_per_pixel == 16) { else if(vinfo.bits_per_pixel == 16) {
uint16_t * fbp16 = (uint16_t *)fbp; uint16_t * fbp16 = (uint16_t *)fbp;

@ -55,9 +55,9 @@ void xpt2046_init(void)
/** /**
* Get the current position and state of the touchpad * Get the current position and state of the touchpad
* @param data store the read data here * @param data store the read data here
* @return false: because no ore data to be read * @return void
*/ */
bool xpt2046_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data) void xpt2046_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{ {
static int16_t last_x = 0; static int16_t last_x = 0;
static int16_t last_y = 0; static int16_t last_y = 0;
@ -105,7 +105,7 @@ bool xpt2046_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
data->point.x = x; data->point.x = x;
data->point.y = y; data->point.y = y;
return false; data->continue_reading = false; /* No more data to be read */
} }
/********************** /**********************

@ -41,7 +41,7 @@ extern "C" {
* GLOBAL PROTOTYPES * GLOBAL PROTOTYPES
**********************/ **********************/
void xpt2046_init(void); void xpt2046_init(void);
bool xpt2046_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data); void xpt2046_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
/********************** /**********************
* MACROS * MACROS

@ -12,6 +12,7 @@
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <errno.h>
#if USE_BSD_EVDEV #if USE_BSD_EVDEV
#include <dev/evdev/input.h> #include <dev/evdev/input.h>
#else #else
@ -22,230 +23,202 @@
#include "xkb.h" #include "xkb.h"
#endif /* USE_XKB */ #endif /* USE_XKB */
/*********************
* DEFINES
*********************/
/********************** /**********************
* TYPEDEFS * STATIC VARIABLES
**********************/ **********************/
/********************** evdev_device_t global_dsc;
* STATIC PROTOTYPES
**********************/
int map(int x, int in_min, int in_max, int out_min, int out_max);
/********************** /**********************
* STATIC VARIABLES * STATIC FUNCTIONS
**********************/ **********************/
int evdev_fd = -1;
int evdev_root_x;
int evdev_root_y;
int evdev_button;
int evdev_key_val; static int _evdev_process_key(uint16_t code, bool pressed)
{
#if USE_XKB
return xkb_process_key(code, pressed);
#else
LV_UNUSED(pressed);
switch(code) {
case KEY_UP:
return LV_KEY_UP;
case KEY_DOWN:
return LV_KEY_DOWN;
case KEY_RIGHT:
return LV_KEY_RIGHT;
case KEY_LEFT:
return LV_KEY_LEFT;
case KEY_ESC:
return LV_KEY_ESC;
case KEY_DELETE:
return LV_KEY_DEL;
case KEY_BACKSPACE:
return LV_KEY_BACKSPACE;
case KEY_ENTER:
return LV_KEY_ENTER;
case KEY_NEXT:
case KEY_TAB:
return LV_KEY_NEXT;
case KEY_PREVIOUS:
return LV_KEY_PREV;
case KEY_HOME:
return LV_KEY_HOME;
case KEY_END:
return LV_KEY_END;
default:
return 0;
}
#endif /*USE_XKB*/
}
static int _evdev_calibrate(int v, int in_min, int in_max, int out_min, int out_max)
{
if(in_min != in_max) v = (v - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
return LV_CLAMP(out_min, v, out_max);
}
/********************** static lv_point_t _evdev_process_pointer(lv_indev_drv_t * drv, int x, int y)
* MACROS {
**********************/ evdev_device_t * dsc = drv->user_data ? drv->user_data : &global_dsc;
int swapped_x = dsc->swap_axes ? y : x;
int swapped_y = dsc->swap_axes ? x : y;
int offset_x = 0; /*Not lv_disp_get_offset_x(drv->disp) for bc*/
int offset_y = 0; /*Not lv_disp_get_offset_y(drv->disp) for bc*/
int width = lv_disp_get_hor_res(drv->disp);
int height = lv_disp_get_ver_res(drv->disp);
lv_point_t p;
p.x = _evdev_calibrate(swapped_x, dsc->hor_min, dsc->hor_max, offset_x, offset_x + width - 1);
p.y = _evdev_calibrate(swapped_y, dsc->ver_min, dsc->ver_max, offset_y, offset_y + height - 1);
return p;
}
/********************** /**********************
* GLOBAL FUNCTIONS * GLOBAL FUNCTIONS
**********************/ **********************/
/** void evdev_init()
* Initialize the evdev interface
*/
void evdev_init(void)
{ {
if (!evdev_set_file(EVDEV_NAME)) { evdev_device_init(&global_dsc);
return; #ifdef EVDEV_SWAP_AXES
evdev_device_set_swap_axes(&global_dsc, EVDEV_SWAP_AXES);
#endif
#if EVDEV_CALIBRATE
evdev_device_set_calibration(&global_dsc, EVDEV_HOR_MIN, EVDEV_VER_MIN, EVDEV_HOR_MAX, EVDEV_VER_MAX);
#endif
evdev_device_set_file(&global_dsc, EVDEV_NAME);
} }
void evdev_device_init(evdev_device_t * dsc)
{
lv_memset(dsc, 0, sizeof(evdev_device_t));
dsc->fd = -1;
#if USE_XKB #if USE_XKB
xkb_init(); xkb_init();
#endif #endif
} }
/**
* reconfigure the device file for evdev bool evdev_set_file(const char * dev_path)
* @param dev_name set the evdev device filename
* @return true: the device file set complete
* false: the device file doesn't exist current system
*/
bool evdev_set_file(char* dev_name)
{ {
if(evdev_fd != -1) { return evdev_device_set_file(&global_dsc, dev_path);
close(evdev_fd);
} }
#if USE_BSD_EVDEV
evdev_fd = open(dev_name, O_RDWR | O_NOCTTY);
#else
evdev_fd = open(dev_name, O_RDWR | O_NOCTTY | O_NDELAY);
#endif
if(evdev_fd == -1) { bool evdev_device_set_file(evdev_device_t * dsc, const char * dev_path)
perror("unable to open evdev interface:"); {
/*Reset state*/
dsc->root_x = 0;
dsc->root_y = 0;
dsc->key = 0;
dsc->state = LV_INDEV_STATE_RELEASED;
/*Close previous*/
if(dsc->fd >= 0) {
close(dsc->fd);
dsc->fd = -1;
}
if(!dev_path) return false;
/*Open new*/
dsc->fd = open(dev_path, O_RDONLY | O_NOCTTY | O_CLOEXEC);
if(dsc->fd < 0) {
LV_LOG_ERROR("open failed: %s", strerror(errno));
return false;
}
if(fcntl(dsc->fd, F_SETFL, O_NONBLOCK) < 0) {
LV_LOG_ERROR("fcntl failed: %s", strerror(errno));
close(dsc->fd);
dsc->fd = -1;
return false; return false;
} }
#if USE_BSD_EVDEV return true;
fcntl(evdev_fd, F_SETFL, O_NONBLOCK); }
#else
fcntl(evdev_fd, F_SETFL, O_ASYNC | O_NONBLOCK);
#endif
evdev_root_x = 0; void evdev_device_set_swap_axes(evdev_device_t * dsc, bool swap_axes)
evdev_root_y = 0; {
evdev_key_val = 0; dsc->swap_axes = swap_axes;
evdev_button = LV_INDEV_STATE_REL; }
return true; void evdev_device_set_calibration(evdev_device_t * dsc, int ver_min, int hor_min, int ver_max, int hor_max)
{
dsc->ver_min = ver_min;
dsc->hor_min = hor_min;
dsc->ver_max = ver_max;
dsc->hor_max = hor_max;
} }
/**
* Get the current position and state of the evdev
* @param data store the evdev data here
*/
void evdev_read(lv_indev_drv_t * drv, lv_indev_data_t * data) void evdev_read(lv_indev_drv_t * drv, lv_indev_data_t * data)
{ {
struct input_event in; evdev_device_t * dsc = drv->user_data ? drv->user_data : &global_dsc;
if(dsc->fd < 0) return;
while(read(evdev_fd, &in, sizeof(struct input_event)) > 0) { /*Update dsc with buffered events*/
struct input_event in = { 0 };
while(read(dsc->fd, &in, sizeof(in)) > 0) {
if(in.type == EV_REL) { if(in.type == EV_REL) {
if(in.code == REL_X) if(in.code == REL_X) dsc->root_x += in.value;
#if EVDEV_SWAP_AXES else if(in.code == REL_Y) dsc->root_y += in.value;
evdev_root_y += in.value; }
#else else if(in.type == EV_ABS) {
evdev_root_x += in.value; if(in.code == ABS_X || in.code == ABS_MT_POSITION_X) dsc->root_x = in.value;
#endif else if(in.code == ABS_Y || in.code == ABS_MT_POSITION_Y) dsc->root_y = in.value;
else if(in.code == REL_Y)
#if EVDEV_SWAP_AXES
evdev_root_x += in.value;
#else
evdev_root_y += in.value;
#endif
} else if(in.type == EV_ABS) {
if(in.code == ABS_X)
#if EVDEV_SWAP_AXES
evdev_root_y = in.value;
#else
evdev_root_x = in.value;
#endif
else if(in.code == ABS_Y)
#if EVDEV_SWAP_AXES
evdev_root_x = in.value;
#else
evdev_root_y = in.value;
#endif
else if(in.code == ABS_MT_POSITION_X)
#if EVDEV_SWAP_AXES
evdev_root_y = in.value;
#else
evdev_root_x = in.value;
#endif
else if(in.code == ABS_MT_POSITION_Y)
#if EVDEV_SWAP_AXES
evdev_root_x = in.value;
#else
evdev_root_y = in.value;
#endif
else if(in.code == ABS_MT_TRACKING_ID) { else if(in.code == ABS_MT_TRACKING_ID) {
if(in.value == -1) if(in.value == -1) dsc->state = LV_INDEV_STATE_RELEASED;
evdev_button = LV_INDEV_STATE_REL; else if(in.value == 0) dsc->state = LV_INDEV_STATE_PRESSED;
else if(in.value == 0)
evdev_button = LV_INDEV_STATE_PR;
} }
} else if(in.type == EV_KEY) {
if(in.code == BTN_MOUSE || in.code == BTN_TOUCH) {
if(in.value == 0)
evdev_button = LV_INDEV_STATE_REL;
else if(in.value == 1)
evdev_button = LV_INDEV_STATE_PR;
} else if(drv->type == LV_INDEV_TYPE_KEYPAD) {
#if USE_XKB
data->key = xkb_process_key(in.code, in.value != 0);
#else
switch(in.code) {
case KEY_BACKSPACE:
data->key = LV_KEY_BACKSPACE;
break;
case KEY_ENTER:
data->key = LV_KEY_ENTER;
break;
case KEY_PREVIOUS:
data->key = LV_KEY_PREV;
break;
case KEY_NEXT:
data->key = LV_KEY_NEXT;
break;
case KEY_UP:
data->key = LV_KEY_UP;
break;
case KEY_LEFT:
data->key = LV_KEY_LEFT;
break;
case KEY_RIGHT:
data->key = LV_KEY_RIGHT;
break;
case KEY_DOWN:
data->key = LV_KEY_DOWN;
break;
case KEY_TAB:
data->key = LV_KEY_NEXT;
break;
default:
data->key = 0;
break;
} }
#endif /* USE_XKB */ else if(in.type == EV_KEY) {
if (data->key != 0) { if(in.code == BTN_MOUSE || in.code == BTN_TOUCH) {
/* Only record button state when actual output is produced to prevent widgets from refreshing */ if(in.value == 0) dsc->state = LV_INDEV_STATE_RELEASED;
data->state = (in.value) ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL; else if(in.value == 1) dsc->state = LV_INDEV_STATE_PRESSED;
} }
evdev_key_val = data->key; else {
evdev_button = data->state; dsc->key = _evdev_process_key(in.code, in.value != 0);
return; if(dsc->key) {
dsc->state = in.value ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED;
data->continue_reading = true; /*Keep following events in buffer for now*/
break;
} }
} }
} }
if(drv->type == LV_INDEV_TYPE_KEYPAD) {
/* No data retrieved */
data->key = evdev_key_val;
data->state = evdev_button;
return;
} }
if(drv->type != LV_INDEV_TYPE_POINTER)
return ;
/*Store the collected data*/
#if EVDEV_CALIBRATE
data->point.x = map(evdev_root_x, EVDEV_HOR_MIN, EVDEV_HOR_MAX, 0, drv->disp->driver->hor_res);
data->point.y = map(evdev_root_y, EVDEV_VER_MIN, EVDEV_VER_MAX, 0, drv->disp->driver->ver_res);
#else
data->point.x = evdev_root_x;
data->point.y = evdev_root_y;
#endif
data->state = evdev_button;
if(data->point.x < 0) /*Process and store in data*/
data->point.x = 0; switch(drv->type) {
if(data->point.y < 0) case LV_INDEV_TYPE_KEYPAD:
data->point.y = 0; data->state = dsc->state;
if(data->point.x >= drv->disp->driver->hor_res) data->key = dsc->key;
data->point.x = drv->disp->driver->hor_res - 1; break;
if(data->point.y >= drv->disp->driver->ver_res) case LV_INDEV_TYPE_POINTER:
data->point.y = drv->disp->driver->ver_res - 1; data->state = dsc->state;
data->point = _evdev_process_pointer(drv, dsc->root_x, dsc->root_y);
return ; break;
default:
break;
} }
/**********************
* STATIC FUNCTIONS
**********************/
int map(int x, int in_min, int in_max, int out_min, int out_max)
{
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
} }
#endif #endif /*USE_EVDEV*/

@ -29,39 +29,81 @@ extern "C" {
#include "lvgl/lvgl.h" #include "lvgl/lvgl.h"
#endif #endif
/*********************
* DEFINES
*********************/
/********************** /**********************
* TYPEDEFS * TYPEDEFS
**********************/ **********************/
typedef struct {
/*Device*/
int fd;
/*Config*/
bool swap_axes;
int ver_min;
int hor_min;
int ver_max;
int hor_max;
/*State*/
int root_x;
int root_y;
int key;
lv_indev_state_t state;
} evdev_device_t;
/********************** /**********************
* GLOBAL PROTOTYPES * GLOBAL PROTOTYPES
**********************/ **********************/
/** /**
* Initialize the evdev * Initialize the global evdev device, as configured with EVDEV_NAME,
* EVDEV_SWAP_AXES, and EVDEV_CALIBRATE.
*/ */
void evdev_init(void); void evdev_init();
/** /**
* reconfigure the device file for evdev * Initialize an evdev device.
* @param dev_name set the evdev device filename * @param dsc evdev device
* @return true: the device file set complete
* false: the device file doesn't exist current system
*/ */
bool evdev_set_file(char* dev_name); void evdev_device_init(evdev_device_t * dsc);
/** /**
* Get the current position and state of the evdev * Reconfigure the path for the global evdev device.
* @param data store the evdev data here * @param dev_path device path, e.g., /dev/input/event0, or NULL to close
* @return whether the device was successfully opened
*/ */
void evdev_read(lv_indev_drv_t * drv, lv_indev_data_t * data); bool evdev_set_file(const char * dev_path);
/**
* Configure or reconfigure the path for an evdev device.
* @param dsc evdev device
* @param dev_path device path, e.g., /dev/input/event0, or NULL to close
* @return whether the device was successfully opened
*/
bool evdev_device_set_file(evdev_device_t * dsc, const char * dev_path);
/**
* Configure whether pointer coordinates of an evdev device sould be swapped.
* Default to false.
* @param dsc evdev device
* @param swap_axes whether to swap x and y axes
*/
void evdev_device_set_swap_axes(evdev_device_t * dsc, bool swap_axes);
/**
* Configure a coordinate transformation for an evdev device. Applied after
* axis swap, if any. Defaults to no transformation.
* @param dsc evdev device
* @param hor_min horizontal pointer coordinate mapped to 0
* @param ver_min vertical pointer coordinate mapped to 0
* @param ver_min pointer coordinate mapped to horizontal max of display
* @param ver_max pointer coordinate mapped to vertical max of display
*/
void evdev_device_set_calibration(evdev_device_t * dsc, int hor_min, int ver_min, int hor_max, int ver_max);
/********************** /**
* MACROS * Read callback for the input driver.
**********************/ * @param drv input driver where drv->user_data is NULL for the global evdev
* device or an evdev_device_t pointer.
* @param data destination for input events
*/
void evdev_read(lv_indev_drv_t * drv, lv_indev_data_t * data);
#endif /* USE_EVDEV */ #endif /* USE_EVDEV */

@ -74,7 +74,28 @@ static const struct libinput_interface interface = {
**********************/ **********************/
/** /**
* find connected input device with specific capabilities * Determine the capabilities of a specific libinput device.
* @param device the libinput device to query
* @return the supported input capabilities
*/
libinput_capability libinput_query_capability(struct libinput_device *device) {
libinput_capability capability = LIBINPUT_CAPABILITY_NONE;
if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_KEYBOARD)
&& (libinput_device_keyboard_has_key(device, KEY_ENTER) || libinput_device_keyboard_has_key(device, KEY_KPENTER)))
{
capability |= LIBINPUT_CAPABILITY_KEYBOARD;
}
if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_POINTER)) {
capability |= LIBINPUT_CAPABILITY_POINTER;
}
if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_TOUCH)) {
capability |= LIBINPUT_CAPABILITY_TOUCH;
}
return capability;
}
/**
* Find connected input device with specific capabilities
* @param capabilities required device capabilities * @param capabilities required device capabilities
* @param force_rescan erase the device cache (if any) and rescan the file system for available devices * @param force_rescan erase the device cache (if any) and rescan the file system for available devices
* @return device node path (e.g. /dev/input/event0) for the first matching device or NULL if no device was found. * @return device node path (e.g. /dev/input/event0) for the first matching device or NULL if no device was found.
@ -87,7 +108,7 @@ char *libinput_find_dev(libinput_capability capabilities, bool force_rescan) {
} }
/** /**
* find connected input devices with specific capabilities * Find connected input devices with specific capabilities
* @param capabilities required device capabilities * @param capabilities required device capabilities
* @param devices pre-allocated array to store the found device node paths (e.g. /dev/input/event0). The pointers are * @param devices pre-allocated array to store the found device node paths (e.g. /dev/input/event0). The pointers are
* safe to use until the next forceful device search. * safe to use until the next forceful device search.
@ -138,8 +159,8 @@ bool libinput_set_file_state(libinput_drv_state_t *state, char* dev_name)
// citing libinput.h:libinput_path_remove_device: // citing libinput.h:libinput_path_remove_device:
// > If no matching device exists, this function does nothing. // > If no matching device exists, this function does nothing.
if (state->libinput_device) { if (state->libinput_device) {
state->libinput_device = libinput_device_unref(state->libinput_device);
libinput_path_remove_device(state->libinput_device); libinput_path_remove_device(state->libinput_device);
state->libinput_device = libinput_device_unref(state->libinput_device);
} }
state->libinput_device = libinput_path_add_device(state->libinput_context, dev_name); state->libinput_device = libinput_path_add_device(state->libinput_context, dev_name);
@ -195,6 +216,29 @@ void libinput_init_state(libinput_drv_state_t *state, char* path)
#endif #endif
} }
/**
* De-initialise a previously initialised driver state and free any dynamically allocated memory. Use this function if you want to
* reuse an existing driver state.
* @param state driver state to de-initialize
*/
void libinput_deinit_state(libinput_drv_state_t *state)
{
if (state->libinput_device) {
libinput_path_remove_device(state->libinput_device);
libinput_device_unref(state->libinput_device);
}
if (state->libinput_context) {
libinput_unref(state->libinput_context);
}
#if USE_XKB
xkb_deinit_state(&(state->xkb_state));
#endif
lv_memzero(state, sizeof(libinput_drv_state_t));
}
/** /**
* Read available input events via libinput using the default driver state. Use this function if you only want * Read available input events via libinput using the default driver state. Use this function if you only want
* to connect a single device. * to connect a single device.
@ -296,18 +340,7 @@ static bool rescan_devices(void) {
* as part of this function, we don't have to increase its reference count to keep it alive. * as part of this function, we don't have to increase its reference count to keep it alive.
* https://wayland.freedesktop.org/libinput/doc/latest/api/group__base.html#gaa797496f0150b482a4e01376bd33a47b */ * https://wayland.freedesktop.org/libinput/doc/latest/api/group__base.html#gaa797496f0150b482a4e01376bd33a47b */
libinput_capability capabilities = LIBINPUT_CAPABILITY_NONE; libinput_capability capabilities = libinput_query_capability(device);
if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_KEYBOARD)
&& (libinput_device_keyboard_has_key(device, KEY_ENTER) || libinput_device_keyboard_has_key(device, KEY_KPENTER)))
{
capabilities |= LIBINPUT_CAPABILITY_KEYBOARD;
}
if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_POINTER)) {
capabilities |= LIBINPUT_CAPABILITY_POINTER;
}
if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_TOUCH)) {
capabilities |= LIBINPUT_CAPABILITY_TOUCH;
}
libinput_path_remove_device(device); libinput_path_remove_device(device);
@ -387,13 +420,13 @@ static void read_pointer(libinput_drv_state_t *state, struct libinput_event *eve
case LIBINPUT_EVENT_TOUCH_MOTION: case LIBINPUT_EVENT_TOUCH_MOTION:
case LIBINPUT_EVENT_TOUCH_DOWN: case LIBINPUT_EVENT_TOUCH_DOWN:
touch_event = libinput_event_get_touch_event(event); touch_event = libinput_event_get_touch_event(event);
lv_coord_t x = libinput_event_touch_get_x_transformed(touch_event, drv->physical_hor_res > 0 ? drv->physical_hor_res : drv->hor_res) - drv->offset_x; lv_coord_t x_touch = libinput_event_touch_get_x_transformed(touch_event, drv->physical_hor_res > 0 ? drv->physical_hor_res : drv->hor_res) - drv->offset_x;
lv_coord_t y = libinput_event_touch_get_y_transformed(touch_event, drv->physical_ver_res > 0 ? drv->physical_ver_res : drv->ver_res) - drv->offset_y; lv_coord_t y_touch = libinput_event_touch_get_y_transformed(touch_event, drv->physical_ver_res > 0 ? drv->physical_ver_res : drv->ver_res) - drv->offset_y;
if (x < 0 || x > drv->hor_res || y < 0 || y > drv->ver_res) { if (x_touch < 0 || x_touch > drv->hor_res || y_touch < 0 || y_touch > drv->ver_res) {
break; /* ignore touches that are out of bounds */ break; /* ignore touches that are out of bounds */
} }
state->most_recent_touch_point.x = x; state->most_recent_touch_point.x = x_touch;
state->most_recent_touch_point.y = y; state->most_recent_touch_point.y = y_touch;
state->button = LV_INDEV_STATE_PR; state->button = LV_INDEV_STATE_PR;
break; break;
case LIBINPUT_EVENT_TOUCH_UP: case LIBINPUT_EVENT_TOUCH_UP:
@ -406,6 +439,16 @@ static void read_pointer(libinput_drv_state_t *state, struct libinput_event *eve
state->most_recent_touch_point.x = LV_CLAMP(0, state->most_recent_touch_point.x, drv->hor_res - 1); state->most_recent_touch_point.x = LV_CLAMP(0, state->most_recent_touch_point.x, drv->hor_res - 1);
state->most_recent_touch_point.y = LV_CLAMP(0, state->most_recent_touch_point.y, drv->ver_res - 1); state->most_recent_touch_point.y = LV_CLAMP(0, state->most_recent_touch_point.y, drv->ver_res - 1);
break; break;
case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE:
pointer_event = libinput_event_get_pointer_event(event);
lv_coord_t x_pointer = libinput_event_pointer_get_absolute_x_transformed(pointer_event, drv->physical_hor_res > 0 ? drv->physical_hor_res : drv->hor_res) - drv->offset_x;
lv_coord_t y_pointer = libinput_event_pointer_get_absolute_y_transformed(pointer_event, drv->physical_ver_res > 0 ? drv->physical_ver_res : drv->ver_res) - drv->offset_y;
if (x_pointer < 0 || x_pointer > drv->hor_res || y_pointer < 0 || y_pointer > drv->ver_res) {
break; /* ignore pointer events that are out of bounds */
}
state->most_recent_touch_point.x = x_pointer;
state->most_recent_touch_point.y = y_pointer;
break;
case LIBINPUT_EVENT_POINTER_BUTTON: case LIBINPUT_EVENT_POINTER_BUTTON:
pointer_event = libinput_event_get_pointer_event(event); pointer_event = libinput_event_get_pointer_event(event);
enum libinput_button_state button_state = libinput_event_pointer_get_button_state(pointer_event); enum libinput_button_state button_state = libinput_event_pointer_get_button_state(pointer_event);

@ -70,7 +70,13 @@ typedef struct {
**********************/ **********************/
/** /**
* find connected input device with specific capabilities * Determine the capabilities of a specific libinput device.
* @param device the libinput device to query
* @return the supported input capabilities
*/
libinput_capability libinput_query_capability(struct libinput_device *device);
/**
* Find connected input device with specific capabilities
* @param capabilities required device capabilities * @param capabilities required device capabilities
* @param force_rescan erase the device cache (if any) and rescan the file system for available devices * @param force_rescan erase the device cache (if any) and rescan the file system for available devices
* @return device node path (e.g. /dev/input/event0) for the first matching device or NULL if no device was found. * @return device node path (e.g. /dev/input/event0) for the first matching device or NULL if no device was found.
@ -78,7 +84,7 @@ typedef struct {
*/ */
char *libinput_find_dev(libinput_capability capabilities, bool force_rescan); char *libinput_find_dev(libinput_capability capabilities, bool force_rescan);
/** /**
* find connected input devices with specific capabilities * Find connected input devices with specific capabilities
* @param capabilities required device capabilities * @param capabilities required device capabilities
* @param devices pre-allocated array to store the found device node paths (e.g. /dev/input/event0). The pointers are * @param devices pre-allocated array to store the found device node paths (e.g. /dev/input/event0). The pointers are
* safe to use until the next forceful device search. * safe to use until the next forceful device search.
@ -99,6 +105,12 @@ void libinput_init(void);
* @param path input device node path (e.g. /dev/input/event0) * @param path input device node path (e.g. /dev/input/event0)
*/ */
void libinput_init_state(libinput_drv_state_t *state, char* path); void libinput_init_state(libinput_drv_state_t *state, char* path);
/**
* De-initialise a previously initialised driver state and free any dynamically allocated memory. Use this function if you want to
* reuse an existing driver state.
* @param state driver state to de-initialize
*/
void libinput_deinit_state(libinput_drv_state_t *state);
/** /**
* Reconfigure the device file for libinput using the default driver state. Use this function if you only want * Reconfigure the device file for libinput using the default driver state. Use this function if you only want
* to connect a single device. * to connect a single device.

@ -73,6 +73,23 @@ bool xkb_init_state(xkb_drv_state_t *state) {
#endif #endif
} }
/**
* De-initialise a previously initialised driver state and free any dynamically allocated memory. Use this function if you want to
* reuse an existing driver state.
* @param state XKB driver state to use
*/
void xkb_deinit_state(xkb_drv_state_t *state) {
if (state->state) {
xkb_state_unref(state->state);
state->state = NULL;
}
if (state->keymap) {
xkb_keymap_unref(state->keymap);
state->keymap = NULL;
}
}
/** /**
* Set a new keymap to be used for processing future key events using the default driver state. Use * Set a new keymap to be used for processing future key events using the default driver state. Use
* this function if you only want to connect a single device. * this function if you only want to connect a single device.
@ -102,12 +119,6 @@ bool xkb_set_keymap_state(xkb_drv_state_t *state, struct xkb_rule_names names) {
return false; return false;
} }
state->keymap = xkb_keymap_ref(state->keymap);
if (!state->keymap) {
perror("could not reference XKB keymap");
return false;
}
if (state->state) { if (state->state) {
xkb_state_unref(state->state); xkb_state_unref(state->state);
state->state = NULL; state->state = NULL;
@ -119,12 +130,6 @@ bool xkb_set_keymap_state(xkb_drv_state_t *state, struct xkb_rule_names names) {
return false; return false;
} }
state->state = xkb_state_ref(state->state);
if (!state->state) {
perror("could not reference XKB state");
return false;
}
return true; return true;
} }

@ -60,6 +60,12 @@ bool xkb_init(void);
* @return true if the initialisation was successful * @return true if the initialisation was successful
*/ */
bool xkb_init_state(xkb_drv_state_t *state); bool xkb_init_state(xkb_drv_state_t *state);
/**
* De-initialise a previously initialised driver state and free any dynamically allocated memory. Use this function if you want to
* reuse an existing driver state.
* @param state XKB driver state to use
*/
void xkb_deinit_state(xkb_drv_state_t *state);
/** /**
* Set a new keymap to be used for processing future key events using the default driver state. Use * Set a new keymap to be used for processing future key events using the default driver state. Use
* this function if you only want to connect a single device. * this function if you only want to connect a single device.

@ -1,6 +1,6 @@
{ {
"name": "lv_drivers", "name": "lv_drivers",
"version": "8.3.0", "version": "9.0.0-dev",
"keywords": "littlevgl, lvgl, driver, display, touchpad", "keywords": "littlevgl, lvgl, driver, display, touchpad",
"description": "Drivers for LittlevGL graphics library.", "description": "Drivers for LittlevGL graphics library.",
"repository": { "repository": {

@ -0,0 +1,11 @@
prefix="@CMAKE_INSTALL_PREFIX@"
includedir="${prefix}/@INC_INSTALL_DIR@"
libdir=${prefix}/lib
Name: lv-drivers
Description: Display controller and touchpad driver that can be directly used with LVGL
URL: https://lvgl.io/
Version: 9.0.0
Cflags: -I${includedir}
Libs: -L${libdir} -llv_drivers
Requires: lvgl

@ -1,10 +1,5 @@
LV_DRIVERS_DIR_NAME ?= lv_drivers LV_DRIVERS_PATH ?= ${shell pwd}/lv_drivers
override CFLAGS := -I$(LVGL_DIR) $(CFLAGS) CSRCS += $(shell find $(LV_DRIVERS_PATH) -type f -name '*.c')
CFLAGS += "-I$(LV_DRIVERS_PATH)"
CSRCS += $(wildcard $(LVGL_DIR)/$(LV_DRIVERS_DIR_NAME)/*.c)
CSRCS += $(wildcard $(LVGL_DIR)/$(LV_DRIVERS_DIR_NAME)/wayland/*.c)
CSRCS += $(wildcard $(LVGL_DIR)/$(LV_DRIVERS_DIR_NAME)/indev/*.c)
CSRCS += $(wildcard $(LVGL_DIR)/$(LV_DRIVERS_DIR_NAME)/gtkdrv/*.c)
CSRCS += $(wildcard $(LVGL_DIR)/$(LV_DRIVERS_DIR_NAME)/display/*.c)
CSRCS += $(wildcard $(LVGL_DIR)/$(LV_DRIVERS_DIR_NAME)/sdl/*.c)

@ -1,6 +1,6 @@
/** /**
* @file lv_drv_conf.h * @file lv_drv_conf.h
* Configuration file for v8.3.0 * Configuration file for v9.0.0-dev
*/ */
/* /*
@ -110,6 +110,9 @@
/*Open two windows to test multi display support*/ /*Open two windows to test multi display support*/
# define SDL_DUAL_DISPLAY 0 # define SDL_DUAL_DISPLAY 0
/* Window Title */
# define SDL_WINDOW_TITLE "TFT Simulator"
#endif #endif
/*------------------- /*-------------------
@ -192,6 +195,13 @@
# endif # endif
#endif #endif
/*----------------------------------------
* X11 drivers (monitor, mouse, keyboard)
*---------------------------------------*/
#ifndef USE_X11
# define USE_X11 0
#endif
/*---------------- /*----------------
* SSD1963 * SSD1963
*--------------*/ *--------------*/

@ -9,8 +9,8 @@
#include "sdl.h" #include "sdl.h"
#if USE_MONITOR || USE_SDL #if USE_MONITOR || USE_SDL
#if LV_USE_GPU_SDL #if LV_USE_DRAW_SDL
# error "LV_USE_GPU_SDL must not be enabled" # error "LV_USE_DRAW_SDL must not be enabled"
#endif #endif
#if USE_MONITOR #if USE_MONITOR
@ -47,6 +47,7 @@
# define SDL_FULLSCREEN 0 # define SDL_FULLSCREEN 0
#endif #endif
#include "sdl_common_internal.h"
#include <stdlib.h> #include <stdlib.h>
#include <stdbool.h> #include <stdbool.h>
#include <string.h> #include <string.h>
@ -59,6 +60,9 @@
#define KEYBOARD_BUFFER_SIZE SDL_TEXTINPUTEVENT_TEXT_SIZE #define KEYBOARD_BUFFER_SIZE SDL_TEXTINPUTEVENT_TEXT_SIZE
#endif #endif
#ifndef SDL_WINDOW_TITLE
#define SDL_WINDOW_TITLE "TFT Simulator"
#endif
/********************** /**********************
* TYPEDEFS * TYPEDEFS
**********************/ **********************/
@ -96,17 +100,6 @@ monitor_t monitor;
monitor_t monitor2; monitor_t monitor2;
#endif #endif
static volatile bool sdl_inited = false;
static bool left_button_down = false;
static int16_t last_x = 0;
static int16_t last_y = 0;
static int16_t wheel_diff = 0;
static lv_indev_state_t wheel_state = LV_INDEV_STATE_RELEASED;
static char buf[KEYBOARD_BUFFER_SIZE];
/********************** /**********************
* MACROS * MACROS
**********************/ **********************/
@ -344,7 +337,7 @@ static void window_create(monitor_t * m)
flag |= SDL_WINDOW_FULLSCREEN; flag |= SDL_WINDOW_FULLSCREEN;
#endif #endif
m->window = SDL_CreateWindow("TFT Simulator", m->window = SDL_CreateWindow(SDL_WINDOW_TITLE,
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
SDL_HOR_RES * SDL_ZOOM, SDL_VER_RES * SDL_ZOOM, flag); /*last param. SDL_WINDOW_BORDERLESS to hide borders*/ SDL_HOR_RES * SDL_ZOOM, SDL_VER_RES * SDL_ZOOM, flag); /*last param. SDL_WINDOW_BORDERLESS to hide borders*/
@ -375,7 +368,7 @@ static void window_update(monitor_t * m)
#endif #endif
SDL_RenderClear(m->renderer); SDL_RenderClear(m->renderer);
lv_disp_t * d = _lv_refr_get_disp_refreshing(); lv_disp_t * d = _lv_refr_get_disp_refreshing();
if(d->driver->screen_transp) { if(d && d->driver->screen_transp) {
SDL_SetRenderDrawColor(m->renderer, 0xff, 0, 0, 0xff); SDL_SetRenderDrawColor(m->renderer, 0xff, 0, 0, 0xff);
SDL_Rect r; SDL_Rect r;
r.x = 0; r.y = 0; r.w = SDL_HOR_RES; r.h = SDL_VER_RES; r.x = 0; r.y = 0; r.w = SDL_HOR_RES; r.h = SDL_VER_RES;

@ -5,6 +5,8 @@
#include "sdl_common.h" #include "sdl_common.h"
#if USE_SDL || USE_SDL_GPU #if USE_SDL || USE_SDL_GPU
#include "sdl_common_internal.h"
/********************* /*********************
* DEFINES * DEFINES
*********************/ *********************/
@ -130,17 +132,17 @@ void mouse_handler(SDL_Event * event)
case SDL_FINGERUP: case SDL_FINGERUP:
left_button_down = false; left_button_down = false;
last_x = LV_HOR_RES * event->tfinger.x / SDL_ZOOM; last_x = SDL_HOR_RES * event->tfinger.x / SDL_ZOOM;
last_y = LV_VER_RES * event->tfinger.y / SDL_ZOOM; last_y = SDL_VER_RES * event->tfinger.y / SDL_ZOOM;
break; break;
case SDL_FINGERDOWN: case SDL_FINGERDOWN:
left_button_down = true; left_button_down = true;
last_x = LV_HOR_RES * event->tfinger.x / SDL_ZOOM; last_x = SDL_HOR_RES * event->tfinger.x / SDL_ZOOM;
last_y = LV_VER_RES * event->tfinger.y / SDL_ZOOM; last_y = SDL_VER_RES * event->tfinger.y / SDL_ZOOM;
break; break;
case SDL_FINGERMOTION: case SDL_FINGERMOTION:
last_x = LV_HOR_RES * event->tfinger.x / SDL_ZOOM; last_x = SDL_HOR_RES * event->tfinger.x / SDL_ZOOM;
last_y = LV_VER_RES * event->tfinger.y / SDL_ZOOM; last_y = SDL_VER_RES * event->tfinger.y / SDL_ZOOM;
break; break;
} }

@ -32,7 +32,6 @@ extern "C" {
#ifndef SDL_INCLUDE_PATH #ifndef SDL_INCLUDE_PATH
#define SDL_INCLUDE_PATH MONITOR_SDL_INCLUDE_PATH #define SDL_INCLUDE_PATH MONITOR_SDL_INCLUDE_PATH
#endif #endif
#include SDL_INCLUDE_PATH
#ifndef SDL_ZOOM #ifndef SDL_ZOOM
#define SDL_ZOOM MONITOR_ZOOM #define SDL_ZOOM MONITOR_ZOOM
@ -85,16 +84,6 @@ void sdl_mousewheel_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
*/ */
void sdl_keyboard_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data); void sdl_keyboard_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
int quit_filter(void * userdata, SDL_Event * event);
void mouse_handler(SDL_Event * event);
void mousewheel_handler(SDL_Event * event);
uint32_t keycode_to_ctrl_key(SDL_Keycode sdl_key);
void keyboard_handler(SDL_Event * event);
/**********************
* MACROS
**********************/
#endif /* USE_SDL || USE_SDL_GPU */ #endif /* USE_SDL || USE_SDL_GPU */
#ifdef __cplusplus #ifdef __cplusplus

@ -0,0 +1,39 @@
/**
* @file sdl_common_internal.h
* Provides SDL related functions which are only used internal.
*
*/
#ifndef SDL_COMMON_INTERNAL_H
#define SDL_COMMON_INTERNAL_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "sdl_common.h"
#if USE_SDL || USE_SDL_GPU
#include SDL_INCLUDE_PATH
/**********************
* GLOBAL PROTOTYPES
**********************/
int quit_filter(void * userdata, SDL_Event * event);
void mouse_handler(SDL_Event * event);
void mousewheel_handler(SDL_Event * event);
uint32_t keycode_to_ctrl_key(SDL_Keycode sdl_key);
void keyboard_handler(SDL_Event * event);
#endif /* USE_SDL || USE_SDL_GPU */
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* SDL_COMMON_INTERNAL_H */

@ -9,7 +9,7 @@
#include "sdl_gpu.h" #include "sdl_gpu.h"
#if USE_SDL_GPU #if USE_SDL_GPU
#if LV_USE_GPU_SDL == 0 #if LV_USE_DRAW_SDL == 0
# error "LV_USE_DRAW_SDL must be enabled" # error "LV_USE_DRAW_SDL must be enabled"
#endif #endif
@ -29,6 +29,7 @@
# error "Cannot enable both MONITOR and SDL at the same time. " # error "Cannot enable both MONITOR and SDL at the same time. "
#endif #endif
#include "sdl_common_internal.h"
#include <stdlib.h> #include <stdlib.h>
#include <stdbool.h> #include <stdbool.h>
#include <string.h> #include <string.h>
@ -90,14 +91,14 @@ void sdl_init(void)
void sdl_disp_drv_init(lv_disp_drv_t * disp_drv, lv_coord_t hor_res, lv_coord_t ver_res) void sdl_disp_drv_init(lv_disp_drv_t * disp_drv, lv_coord_t hor_res, lv_coord_t ver_res)
{ {
monitor_t *m = lv_mem_alloc(sizeof(monitor_t)); monitor_t *m = lv_malloc(sizeof(monitor_t));
window_create(m); window_create(m);
lv_disp_drv_init(disp_drv); lv_disp_drv_init(disp_drv);
disp_drv->direct_mode = 1; disp_drv->direct_mode = 1;
disp_drv->flush_cb = monitor_flush; disp_drv->flush_cb = monitor_flush;
disp_drv->hor_res = hor_res; disp_drv->hor_res = hor_res;
disp_drv->ver_res = ver_res; disp_drv->ver_res = ver_res;
lv_disp_draw_buf_t *disp_buf = lv_mem_alloc(sizeof(lv_disp_draw_buf_t)); lv_disp_draw_buf_t *disp_buf = lv_malloc(sizeof(lv_disp_draw_buf_t));
lv_disp_draw_buf_init(disp_buf, m->texture, NULL, hor_res * ver_res); lv_disp_draw_buf_init(disp_buf, m->texture, NULL, hor_res * ver_res);
disp_drv->draw_buf = disp_buf; disp_drv->draw_buf = disp_buf;
disp_drv->antialiasing = 1; disp_drv->antialiasing = 1;

@ -8,7 +8,7 @@ pkg_check_modules(xkbcommon REQUIRED xkbcommon)
# Wayland protocols # Wayland protocols
find_program(WAYLAND_SCANNER_EXECUTABLE NAMES wayland-scanner) find_program(WAYLAND_SCANNER_EXECUTABLE NAMES wayland-scanner)
pkg_check_modules(WAYLAND_PROTOCOLS REQUIRED wayland-protocols>=1.15) pkg_check_modules(WAYLAND_PROTOCOLS REQUIRED wayland-protocols>=1.25)
pkg_get_variable(WAYLAND_PROTOCOLS_BASE wayland-protocols pkgdatadir) pkg_get_variable(WAYLAND_PROTOCOLS_BASE wayland-protocols pkgdatadir)
macro(wayland_generate protocol_xml_file output_dir target) macro(wayland_generate protocol_xml_file output_dir target)

@ -0,0 +1,652 @@
/**
* @file smm.c
*
*/
#if USE_WAYLAND
#include <stddef.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "smm.h"
#define MAX_NAME_ATTEMPTS (5)
#define PREFER_NUM_BUFFERS (3)
#define ROUND_UP(n, b) (((((n) ? (n) : 1) + (b) - 1) / (b)) * (b))
#define LLHEAD(type) \
struct { \
struct type *first; \
struct type *last; \
}
#define LLLINK(type) \
struct { \
struct type *next; \
struct type *prev; \
}
#define LL_FIRST(head) ((head)->first)
#define LL_LAST(head) ((head)->last)
#define LL_IS_EMPTY(head) (LL_FIRST(head) == NULL)
#define LL_NEXT(src, member) ((src)->member.next)
#define LL_PREV(src, member) ((src)->member.prev)
#define LL_INIT(head) do { \
(head)->first = NULL; \
(head)->last = NULL; \
} while (0)
#define LL_ENQUEUE(head, src, member) do { \
(src)->member.next = NULL; \
(src)->member.prev = (head)->last; \
if ((head)->last == NULL) { \
(head)->first = (src); \
} else { \
(head)->last->member.next = (src); \
} \
(head)->last = (src); \
} while (0)
#define LL_DEQUEUE(entry, head, member) do { \
(entry) = LL_FIRST(head); \
LL_REMOVE(head, entry, member); \
} while (0)
#define LL_INSERT_AFTER(head, dest, src, member) do { \
(src)->member.prev = (dest); \
(src)->member.next = (dest)->member.next; \
if ((dest)->member.next != NULL) { \
(dest)->member.next->member.prev = (src); \
} else { \
(head)->last = (src); \
} \
(dest)->member.next = (src); \
} while (0)
#define LL_REMOVE(head, src, member) do { \
if ((src)->member.prev != NULL) { \
(src)->member.prev->member.next = (src)->member.next; \
} else { \
(head)->first = (src)->member.next; \
} \
if ((src)->member.next != NULL) { \
(src)->member.next->member.prev = (src)->member.prev; \
} else { \
(head)->last = (src)->member.prev; \
} \
} while (0)
#define LL_FOREACH(entry, head, member) \
for ((entry) = LL_FIRST(head); \
(entry) != NULL; \
(entry) = LL_NEXT(entry, member))
struct smm_pool {
struct smm_pool_properties props;
LLHEAD(smm_buffer) allocd;
void *map;
size_t map_size;
bool map_outdated;
};
struct smm_buffer {
struct smm_buffer_properties props;
bool group_resized;
LLLINK(smm_buffer) pool;
LLLINK(smm_buffer) use;
LLLINK(smm_buffer) age;
};
struct smm_group {
struct smm_group_properties props;
size_t size;
unsigned char num_buffers;
LLHEAD(smm_buffer) unused;
LLHEAD(smm_buffer) inuse;
LLHEAD(smm_buffer) history;
LLLINK(smm_group) link;
};
static size_t calc_buffer_size(struct smm_buffer *buf);
static void purge_history(struct smm_buffer *buf);
static struct smm_buffer *get_from_pool(struct smm_group *grp);
static void return_to_pool(struct smm_buffer *buf);
static struct smm_pool *alloc_pool(void);
static void free_pool(struct smm_pool *pool);
static struct smm_buffer *alloc_buffer(struct smm_buffer *last, size_t offset);
static void free_buffer(struct smm_buffer *buf);
static struct {
unsigned long page_sz;
struct smm_events cbs;
struct smm_pool *active;
LLHEAD(smm_group) groups;
struct {
size_t active_used;
} statistics;
} smm_instance;
void smm_init(struct smm_events *evs)
{
memcpy(&smm_instance.cbs, evs, sizeof(struct smm_events));
srand((unsigned int)clock());
smm_instance.page_sz = (unsigned long)sysconf(_SC_PAGESIZE);
LL_INIT(&smm_instance.groups);
}
void smm_deinit(void)
{
struct smm_group *grp;
/* Destroy all buffer groups */
while (!LL_IS_EMPTY(&smm_instance.groups)) {
LL_DEQUEUE(grp, &smm_instance.groups, link);
smm_destroy(grp);
}
}
void smm_setctx(void *ctx)
{
smm_instance.cbs.ctx = ctx;
}
smm_group_t *smm_create(void)
{
struct smm_group *grp;
/* Allocate and intialize a new buffer group */
grp = malloc(sizeof(struct smm_group));
if (grp != NULL) {
grp->size = smm_instance.page_sz;
grp->num_buffers = 0;
LL_INIT(&grp->unused);
LL_INIT(&grp->inuse);
LL_INIT(&grp->history);
/* Add to instance groups queue */
LL_ENQUEUE(&smm_instance.groups, grp, link);
}
return grp;
}
void smm_resize(smm_group_t *grp, size_t sz)
{
struct smm_buffer *buf;
struct smm_group *rgrp = grp;
/* Round allocation size up to a sysconf(_SC_PAGE_SIZE) boundary */
rgrp->size = ROUND_UP(sz, smm_instance.page_sz);
/* Return all unused buffers to pool (to be re-allocated at the new size) */
while (!LL_IS_EMPTY(&rgrp->unused)) {
LL_DEQUEUE(buf, &rgrp->unused, use);
return_to_pool(buf);
}
/* Mark all buffers in use to be freed to pool when possible */
LL_FOREACH(buf, &rgrp->inuse, use) {
buf->group_resized = true;
purge_history(buf);
}
}
void smm_destroy(smm_group_t *grp)
{
struct smm_buffer *buf;
struct smm_group *dgrp = grp;
/* Return unused buffers */
while (!LL_IS_EMPTY(&dgrp->unused)) {
LL_DEQUEUE(buf, &dgrp->unused, use);
return_to_pool(buf);
}
/* Return buffers that are still in use (ideally this queue should be empty
* at this time)
*/
while (!LL_IS_EMPTY(&dgrp->inuse)) {
LL_DEQUEUE(buf, &dgrp->inuse, use);
return_to_pool(buf);
}
/* Remove from instance groups queue */
LL_REMOVE(&smm_instance.groups, dgrp, link);
free(dgrp);
}
smm_buffer_t *smm_acquire(smm_group_t *grp)
{
struct smm_buffer *buf;
struct smm_group *agrp = grp;
if (LL_IS_EMPTY(&agrp->unused)) {
/* No unused buffer available, so get a new one from pool */
buf = get_from_pool(agrp);
} else {
/* Otherwise, reuse an unused buffer */
LL_DEQUEUE(buf, &agrp->unused, use);
}
if (buf != NULL) {
/* Add buffer to in-use queue */
LL_ENQUEUE(&agrp->inuse, buf, use);
/* Emit 'init buffer' event */
if (smm_instance.cbs.init_buffer != NULL) {
if (smm_instance.cbs.init_buffer(smm_instance.cbs.ctx, &buf->props)) {
smm_release(buf);
buf = NULL;
}
}
if (buf != NULL) {
/* Remove from history */
purge_history(buf);
/* Add to history a-new */
LL_ENQUEUE(&agrp->history, buf, age);
}
}
return buf;
}
void *smm_map(smm_buffer_t *buf)
{
struct smm_buffer *mbuf = buf;
struct smm_pool *pool = mbuf->props.pool;
void *map = pool->map;
if (pool->map_outdated) {
/* Update mapping to current pool size */
if (pool->map != NULL) {
munmap(pool->map, pool->map_size);
}
map = mmap(NULL,
pool->props.size,
PROT_READ | PROT_WRITE,
MAP_SHARED,
pool->props.fd,
0);
if (map == MAP_FAILED) {
map = NULL;
pool->map = NULL;
} else {
pool->map = map;
pool->map_size = pool->props.size;
pool->map_outdated = false;
}
}
/* Calculate buffer mapping (from offset in pool) */
if (map != NULL) {
map = (((char *)map) + mbuf->props.offset);
}
return map;
}
void smm_release(smm_buffer_t *buf)
{
struct smm_buffer *rbuf = buf;
struct smm_group *grp = rbuf->props.group;
/* Remove from in-use queue */
LL_REMOVE(&grp->inuse, rbuf, use);
if (rbuf->group_resized) {
/* Buffer group was resized while this buffer was in-use, thus it must be
* returned to it's pool
*/
rbuf->group_resized = false;
return_to_pool(rbuf);
} else {
/* Move to unused queue */
LL_ENQUEUE(&grp->unused, rbuf, use);
/* Try to limit total number of buffers to preferred number */
while ((grp->num_buffers > PREFER_NUM_BUFFERS) &&
(!LL_IS_EMPTY(&grp->unused))) {
LL_DEQUEUE(rbuf, &grp->unused, use);
return_to_pool(rbuf);
}
}
}
smm_buffer_t *smm_latest(smm_group_t *grp)
{
struct smm_group *lgrp = grp;
return LL_LAST(&lgrp->history);
}
smm_buffer_t *smm_next(smm_buffer_t *buf)
{
struct smm_buffer *ibuf;
struct smm_buffer *nbuf = buf;
struct smm_group *grp = nbuf->props.group;
LL_FOREACH(ibuf, &grp->history, age) {
if (ibuf == nbuf) {
ibuf = LL_NEXT(ibuf, age);
break;
}
}
return ibuf;
}
void purge_history(struct smm_buffer *buf)
{
struct smm_buffer *ibuf;
struct smm_group *grp = buf->props.group;
/* Remove from history (and any older) */
LL_FOREACH(ibuf, &grp->history, age) {
if (ibuf == buf) {
do {
LL_DEQUEUE(ibuf, &grp->history, age);
} while (ibuf != buf);
break;
}
}
}
size_t calc_buffer_size(struct smm_buffer *buf)
{
size_t buf_sz;
struct smm_pool *buf_pool = buf->props.pool;
if (buf == LL_LAST(&buf_pool->allocd)) {
buf_sz = (buf_pool->props.size - buf->props.offset);
} else {
buf_sz = (LL_NEXT(buf, pool)->props.offset - buf->props.offset);
}
return buf_sz;
}
struct smm_buffer *get_from_pool(struct smm_group *grp)
{
int ret;
size_t buf_sz;
struct smm_buffer *buf;
struct smm_buffer *last = NULL;
/* TODO: Determine when to allocate a new active pool (i.e. memory shrink) */
if (smm_instance.active == NULL) {
/* Allocate a new active pool */
smm_instance.active = alloc_pool();
smm_instance.statistics.active_used = 0;
}
if (smm_instance.active == NULL) {
buf = NULL;
} else {
/* Search for a free buffer large enough for allocation */
LL_FOREACH(buf, &smm_instance.active->allocd, pool) {
last = buf;
if (buf->props.group == NULL) {
buf_sz = calc_buffer_size(buf);
if (buf_sz == grp->size) {
break;
} else if (buf_sz > grp->size) {
if ((buf != LL_LAST(&smm_instance.active->allocd)) &&
(LL_NEXT(buf, pool)->props.group == NULL)) {
/* Pull back next buffer to use unallocated size */
LL_NEXT(buf, pool)->props.offset -= (buf_sz - grp->size);
} else {
/* Allocate another buffer to hold unallocated size */
alloc_buffer(buf, buf->props.offset + grp->size);
}
break;
}
}
}
if (buf == NULL) {
/* No buffer found to meet allocation size, expand pool */
if ((last != NULL) &&
(last->props.group == NULL)) {
/* Use last free buffer */
buf_sz = (grp->size - buf_sz);
} else {
/* Allocate new buffer */
buf_sz = grp->size;
if (last == NULL) {
buf = alloc_buffer(NULL, 0);
} else {
buf = alloc_buffer(last, smm_instance.active->props.size);
}
last = buf;
}
if (last != NULL) {
/* Expand pool backing memory */
ret = ftruncate(smm_instance.active->props.fd,
smm_instance.active->props.size + buf_sz);
if (ret) {
if (buf != NULL) {
free_buffer(buf);
buf = NULL;
}
} else {
smm_instance.active->props.size += buf_sz;
smm_instance.active->map_outdated = true;
buf = last;
if (!(smm_instance.active->props.size - buf_sz)) {
/* Emit 'new pool' event */
if ((smm_instance.cbs.new_pool != NULL) &&
(smm_instance.cbs.new_pool(smm_instance.cbs.ctx,
&smm_instance.active->props))) {
free_buffer(buf);
free_pool(smm_instance.active);
smm_instance.active = NULL;
buf = NULL;
}
} else {
/* Emit 'expand pool' event */
if (smm_instance.cbs.expand_pool != NULL) {
smm_instance.cbs.expand_pool(smm_instance.cbs.ctx,
&smm_instance.active->props);
}
}
}
}
}
}
if (buf != NULL) {
/* Set buffer group */
memcpy((void *)&buf->props.group, &grp, sizeof(struct smm_group *));
/* Emit 'new buffer' event */
if (smm_instance.cbs.new_buffer != NULL) {
if (smm_instance.cbs.new_buffer(smm_instance.cbs.ctx, &buf->props)) {
grp = NULL;
memcpy((void *)&buf->props.group, &grp, sizeof(struct smm_group *));
buf = NULL;
}
}
if (buf != NULL) {
/* Update active pool usage statistic */
smm_instance.statistics.active_used += grp->size;
grp->num_buffers++;
}
}
return buf;
}
void return_to_pool(struct smm_buffer *buf)
{
struct smm_group *grp = buf->props.group;
struct smm_pool *pool = buf->props.pool;
/* Emit 'free buffer' event */
if (smm_instance.cbs.free_buffer != NULL) {
smm_instance.cbs.free_buffer(smm_instance.cbs.ctx, &buf->props);
}
/* Buffer is no longer part of history */
purge_history(buf);
/* Buffer is no longer part of group */
grp->num_buffers--;
grp = NULL;
memcpy((void *)&buf->props.group, &grp, sizeof(struct smm_group *));
/* Update active pool usage statistic */
if (smm_instance.active == pool) {
smm_instance.statistics.active_used -= calc_buffer_size(buf);
}
/* Coalesce with ungrouped buffers beside this one */
if ((buf != LL_LAST(&pool->allocd)) &&
(LL_NEXT(buf, pool)->props.group == NULL)) {
free_buffer(LL_NEXT(buf, pool));
}
if ((buf != LL_FIRST(&pool->allocd)) &&
(LL_PREV(buf, pool)->props.group == NULL)) {
buf = LL_PREV(buf, pool);
pool = buf->props.pool;
free_buffer(LL_NEXT(buf, pool));
}
/* Free buffer (and pool), if only remaining buffer in pool */
if ((buf == LL_FIRST(&pool->allocd)) &&
(buf == LL_LAST(&pool->allocd))) {
free_buffer(buf);
/* Emit 'free pool' event */
if (smm_instance.cbs.free_pool != NULL) {
smm_instance.cbs.free_pool(smm_instance.cbs.ctx, &pool->props);
}
free_pool(pool);
if (smm_instance.active == pool) {
smm_instance.active = NULL;
}
}
}
struct smm_pool *alloc_pool(void)
{
struct smm_pool *pool;
char name[] = ("/" SMM_FD_NAME "-XXXXX");
unsigned char attempts = 0;
bool opened = false;
pool = malloc(sizeof(struct smm_pool));
if (pool != NULL) {
do {
/* A randomized pool name should help reduce collisions */
sprintf(name + sizeof(SMM_FD_NAME) + 1, "%05X", rand() & 0xFFFF);
pool->props.fd = shm_open(name,
O_RDWR | O_CREAT | O_EXCL,
S_IRUSR | S_IWUSR);
if (pool->props.fd >= 0) {
shm_unlink(name);
pool->props.size = 0;
pool->map = NULL;
pool->map_size = 0;
pool->map_outdated = false;
LL_INIT(&pool->allocd);
opened = true;
break;
} else {
if (errno != EEXIST) {
break;
}
attempts++;
}
} while (attempts < MAX_NAME_ATTEMPTS);
if (!opened) {
free(pool);
pool = NULL;
}
}
return pool;
}
void free_pool(struct smm_pool *pool)
{
if (pool->map != NULL) {
munmap(pool->map, pool->map_size);
}
close(pool->props.fd);
free(pool);
}
struct smm_buffer *alloc_buffer(struct smm_buffer *last, size_t offset)
{
struct smm_buffer *buf;
struct smm_buffer_properties initial_props = {
{NULL},
NULL,
smm_instance.active,
offset
};
/* Allocate and intialize a new buffer (including linking in to pool) */
buf = malloc(sizeof(struct smm_buffer));
if (buf != NULL) {
memcpy(&buf->props, &initial_props, sizeof(struct smm_buffer_properties));
buf->group_resized = false;
if (last == NULL) {
LL_ENQUEUE(&smm_instance.active->allocd, buf, pool);
} else {
LL_INSERT_AFTER(&smm_instance.active->allocd, last, buf, pool);
}
}
return buf;
}
void free_buffer(struct smm_buffer *buf)
{
struct smm_pool *buf_pool = buf->props.pool;
/* Remove from pool */
LL_REMOVE(&buf_pool->allocd, buf, pool);
free(buf);
}
#endif

@ -0,0 +1,67 @@
/**
* @file smm.h
*
*/
#ifndef SMM_H
#define SMM_H
#include <stddef.h>
#include <stdbool.h>
#define SMM_FD_NAME "lvgl-wayland"
#define SMM_POOL_TAGS (1)
#define SMM_BUFFER_TAGS (2)
#define SMM_GROUP_TAGS (1)
#define SMM_POOL_PROPERTIES(p) ((const struct smm_pool_properties *)(p))
#define SMM_BUFFER_PROPERTIES(b) ((const struct smm_buffer_properties *)(b))
#define SMM_GROUP_PROPERTIES(g) ((const struct smm_group_properties *)(g))
#define SMM_TAG(o, n, v) \
do { \
void **smm_tag = (void **)((char *)o + (n * sizeof(void *))); \
*smm_tag = (v); \
} while(0)
typedef void smm_pool_t;
typedef void smm_buffer_t;
typedef void smm_group_t;
struct smm_events {
void *ctx;
bool (*new_pool)(void *ctx, smm_pool_t *pool);
void (*expand_pool)(void *ctx, smm_pool_t *pool);
void (*free_pool)(void *ctx, smm_pool_t *pool);
bool (*new_buffer)(void *ctx, smm_buffer_t *buf);
bool (*init_buffer)(void *ctx, smm_buffer_t *buf);
void (*free_buffer)(void *ctx, smm_buffer_t *buf);
};
struct smm_pool_properties {
void *tag[SMM_POOL_TAGS];
size_t size;
int fd;
};
struct smm_buffer_properties {
void *tag[SMM_BUFFER_TAGS];
smm_group_t *const group;
smm_pool_t *const pool;
size_t offset;
};
struct smm_group_properties {
void *tag[SMM_GROUP_TAGS];
};
void smm_init(struct smm_events *evs);
void smm_setctx(void *ctx);
void smm_deinit(void);
smm_group_t *smm_create(void);
void smm_resize(smm_group_t *grp, size_t sz);
void smm_destroy(smm_group_t *grp);
smm_buffer_t *smm_acquire(smm_group_t *grp);
void *smm_map(smm_buffer_t *buf);
void smm_release(smm_buffer_t *buf);
smm_buffer_t *smm_latest(smm_group_t *grp);
smm_buffer_t *smm_next(smm_buffer_t *buf);
#endif

File diff suppressed because it is too large Load Diff

@ -54,7 +54,6 @@ lv_disp_t * lv_wayland_create_window(lv_coord_t hor_res, lv_coord_t ver_res, cha
lv_wayland_display_close_f_t close_cb); lv_wayland_display_close_f_t close_cb);
void lv_wayland_close_window(lv_disp_t * disp); void lv_wayland_close_window(lv_disp_t * disp);
bool lv_wayland_window_is_open(lv_disp_t * disp); bool lv_wayland_window_is_open(lv_disp_t * disp);
bool lv_wayland_window_is_flush_pending(lv_disp_t * disp);
void lv_wayland_window_set_fullscreen(lv_disp_t * disp, bool fullscreen); void lv_wayland_window_set_fullscreen(lv_disp_t * disp, bool fullscreen);
lv_indev_t * lv_wayland_get_pointer(lv_disp_t * disp); lv_indev_t * lv_wayland_get_pointer(lv_disp_t * disp);
lv_indev_t * lv_wayland_get_pointeraxis(lv_disp_t * disp); lv_indev_t * lv_wayland_get_pointeraxis(lv_disp_t * disp);

File diff suppressed because it is too large Load Diff

@ -20,7 +20,11 @@
#if USE_WIN32DRV #if USE_WIN32DRV
#include <Windows.h> #if LV_USE_USER_DATA == 0
#error "Support for user data is required by new Win32 driver. Set LV_USE_USER_DATA to 1 in lv_conf.h"
#endif
#include <windows.h>
#if _MSC_VER >= 1200 #if _MSC_VER >= 1200
// Disable compilation warnings. // Disable compilation warnings.
@ -46,10 +50,50 @@
* DEFINES * DEFINES
*********************/ *********************/
#define LVGL_SIMULATOR_WINDOW_CLASS L"LVGL.SimulatorWindow"
/********************** /**********************
* TYPEDEFS * TYPEDEFS
**********************/ **********************/
typedef struct _lv_win32_keyboard_queue_item_t
{
SLIST_ENTRY ItemEntry;
uint32_t key;
lv_indev_state_t state;
} lv_win32_keyboard_queue_item_t;
typedef struct _lv_win32_window_context_t
{
lv_disp_t* display_device_object;
lv_indev_t* mouse_device_object;
lv_indev_t* mousewheel_device_object;
lv_indev_t* keyboard_device_object;
lv_coord_t display_hor_res;
lv_coord_t display_ver_res;
uint32_t display_dpi;
HDC display_framebuffer_context_handle;
uint32_t* display_framebuffer_base;
size_t display_framebuffer_size;
lv_disp_draw_buf_t display_buffer;
lv_disp_drv_t display_driver;
lv_indev_state_t mouse_state;
lv_point_t mouse_point;
lv_indev_drv_t mouse_driver;
lv_indev_state_t mousewheel_state;
int16_t mousewheel_enc_diff;
lv_indev_drv_t mousewheel_driver;
CRITICAL_SECTION keyboard_mutex;
PSLIST_HEADER keyboard_queue;
uint16_t keyboard_utf16_high_surrogate;
uint16_t keyboard_utf16_low_surrogate;
lv_indev_drv_t keyboard_driver;
} lv_win32_window_context_t;
/********************** /**********************
* GLOBAL PROTOTYPES * GLOBAL PROTOTYPES
**********************/ **********************/
@ -63,6 +107,19 @@ EXTERN_C lv_indev_t* lv_win32_encoder_device_object;
EXTERN_C void lv_win32_add_all_input_devices_to_group( EXTERN_C void lv_win32_add_all_input_devices_to_group(
lv_group_t* group); lv_group_t* group);
EXTERN_C lv_win32_window_context_t* lv_win32_get_window_context(
HWND window_handle);
EXTERN_C bool lv_win32_init_window_class();
EXTERN_C HWND lv_win32_create_display_window(
const wchar_t* window_title,
lv_coord_t hor_res,
lv_coord_t ver_res,
HINSTANCE instance_handle,
HICON icon_handle,
int show_window_mode);
EXTERN_C bool lv_win32_init( EXTERN_C bool lv_win32_init(
HINSTANCE instance_handle, HINSTANCE instance_handle,
int show_window_mode, int show_window_mode,

@ -0,0 +1,281 @@
/**
* @file x11.c
*
*/
/*********************
* INCLUDES
*********************/
#include "x11.h"
#if USE_X11
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
/*********************
* DEFINES
*********************/
#ifndef KEYBOARD_BUFFER_SIZE
#define KEYBOARD_BUFFER_SIZE 64
#endif
#define MIN(A, B) ((A) < (B) ? (A) : (B))
#define MAX(A, B) ((A) > (B) ? (A) : (B))
#ifndef X11_OPTIMIZED_SCREEN_UPDATE
#define X11_OPTIMIZED_SCREEN_UPDATE 1
#endif
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC VARIABLES
**********************/
static Display* display = NULL;
static Window window = (XID)-1;
static GC gc = NULL;
static XImage* ximage = NULL;
static lv_timer_t* timer = NULL;
static char kb_buffer[KEYBOARD_BUFFER_SIZE];
static lv_point_t mouse_pos = { 0, 0 };
static bool left_mouse_btn = false;
static bool right_mouse_btn = false;
static bool wheel_mouse_btn = false;
static int16_t wheel_cnt = 0;
/**********************
* MACROS
**********************/
/**********************
* STATIC FUNCTIONS
**********************/
static int predicate(Display* disp, XEvent* evt, XPointer arg) { return 1; }
static void x11_event_handler(lv_timer_t * t)
{
XEvent myevent;
KeySym mykey;
int n;
/* handle all outstanding X events */
while (XCheckIfEvent(display, &myevent, predicate, NULL)) {
switch(myevent.type)
{
case Expose:
if(myevent.xexpose.count==0)
{
XPutImage(display, window, gc, ximage, 0, 0, 0, 0, LV_HOR_RES, LV_VER_RES);
}
break;
case MotionNotify:
mouse_pos.x = myevent.xmotion.x;
mouse_pos.y = myevent.xmotion.y;
break;
case ButtonPress:
switch (myevent.xbutton.button)
{
case Button1:
left_mouse_btn = true;
break;
case Button2:
wheel_mouse_btn = true;
break;
case Button3:
right_mouse_btn = true;
break;
case Button4:
wheel_cnt--; // Scrolled up
break;
case Button5:
wheel_cnt++; // Scrolled down
break;
default:
LV_LOG_WARN("unhandled button press : %d", myevent.xbutton.button);
}
break;
case ButtonRelease:
switch (myevent.xbutton.button)
{
case Button1:
left_mouse_btn = false;
break;
case Button2:
wheel_mouse_btn = false;
break;
case Button3:
right_mouse_btn = false;
break;
}
break;
case KeyPress:
n = XLookupString(&myevent.xkey, &kb_buffer[0], sizeof(kb_buffer), &mykey, NULL);
kb_buffer[n] = '\0';
break;
case KeyRelease:
break;
default:
LV_LOG_WARN("unhandled x11 event: %d", myevent.type);
}
}
}
static void lv_x11_hide_cursor()
{
XColor black = { .red = 0, .green = 0, .blue = 0 };
char empty_data[] = { 0 };
Pixmap empty_bitmap = XCreateBitmapFromData(display, window, empty_data, 1, 1);
Cursor inv_cursor = XCreatePixmapCursor(display, empty_bitmap, empty_bitmap, &black, &black, 0, 0);
XDefineCursor(display, window, inv_cursor);
XFreeCursor(display, inv_cursor);
XFreePixmap(display, empty_bitmap);
}
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_x11_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p)
{
#if X11_OPTIMIZED_SCREEN_UPDATE
static const lv_area_t inv_area = { .x1 = 0xFFFF,
.x2 = 0,
.y1 = 0xFFFF,
.y2 = 0
};
static lv_area_t upd_area = inv_area;
/* build display update area until lv_disp_flush_is_last */
upd_area.x1 = MIN(upd_area.x1, area->x1);
upd_area.x2 = MAX(upd_area.x2, area->x2);
upd_area.y1 = MIN(upd_area.y1, area->y1);
upd_area.y2 = MAX(upd_area.y2, area->y2);
#endif // X11_OPTIMIZED_SCREEN_UPDATE
for (lv_coord_t y = area->y1; y <= area->y2; y++)
{
uint32_t dst_offs = area->x1 + y * LV_HOR_RES;
uint32_t* dst_data = &((uint32_t*)ximage->data)[dst_offs];
for (lv_coord_t x = area->x1; x <= area->x2; x++, color_p++, dst_data++)
{
*dst_data = lv_color_to32(*color_p);
}
}
if (lv_disp_flush_is_last(disp_drv))
{
#if X11_OPTIMIZED_SCREEN_UPDATE
/* refresh collected display update area only */
lv_coord_t upd_w = upd_area.x2 - upd_area.x1 + 1;
lv_coord_t upd_h = upd_area.y2 - upd_area.y1 + 1;
XPutImage(display, window, gc, ximage, upd_area.x1, upd_area.y1, upd_area.x1, upd_area.y1, upd_w, upd_h);
/* invalidate collected area */
upd_area = inv_area;
#else
/* refresh full display */
XPutImage(display, window, gc, ximage, 0, 0, 0, 0, LV_HOR_RES, LV_VER_RES);
#endif
}
lv_disp_flush_ready(disp_drv);
}
void lv_x11_get_pointer(lv_indev_drv_t *indev_drv, lv_indev_data_t *data)
{
(void) indev_drv; // Unused
data->point = mouse_pos;
data->state = left_mouse_btn ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED;
}
void lv_x11_get_mousewheel(lv_indev_drv_t *indev_drv, lv_indev_data_t *data)
{
(void) indev_drv; // Unused
data->state = wheel_mouse_btn ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED;
data->enc_diff = wheel_cnt;
wheel_cnt = 0;
}
void lv_x11_get_keyboard(lv_indev_drv_t *indev_drv, lv_indev_data_t *data)
{
(void) indev_drv; // Unused
size_t len = strlen(kb_buffer);
if (len > 0)
{
data->state = LV_INDEV_STATE_PRESSED;
data->key = kb_buffer[0];
memmove(kb_buffer, kb_buffer + 1, len);
data->continue_reading = (len > 0);
}
else
{
data->state = LV_INDEV_STATE_RELEASED;
}
}
void lv_x11_init(char const* title, lv_coord_t width, lv_coord_t height)
{
/* setup display/screen */
display = XOpenDisplay(NULL);
int screen = DefaultScreen(display);
/* drawing contexts for an window */
unsigned long myforeground = BlackPixel(display, screen);
unsigned long mybackground = WhitePixel(display, screen);
/* create window */
window = XCreateSimpleWindow(display, DefaultRootWindow(display),
0, 0, width, height,
0, myforeground, mybackground);
/* window manager properties (yes, use of StdProp is obsolete) */
XSetStandardProperties(display, window, title, NULL, None, NULL, 0, NULL);
/* allow receiving mouse and keyboard events */
XSelectInput(display, window, PointerMotionMask|ButtonPressMask|ButtonReleaseMask|KeyPressMask|KeyReleaseMask|ExposureMask);
/* graphics context */
gc = XCreateGC(display, window, 0, 0);
lv_x11_hide_cursor();
/* create cache XImage */
Visual* visual = XDefaultVisual(display, screen);
int dplanes = DisplayPlanes(display, screen);
ximage = XCreateImage(display, visual, dplanes, ZPixmap, 0,
malloc(width * height * sizeof(uint32_t)), width, height, 32, 0);
timer = lv_timer_create(x11_event_handler, 10, NULL);
/* finally bring window on top of the other windows */
XMapRaised(display, window);
}
void lv_x11_deinit(void)
{
lv_timer_del(timer);
free(ximage->data);
XDestroyImage(ximage);
ximage = NULL;
XFreeGC(display, gc);
gc = NULL;
XDestroyWindow(display, window);
window = (XID)-1;
XCloseDisplay(display);
display = NULL;
}
#endif // USE_X11

@ -0,0 +1,60 @@
/**
* @file x11.h
*
*/
#ifndef X11_H
#define X11_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifndef LV_DRV_NO_CONF
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_drv_conf.h"
#else
#include "../../lv_drv_conf.h"
#endif
#endif
#if USE_X11
#ifdef LV_LVGL_H_INCLUDE_SIMPLE
#include "lvgl.h"
#else
#include "lvgl/lvgl.h"
#endif
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_x11_init(char const* title, lv_coord_t width, lv_coord_t height);
void lv_x11_deinit(void);
void lv_x11_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p);
void lv_x11_get_pointer(lv_indev_drv_t *indev_drv, lv_indev_data_t *data);
void lv_x11_get_mousewheel(lv_indev_drv_t *indev_drv, lv_indev_data_t *data);
void lv_x11_get_keyboard(lv_indev_drv_t *indev_drv, lv_indev_data_t *data);
/**********************
* MACROS
**********************/
#endif /* USE_X11 */
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* X11_H */