Skip to content

Commit

Permalink
added x11 display driver port (#300)
Browse files Browse the repository at this point in the history
Co-authored-by: Gregor Copoix <gregor.copoix@ithinx.io>
  • Loading branch information
gcopoix and Gregor Copoix authored Nov 6, 2023
1 parent 4391dbf commit 5a5b4a1
Show file tree
Hide file tree
Showing 4 changed files with 350 additions and 1 deletion.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
**/*.o
**/*.d
**/*.d
build/*
7 changes: 7 additions & 0 deletions lv_drv_conf_template.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,13 @@
# endif
#endif

/*----------------------------------------
* X11 drivers (monitor, mouse, keyboard)
*---------------------------------------*/
#ifndef USE_X11
# define USE_X11 0
#endif

/*----------------
* SSD1963
*--------------*/
Expand Down
281 changes: 281 additions & 0 deletions x11/x11.c
Original file line number Diff line number Diff line change
@@ -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
60 changes: 60 additions & 0 deletions x11/x11.h
Original file line number Diff line number Diff line change
@@ -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 */

0 comments on commit 5a5b4a1

Please sign in to comment.