From 5a5b4a1a3e95a46e6cf58ac6aac6ed6e826bfedd Mon Sep 17 00:00:00 2001 From: gcopoix Date: Mon, 6 Nov 2023 15:49:36 +0100 Subject: [PATCH] added x11 display driver port (#300) Co-authored-by: Gregor Copoix --- .gitignore | 3 +- lv_drv_conf_template.h | 7 + x11/x11.c | 281 +++++++++++++++++++++++++++++++++++++++++ x11/x11.h | 60 +++++++++ 4 files changed, 350 insertions(+), 1 deletion(-) create mode 100644 x11/x11.c create mode 100644 x11/x11.h diff --git a/.gitignore b/.gitignore index fd73aaa..953fe48 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ **/*.o -**/*.d \ No newline at end of file +**/*.d +build/* diff --git a/lv_drv_conf_template.h b/lv_drv_conf_template.h index 9b895f0..e77089b 100644 --- a/lv_drv_conf_template.h +++ b/lv_drv_conf_template.h @@ -195,6 +195,13 @@ # endif #endif +/*---------------------------------------- + * X11 drivers (monitor, mouse, keyboard) + *---------------------------------------*/ +#ifndef USE_X11 +# define USE_X11 0 +#endif + /*---------------- * SSD1963 *--------------*/ diff --git a/x11/x11.c b/x11/x11.c new file mode 100644 index 0000000..952328b --- /dev/null +++ b/x11/x11.c @@ -0,0 +1,281 @@ +/** + * @file x11.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "x11.h" +#if USE_X11 +#include +#include +#include +#include +#include + +/********************* + * 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 diff --git a/x11/x11.h b/x11/x11.h new file mode 100644 index 0000000..89d6475 --- /dev/null +++ b/x11/x11.h @@ -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 */