/** * @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