lib/lv_drivers: update to latest 0091dc6 (2024-02-14)

git subtree --prefix=lib/lv_drivers --squash pull \
      https://github.com/lvgl/lv_drivers \
      0091dc612facc94dce1061a9b78d641c77f1791a
master
alex 7 months ago
commit be9cb613de
Signed by: x1ddos
GPG Key ID: FDEFB4A63CBD8460

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

@ -20,6 +20,7 @@ target_include_directories(lv_drivers SYSTEM PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
find_package(PkgConfig)
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})
if("${LIB_INSTALL_DIR}" STREQUAL "")
@ -38,7 +39,16 @@ install(
PATTERN ".git*" EXCLUDE
PATTERN "CMakeFiles" 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")

@ -1,6 +1,6 @@
# 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).

@ -9,19 +9,14 @@
#include "drm.h"
#if USE_DRM
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <sys/time.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <stdint.h>
#include <stdio.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 <inttypes.h>
#include <unistd.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
@ -41,7 +36,7 @@ struct drm_buffer {
uint32_t pitch;
uint32_t offset;
unsigned long int size;
void * map;
uint8_t * map;
uint32_t fb_handle;
};
@ -65,8 +60,9 @@ struct drm_dev {
drmModePropertyPtr plane_props[128];
drmModePropertyPtr crtc_props[128];
drmModePropertyPtr conn_props[128];
struct drm_buffer drm_bufs[2]; /* DUMB buffers */
struct drm_buffer *cur_bufs[2]; /* double buffering handling */
struct drm_buffer drm_bufs[2]; /*DUMB buffers*/
uint8_t active_drm_buf_idx; /*Double buffering handling*/
lv_disp_draw_buf_t draw_buf;
} drm_dev;
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,
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");
if(drm_dev.req) {
drmModeAtomicFree(drm_dev.req);
drm_dev.req = NULL;
}
}
static int drm_get_plane_props(void)
@ -244,7 +251,7 @@ static int drm_dmabuf_set_plane(struct drm_buffer *buf)
{
int ret;
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();
@ -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)
{
LV_UNUSED(crtc_id);
drmModePlaneResPtr planes;
drmModePlanePtr plane;
unsigned int i;
@ -393,7 +401,7 @@ static int drm_find_connector(void)
drm_dev.mmWidth = conn->mmWidth;
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),
&drm_dev.blob_id)) {
@ -464,7 +472,7 @@ static int drm_find_connector(void)
drmModeFreeEncoder(enc);
}
drm_dev.crtc_idx = -1;
drm_dev.crtc_idx = UINT32_MAX;
for (i = 0; i < res->count_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");
goto free_res;
}
@ -614,7 +622,7 @@ static int drm_allocate_dumb(struct drm_buffer *buf)
int ret;
/* create dumb buffer */
memset(&creq, 0, sizeof(creq));
lv_memset(&creq, 0, sizeof(creq));
creq.width = drm_dev.width;
creq.height = drm_dev.height;
creq.bpp = LV_COLOR_DEPTH;
@ -626,12 +634,10 @@ static int drm_allocate_dumb(struct drm_buffer *buf)
buf->handle = creq.handle;
buf->pitch = creq.pitch;
dbg("pitch %d", buf->pitch);
buf->size = creq.size;
dbg("size %d", buf->size);
/* prepare buffer for memory mapping */
memset(&mreq, 0, sizeof(mreq));
lv_memset(&mreq, 0, sizeof(mreq));
mreq.handle = creq.handle;
ret = drmIoctl(drm_dev.fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq);
if (ret) {
@ -640,6 +646,7 @@ static int drm_allocate_dumb(struct drm_buffer *buf)
}
buf->offset = mreq.offset;
info("size %lu pitch %u offset %u", buf->size, buf->pitch, buf->offset);
/* perform actual memory mapping */
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) */
memset(buf->map, 0, creq.size);
lv_memset(buf->map, 0, creq.size);
/* create framebuffer object for the dumb-buffer */
handles[0] = creq.handle;
@ -669,7 +676,7 @@ static int drm_setup_buffers(void)
{
int ret;
/* Allocate DUMB buffers */
/*Allocate DUMB buffers*/
ret = drm_allocate_dumb(&drm_dev.drm_bufs[0]);
if (ret)
return ret;
@ -678,80 +685,61 @@ static int drm_setup_buffers(void)
if (ret)
return ret;
/* Set buffering handling */
drm_dev.cur_bufs[0] = NULL;
drm_dev.cur_bufs[1] = &drm_dev.drm_bufs[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;
fd_set fds;
FD_ZERO(&fds);
FD_SET(drm_dev.fd, &fds);
do {
ret = select(drm_dev.fd + 1, &fds, NULL, NULL, NULL);
} while (ret == -1 && errno == EINTR);
if (ret < 0) {
err("select failed: %s", strerror(errno));
drmModeAtomicFree(drm_dev.req);
drm_dev.req = NULL;
return;
while(drm_dev.req) {
struct pollfd pfd;
pfd.fd = drm_dev.fd;
pfd.events = POLLIN;
int ret;
do {
ret = poll(&pfd, 1, -1);
} while (ret == -1 && errno == EINTR);
if(ret > 0)
drmHandleEvent(drm_dev.fd, &drm_dev.drm_event_ctx);
else {
err("poll failed: %s", strerror(errno));
return;
}
}
if (FD_ISSET(drm_dev.fd, &fds))
drmHandleEvent(drm_dev.fd, &drm_dev.drm_event_ctx);
drmModeAtomicFree(drm_dev.req);
drm_dev.req = NULL;
lv_disp_flush_ready(disp_drv);
}
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];
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);
/* Partial update */
if ((w != drm_dev.width || h != drm_dev.height) && drm_dev.cur_bufs[0])
memcpy(fbuf->map, drm_dev.cur_bufs[0]->map, fbuf->size);
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),
w * (LV_COLOR_SIZE/8));
struct drm_buffer *fbuf = &drm_dev.drm_bufs[drm_dev.active_drm_buf_idx ^ 1];
if(!disp_drv->direct_mode) {
/*Backwards compatibility: Non-direct flush */
uint32_t w = (area->x2 - area->x1) + 1;
for (int y = 0, i = area->y1; i <= area->y2 ; ++i, ++y) {
lv_memcpy(fbuf->map + (area->x1 * (LV_COLOR_SIZE / 8)) + (fbuf->pitch * i),
(uint8_t *)color_p + (w * (LV_COLOR_SIZE / 8) * y),
w * (LV_COLOR_SIZE / 8));
}
}
if (drm_dev.req)
drm_wait_vsync(disp_drv);
if(lv_disp_flush_is_last(disp_drv)) {
/*Request buffer swap*/
if(drm_dmabuf_set_plane(fbuf)) {
err("Flush fail");
return;
}
else
dbg("Flush done");
/* show fbuf plane */
if (drm_dmabuf_set_plane(fbuf)) {
err("Flush fail");
return;
drm_dev.active_drm_buf_idx ^= 1;
}
else
dbg("Flush done");
if (!drm_dev.cur_bufs[0])
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
#define DRM_FOURCC DRM_FORMAT_ARGB8888
#define DRM_FOURCC DRM_FORMAT_XRGB8888
#elif LV_COLOR_DEPTH == 16
#define DRM_FOURCC DRM_FORMAT_RGB565
#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);
}
void drm_init(void)
int drm_init(void)
{
int ret;
@ -778,7 +766,7 @@ void drm_init(void)
if (ret) {
close(drm_dev.fd);
drm_dev.fd = -1;
return;
return -1;
}
ret = drm_setup_buffers();
@ -786,10 +774,28 @@ void drm_init(void)
err("DRM buffer allocation failed");
close(drm_dev.fd);
drm_dev.fd = -1;
return;
return -1;
}
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)

@ -40,13 +40,14 @@ extern "C" {
/**********************
* GLOBAL PROTOTYPES
**********************/
void drm_init(void);
void drm_get_sizes(lv_coord_t *width, lv_coord_t *height, uint32_t *dpi);
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_exit(void);
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);
/**********************
* MACROS
**********************/

@ -101,7 +101,7 @@ void fbdev_init(void)
// Make sure that the display is on.
if (ioctl(fbfd, FBIOBLANK, FB_BLANK_UNBLANK) != 0) {
perror("ioctl(FBIOBLANK)");
return;
// Don't return. Some framebuffer drivers like efifb or simplefb don't implement FBIOBLANK.
}
#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;
unsigned char bit_location = 0;
/*32 or 24 bit per pixel*/
if(vinfo.bits_per_pixel == 32 || vinfo.bits_per_pixel == 24) {
/*32 bit per pixel*/
if(vinfo.bits_per_pixel == 32) {
uint32_t * fbp32 = (uint32_t *)fbp;
int32_t 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;
}
}
/*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*/
else if(vinfo.bits_per_pixel == 16) {
uint16_t * fbp16 = (uint16_t *)fbp;

@ -55,9 +55,9 @@ void xpt2046_init(void)
/**
* Get the current position and state of the touchpad
* @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_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.y = y;
return false;
data->continue_reading = false; /* No more data to be read */
}
/**********************

@ -41,7 +41,7 @@ extern "C" {
* GLOBAL PROTOTYPES
**********************/
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

@ -12,6 +12,7 @@
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#if USE_BSD_EVDEV
#include <dev/evdev/input.h>
#else
@ -22,230 +23,202 @@
#include "xkb.h"
#endif /* USE_XKB */
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
* STATIC VARIABLES
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
int map(int x, int in_min, int in_max, int out_min, int out_max);
evdev_device_t global_dsc;
/**********************
* 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*/
}
/**********************
* MACROS
**********************/
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)
{
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
**********************/
/**
* Initialize the evdev interface
*/
void evdev_init(void)
void evdev_init()
{
if (!evdev_set_file(EVDEV_NAME)) {
return;
}
evdev_device_init(&global_dsc);
#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
xkb_init();
#endif
}
/**
* reconfigure the device file for evdev
* @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) {
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) {
perror("unable to open evdev interface:");
bool evdev_set_file(const char * dev_path)
{
return evdev_device_set_file(&global_dsc, dev_path);
}
bool evdev_device_set_file(evdev_device_t * dsc, const char * dev_path)
{
/*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;
}
}
#if USE_BSD_EVDEV
fcntl(evdev_fd, F_SETFL, O_NONBLOCK);
#else
fcntl(evdev_fd, F_SETFL, O_ASYNC | O_NONBLOCK);
#endif
return true;
}
evdev_root_x = 0;
evdev_root_y = 0;
evdev_key_val = 0;
evdev_button = LV_INDEV_STATE_REL;
void evdev_device_set_swap_axes(evdev_device_t * dsc, bool swap_axes)
{
dsc->swap_axes = swap_axes;
}
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)
{
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.code == REL_X)
#if EVDEV_SWAP_AXES
evdev_root_y += in.value;
#else
evdev_root_x += in.value;
#endif
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
if(in.code == REL_X) dsc->root_x += in.value;
else if(in.code == REL_Y) dsc->root_y += in.value;
}
else if(in.type == EV_ABS) {
if(in.code == ABS_X || in.code == ABS_MT_POSITION_X) dsc->root_x = in.value;
else if(in.code == ABS_Y || in.code == ABS_MT_POSITION_Y) dsc->root_y = in.value;
else if(in.code == ABS_MT_TRACKING_ID) {
if(in.value == -1)
evdev_button = LV_INDEV_STATE_REL;
else if(in.value == 0)
evdev_button = LV_INDEV_STATE_PR;
if(in.value == -1) dsc->state = LV_INDEV_STATE_RELEASED;
else if(in.value == 0) dsc->state = LV_INDEV_STATE_PRESSED;
}
} else if(in.type == EV_KEY) {
}
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 */
if (data->key != 0) {
/* Only record button state when actual output is produced to prevent widgets from refreshing */
data->state = (in.value) ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL;
if(in.value == 0) dsc->state = LV_INDEV_STATE_RELEASED;
else if(in.value == 1) dsc->state = LV_INDEV_STATE_PRESSED;
}
else {
dsc->key = _evdev_process_key(in.code, in.value != 0);
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;
}
evdev_key_val = data->key;
evdev_button = data->state;
return;
}
}
}
if(drv->type == LV_INDEV_TYPE_KEYPAD) {
/* No data retrieved */
data->key = evdev_key_val;
data->state = evdev_button;
return;
/*Process and store in data*/
switch(drv->type) {
case LV_INDEV_TYPE_KEYPAD:
data->state = dsc->state;
data->key = dsc->key;
break;
case LV_INDEV_TYPE_POINTER:
data->state = dsc->state;
data->point = _evdev_process_pointer(drv, dsc->root_x, dsc->root_y);
break;
default:
break;
}
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)
data->point.x = 0;
if(data->point.y < 0)
data->point.y = 0;
if(data->point.x >= drv->disp->driver->hor_res)
data->point.x = drv->disp->driver->hor_res - 1;
if(data->point.y >= drv->disp->driver->ver_res)
data->point.y = drv->disp->driver->ver_res - 1;
return ;
}
/**********************
* 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"
#endif
/*********************
* DEFINES
*********************/
/**********************
* 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
**********************/
/**
* 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
* @param dev_name set the evdev device filename
* @return true: the device file set complete
* false: the device file doesn't exist current system
* Initialize an evdev device.
* @param dsc evdev device
*/
bool evdev_set_file(char* dev_name);
void evdev_device_init(evdev_device_t * dsc);
/**
* Get the current position and state of the evdev
* @param data store the evdev data here
* Reconfigure the path for the global evdev device.
* @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 */

@ -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 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.
@ -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 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.
@ -138,8 +159,8 @@ bool libinput_set_file_state(libinput_drv_state_t *state, char* dev_name)
// citing libinput.h:libinput_path_remove_device:
// > If no matching device exists, this function does nothing.
if (state->libinput_device) {
state->libinput_device = libinput_device_unref(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);
@ -195,6 +216,29 @@ void libinput_init_state(libinput_drv_state_t *state, char* path)
#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
* 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.
* https://wayland.freedesktop.org/libinput/doc/latest/api/group__base.html#gaa797496f0150b482a4e01376bd33a47b */
libinput_capability capabilities = 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)))
{
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_capability capabilities = libinput_query_capability(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_DOWN:
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 y = 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) {
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_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_touch < 0 || x_touch > drv->hor_res || y_touch < 0 || y_touch > drv->ver_res) {
break; /* ignore touches that are out of bounds */
}
state->most_recent_touch_point.x = x;
state->most_recent_touch_point.y = y;
state->most_recent_touch_point.x = x_touch;
state->most_recent_touch_point.y = y_touch;
state->button = LV_INDEV_STATE_PR;
break;
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.y = LV_CLAMP(0, state->most_recent_touch_point.y, drv->ver_res - 1);
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:
pointer_event = libinput_event_get_pointer_event(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 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.
@ -78,7 +84,7 @@ typedef struct {
*/
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 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.
@ -99,6 +105,12 @@ void libinput_init(void);
* @param path input device node path (e.g. /dev/input/event0)
*/
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
* to connect a single device.

@ -73,6 +73,23 @@ bool xkb_init_state(xkb_drv_state_t *state) {
#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
* this function if you only want to connect a single device.
@ -95,19 +112,13 @@ bool xkb_set_keymap_state(xkb_drv_state_t *state, struct xkb_rule_names names) {
xkb_keymap_unref(state->keymap);
state->keymap = NULL;
}
state->keymap = xkb_keymap_new_from_names(context, &names, XKB_KEYMAP_COMPILE_NO_FLAGS);
if (!state->keymap) {
perror("could not create XKB keymap");
return false;
}
state->keymap = xkb_keymap_ref(state->keymap);
if (!state->keymap) {
perror("could not reference XKB keymap");
return false;
}
if (state->state) {
xkb_state_unref(state->state);
state->state = NULL;
@ -119,12 +130,6 @@ bool xkb_set_keymap_state(xkb_drv_state_t *state, struct xkb_rule_names names) {
return false;
}
state->state = xkb_state_ref(state->state);
if (!state->state) {
perror("could not reference XKB state");
return false;
}
return true;
}

@ -60,6 +60,12 @@ bool xkb_init(void);
* @return true if the initialisation was successful
*/
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
* this function if you only want to connect a single device.

@ -1,6 +1,6 @@
{
"name": "lv_drivers",
"version": "8.3.0",
"version": "9.0.0-dev",
"keywords": "littlevgl, lvgl, driver, display, touchpad",
"description": "Drivers for LittlevGL graphics library.",
"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
* 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*/
# define SDL_DUAL_DISPLAY 0
/* Window Title */
# define SDL_WINDOW_TITLE "TFT Simulator"
#endif
/*-------------------
@ -192,6 +195,13 @@
# endif
#endif
/*----------------------------------------
* X11 drivers (monitor, mouse, keyboard)
*---------------------------------------*/
#ifndef USE_X11
# define USE_X11 0
#endif
/*----------------
* SSD1963
*--------------*/

@ -9,8 +9,8 @@
#include "sdl.h"
#if USE_MONITOR || USE_SDL
#if LV_USE_GPU_SDL
# error "LV_USE_GPU_SDL must not be enabled"
#if LV_USE_DRAW_SDL
# error "LV_USE_DRAW_SDL must not be enabled"
#endif
#if USE_MONITOR
@ -47,6 +47,7 @@
# define SDL_FULLSCREEN 0
#endif
#include "sdl_common_internal.h"
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
@ -59,6 +60,9 @@
#define KEYBOARD_BUFFER_SIZE SDL_TEXTINPUTEVENT_TEXT_SIZE
#endif
#ifndef SDL_WINDOW_TITLE
#define SDL_WINDOW_TITLE "TFT Simulator"
#endif
/**********************
* TYPEDEFS
**********************/
@ -96,17 +100,6 @@ monitor_t monitor;
monitor_t monitor2;
#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
**********************/
@ -344,7 +337,7 @@ static void window_create(monitor_t * m)
flag |= SDL_WINDOW_FULLSCREEN;
#endif
m->window = SDL_CreateWindow("TFT Simulator",
m->window = SDL_CreateWindow(SDL_WINDOW_TITLE,
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*/
@ -375,7 +368,7 @@ static void window_update(monitor_t * m)
#endif
SDL_RenderClear(m->renderer);
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_Rect r;
r.x = 0; r.y = 0; r.w = SDL_HOR_RES; r.h = SDL_VER_RES;

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

@ -32,7 +32,6 @@ extern "C" {
#ifndef SDL_INCLUDE_PATH
#define SDL_INCLUDE_PATH MONITOR_SDL_INCLUDE_PATH
#endif
#include SDL_INCLUDE_PATH
#ifndef SDL_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);
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 */
#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"
#if USE_SDL_GPU
#if LV_USE_GPU_SDL == 0
#if LV_USE_DRAW_SDL == 0
# error "LV_USE_DRAW_SDL must be enabled"
#endif
@ -29,6 +29,7 @@
# error "Cannot enable both MONITOR and SDL at the same time. "
#endif
#include "sdl_common_internal.h"
#include <stdlib.h>
#include <stdbool.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)
{
monitor_t *m = lv_mem_alloc(sizeof(monitor_t));
monitor_t *m = lv_malloc(sizeof(monitor_t));
window_create(m);
lv_disp_drv_init(disp_drv);
disp_drv->direct_mode = 1;
disp_drv->flush_cb = monitor_flush;
disp_drv->hor_res = hor_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);
disp_drv->draw_buf = disp_buf;
disp_drv->antialiasing = 1;

@ -8,7 +8,7 @@ pkg_check_modules(xkbcommon REQUIRED xkbcommon)
# Wayland protocols
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)
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);
void lv_wayland_close_window(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);
lv_indev_t * lv_wayland_get_pointer(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
#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
// Disable compilation warnings.
@ -46,10 +50,50 @@
* DEFINES
*********************/
#define LVGL_SIMULATOR_WINDOW_CLASS L"LVGL.SimulatorWindow"
/**********************
* 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
**********************/
@ -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(
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(
HINSTANCE instance_handle,
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 */