diff --git a/lib/lv_drivers/.gitignore b/lib/lv_drivers/.gitignore index fd73aaa..953fe48 100644 --- a/lib/lv_drivers/.gitignore +++ b/lib/lv_drivers/.gitignore @@ -1,2 +1,3 @@ **/*.o -**/*.d \ No newline at end of file +**/*.d +build/* diff --git a/lib/lv_drivers/CMakeLists.txt b/lib/lv_drivers/CMakeLists.txt index fadccaa..74a666b 100644 --- a/lib/lv_drivers/CMakeLists.txt +++ b/lib/lv_drivers/CMakeLists.txt @@ -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") diff --git a/lib/lv_drivers/README.md b/lib/lv_drivers/README.md index 6a2da33..98bdac3 100644 --- a/lib/lv_drivers/README.md +++ b/lib/lv_drivers/README.md @@ -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). diff --git a/lib/lv_drivers/display/drm.c b/lib/lv_drivers/display/drm.c index 9cec6e3..bbf69c1 100644 --- a/lib/lv_drivers/display/drm.c +++ b/lib/lv_drivers/display/drm.c @@ -9,19 +9,14 @@ #include "drm.h" #if USE_DRM -#include -#include -#include -#include +#include +#include +#include +#include #include #include -#include -#include -#include -#include -#include #include -#include +#include #include #include @@ -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) diff --git a/lib/lv_drivers/display/drm.h b/lib/lv_drivers/display/drm.h index ebf2e28..9aa6dab 100644 --- a/lib/lv_drivers/display/drm.h +++ b/lib/lv_drivers/display/drm.h @@ -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 **********************/ diff --git a/lib/lv_drivers/display/fbdev.c b/lib/lv_drivers/display/fbdev.c index fe8d2ff..f649703 100644 --- a/lib/lv_drivers/display/fbdev.c +++ b/lib/lv_drivers/display/fbdev.c @@ -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; diff --git a/lib/lv_drivers/indev/XPT2046.c b/lib/lv_drivers/indev/XPT2046.c index f27fa76..c63b27f 100644 --- a/lib/lv_drivers/indev/XPT2046.c +++ b/lib/lv_drivers/indev/XPT2046.c @@ -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 */ } /********************** diff --git a/lib/lv_drivers/indev/XPT2046.h b/lib/lv_drivers/indev/XPT2046.h index 7eee8c0..043b6fc 100644 --- a/lib/lv_drivers/indev/XPT2046.h +++ b/lib/lv_drivers/indev/XPT2046.h @@ -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 diff --git a/lib/lv_drivers/indev/evdev.c b/lib/lv_drivers/indev/evdev.c index 4d46b5b..2a62129 100644 --- a/lib/lv_drivers/indev/evdev.c +++ b/lib/lv_drivers/indev/evdev.c @@ -12,6 +12,7 @@ #include #include #include +#include #if USE_BSD_EVDEV #include #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*/ diff --git a/lib/lv_drivers/indev/evdev.h b/lib/lv_drivers/indev/evdev.h index c1b2280..541e61b 100644 --- a/lib/lv_drivers/indev/evdev.h +++ b/lib/lv_drivers/indev/evdev.h @@ -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 */ diff --git a/lib/lv_drivers/indev/libinput.c b/lib/lv_drivers/indev/libinput.c index fc995b1..d4f8e2a 100644 --- a/lib/lv_drivers/indev/libinput.c +++ b/lib/lv_drivers/indev/libinput.c @@ -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); diff --git a/lib/lv_drivers/indev/libinput_drv.h b/lib/lv_drivers/indev/libinput_drv.h index dd6e929..8dd8c43 100644 --- a/lib/lv_drivers/indev/libinput_drv.h +++ b/lib/lv_drivers/indev/libinput_drv.h @@ -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. diff --git a/lib/lv_drivers/indev/xkb.c b/lib/lv_drivers/indev/xkb.c index 7faffef..8e044be 100644 --- a/lib/lv_drivers/indev/xkb.c +++ b/lib/lv_drivers/indev/xkb.c @@ -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; } diff --git a/lib/lv_drivers/indev/xkb.h b/lib/lv_drivers/indev/xkb.h index 5018747..5bc8d1b 100644 --- a/lib/lv_drivers/indev/xkb.h +++ b/lib/lv_drivers/indev/xkb.h @@ -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. diff --git a/lib/lv_drivers/library.json b/lib/lv_drivers/library.json index 7299869..a8d331a 100644 --- a/lib/lv_drivers/library.json +++ b/lib/lv_drivers/library.json @@ -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": { diff --git a/lib/lv_drivers/lv-drivers.pc.in b/lib/lv_drivers/lv-drivers.pc.in new file mode 100644 index 0000000..fc71252 --- /dev/null +++ b/lib/lv_drivers/lv-drivers.pc.in @@ -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 diff --git a/lib/lv_drivers/lv_drivers.mk b/lib/lv_drivers/lv_drivers.mk index 97d4621..71d0f28 100644 --- a/lib/lv_drivers/lv_drivers.mk +++ b/lib/lv_drivers/lv_drivers.mk @@ -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) diff --git a/lib/lv_drivers/lv_drv_conf_template.h b/lib/lv_drivers/lv_drv_conf_template.h index 8c70874..e77089b 100644 --- a/lib/lv_drivers/lv_drv_conf_template.h +++ b/lib/lv_drivers/lv_drv_conf_template.h @@ -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 *--------------*/ diff --git a/lib/lv_drivers/sdl/sdl.c b/lib/lv_drivers/sdl/sdl.c index 0ac8192..b538f21 100644 --- a/lib/lv_drivers/sdl/sdl.c +++ b/lib/lv_drivers/sdl/sdl.c @@ -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 #include #include @@ -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; diff --git a/lib/lv_drivers/sdl/sdl_common.c b/lib/lv_drivers/sdl/sdl_common.c index 64f739d..1eae5b5 100644 --- a/lib/lv_drivers/sdl/sdl_common.c +++ b/lib/lv_drivers/sdl/sdl_common.c @@ -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; } diff --git a/lib/lv_drivers/sdl/sdl_common.h b/lib/lv_drivers/sdl/sdl_common.h index b2832c4..4556595 100644 --- a/lib/lv_drivers/sdl/sdl_common.h +++ b/lib/lv_drivers/sdl/sdl_common.h @@ -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 diff --git a/lib/lv_drivers/sdl/sdl_common_internal.h b/lib/lv_drivers/sdl/sdl_common_internal.h new file mode 100644 index 0000000..0c5cce6 --- /dev/null +++ b/lib/lv_drivers/sdl/sdl_common_internal.h @@ -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 */ diff --git a/lib/lv_drivers/sdl/sdl_gpu.c b/lib/lv_drivers/sdl/sdl_gpu.c index 9794cc7..d372903 100644 --- a/lib/lv_drivers/sdl/sdl_gpu.c +++ b/lib/lv_drivers/sdl/sdl_gpu.c @@ -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 #include #include @@ -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; diff --git a/lib/lv_drivers/wayland/CMakeLists.txt b/lib/lv_drivers/wayland/CMakeLists.txt index 6ac6505..1d09e14 100644 --- a/lib/lv_drivers/wayland/CMakeLists.txt +++ b/lib/lv_drivers/wayland/CMakeLists.txt @@ -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) diff --git a/lib/lv_drivers/wayland/smm.c b/lib/lv_drivers/wayland/smm.c new file mode 100644 index 0000000..00ee012 --- /dev/null +++ b/lib/lv_drivers/wayland/smm.c @@ -0,0 +1,652 @@ +/** + * @file smm.c + * + */ +#if USE_WAYLAND + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 diff --git a/lib/lv_drivers/wayland/smm.h b/lib/lv_drivers/wayland/smm.h new file mode 100644 index 0000000..972936b --- /dev/null +++ b/lib/lv_drivers/wayland/smm.h @@ -0,0 +1,67 @@ +/** + * @file smm.h + * + */ +#ifndef SMM_H +#define SMM_H +#include +#include + +#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 diff --git a/lib/lv_drivers/wayland/wayland.c b/lib/lv_drivers/wayland/wayland.c index 11ed05c..e7840c2 100644 --- a/lib/lv_drivers/wayland/wayland.c +++ b/lib/lv_drivers/wayland/wayland.c @@ -7,6 +7,7 @@ * INCLUDES *********************/ #include "wayland.h" +#include "smm.h" #if USE_WAYLAND @@ -41,6 +42,10 @@ *********************/ #define BYTES_PER_PIXEL ((LV_COLOR_DEPTH + 7) / 8) +#define LVGL_DRAW_BUFFER_DIV (8) +#define DMG_CACHE_CAPACITY (32) +#define TAG_LOCAL (0) +#define TAG_BUFFER_DAMAGE (1) #if LV_WAYLAND_CLIENT_SIDE_DECORATIONS #define TITLE_BAR_HEIGHT 24 @@ -51,7 +56,7 @@ #endif #ifndef LV_WAYLAND_CYCLE_PERIOD -#define LV_WAYLAND_CYCLE_PERIOD LV_MIN(LV_DISP_DEF_REFR_PERIOD,1) +#define LV_WAYLAND_CYCLE_PERIOD LV_MIN(LV_DEF_REFR_PERIOD,1) #endif /********************** @@ -59,7 +64,7 @@ **********************/ enum object_type { - OBJECT_TITLEBAR, + OBJECT_TITLEBAR = 0, OBJECT_BUTTON_CLOSE, #if LV_WAYLAND_XDG_SHELL OBJECT_BUTTON_MAXIMIZE, @@ -69,11 +74,11 @@ enum object_type { OBJECT_BORDER_BOTTOM, OBJECT_BORDER_LEFT, OBJECT_BORDER_RIGHT, - FIRST_DECORATION = OBJECT_TITLEBAR, - LAST_DECORATION = OBJECT_BORDER_RIGHT, OBJECT_WINDOW, }; +#define FIRST_DECORATION (OBJECT_TITLEBAR) +#define LAST_DECORATION (OBJECT_BORDER_RIGHT) #define NUM_DECORATIONS (LAST_DECORATION-FIRST_DECORATION+1) struct window; @@ -116,36 +121,20 @@ struct seat } xkb; }; -struct buffer_hdl -{ - void *base; - int size; - struct wl_buffer *wl_buffer; - bool busy; -}; - -struct buffer_allocator -{ - int shm_mem_fd; - int shm_mem_size; - int shm_file_free_size; - struct wl_shm_pool *shm_pool; -}; - struct graphic_object { struct window *window; struct wl_surface *surface; bool surface_configured; + smm_buffer_t *pending_buffer; + smm_group_t *buffer_group; struct wl_subsurface *subsurface; enum object_type type; int width; int height; - struct buffer_hdl buffer; - struct input input; }; @@ -220,11 +209,16 @@ struct window #if LV_WAYLAND_XDG_SHELL struct xdg_surface *xdg_surface; struct xdg_toplevel *xdg_toplevel; + uint32_t wm_capabilities; #endif - struct buffer_allocator allocator; - struct graphic_object * body; + struct { + lv_area_t cache[DMG_CACHE_CAPACITY]; + unsigned char start; + unsigned char end; + unsigned size; + } dmg_cache; #if LV_WAYLAND_CLIENT_SIDE_DECORATIONS struct graphic_object * decoration[NUM_DECORATIONS]; @@ -1085,13 +1079,17 @@ static const struct wl_shell_surface_listener shell_surface_listener = { static void xdg_surface_handle_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial) { struct window *window = (struct window *)data; - struct buffer_hdl *buffer = &window->body->buffer; + struct wl_buffer *wl_buf; xdg_surface_ack_configure(xdg_surface, serial); - if ((!window->body->surface_configured) && (buffer->busy)) { - // Flush occured before surface was configured, so add buffer here - wl_surface_attach(window->body->surface, buffer->wl_buffer, 0, 0); + if ((!window->body->surface_configured) && + (window->body->pending_buffer != NULL)) { + // LVGL flush occured before surface was configured, so attach pending buffer here + wl_buf = SMM_BUFFER_PROPERTIES(window->body->pending_buffer)->tag[TAG_LOCAL]; + window->body->pending_buffer = NULL; + + wl_surface_attach(window->body->surface, wl_buf, 0, 0); wl_surface_commit(window->body->surface); window->flush_pending = true; } @@ -1144,10 +1142,23 @@ static void xdg_toplevel_handle_configure_bounds(void *data, struct xdg_toplevel */ } +static void xdg_toplevel_handle_wm_capabilities(void *data, struct xdg_toplevel *xdg_toplevel, + struct wl_array *capabilities) +{ + uint32_t *cap; + struct window *window = (struct window *)data; + + wl_array_for_each(cap, capabilities) { + window->wm_capabilities |= (1 << (*cap)); + /* TODO: Disable appropriate graphics/capabilities as appropriate */ + } +} + static const struct xdg_toplevel_listener xdg_toplevel_listener = { .configure = xdg_toplevel_handle_configure, .close = xdg_toplevel_handle_close, - .configure_bounds = xdg_toplevel_handle_configure_bounds + .configure_bounds = xdg_toplevel_handle_configure_bounds, + .wm_capabilities = xdg_toplevel_handle_wm_capabilities }; static void xdg_wm_base_ping(void *data, struct xdg_wm_base *xdg_wm_base, uint32_t serial) @@ -1211,176 +1222,246 @@ static const struct wl_registry_listener registry_listener = { static void handle_wl_buffer_release(void *data, struct wl_buffer *wl_buffer) { - struct buffer_hdl *buffer_hdl = (struct buffer_hdl *)data; - buffer_hdl->busy = false; + smm_release((smm_buffer_t *)data); } static const struct wl_buffer_listener wl_buffer_listener = { .release = handle_wl_buffer_release, }; -static bool initialize_allocator(struct buffer_allocator *allocator, const char *dir) +static void cache_clear(struct window *window) { - static const char template[] = "/lvgl-wayland-XXXXXX"; - char *name; + window->dmg_cache.start = window->dmg_cache.end; + window->dmg_cache.size = 0; +} - // Create file for shared memory allocation - name = lv_mem_alloc(strlen(dir) + sizeof(template)); - LV_ASSERT_MSG(name, "cannot allocate memory for name"); - if (!name) +static void cache_purge(struct window *window, smm_buffer_t *buf) +{ + lv_area_t *next_dmg; + smm_buffer_t *next_buf = smm_next(buf); + + /* Remove all damage areas up until start of next buffers damage */ + if (next_buf == NULL) { - return false; + cache_clear(window); } - - strcpy(name, dir); - strcat(name, template); - - allocator->shm_mem_fd = mkstemp(name); - - unlink(name); - lv_mem_free(name); - - LV_ASSERT_MSG((allocator->shm_mem_fd >= 0), "cannot create tmpfile"); - if (allocator->shm_mem_fd < 0) + else { - return false; + next_dmg = SMM_BUFFER_PROPERTIES(next_buf)->tag[TAG_BUFFER_DAMAGE]; + while ((window->dmg_cache.cache + window->dmg_cache.start) != next_dmg) + { + window->dmg_cache.start++; + window->dmg_cache.start %= DMG_CACHE_CAPACITY; + window->dmg_cache.size--; + } } - - allocator->shm_mem_size = 0; - allocator->shm_file_free_size = 0; - - return true; } -static void deinitialize_allocator(struct buffer_allocator *allocator) +static void cache_add_area(struct window *window, smm_buffer_t *buf, const lv_area_t *area) { - if (allocator->shm_pool) + if (SMM_BUFFER_PROPERTIES(buf)->tag[TAG_BUFFER_DAMAGE] == NULL) { - wl_shm_pool_destroy(allocator->shm_pool); + /* Buffer damage beyond cache capacity */ + goto done; } - if (allocator->shm_mem_fd >= 0) + if ((window->dmg_cache.start == window->dmg_cache.end) && + (window->dmg_cache.size)) { - close(allocator->shm_mem_fd); - allocator->shm_mem_fd = -1; + /* This buffer has more damage then the cache's capacity, so + * clear cache and leave buffer damage unrecorded + */ + cache_clear(window); + SMM_TAG(buf, TAG_BUFFER_DAMAGE, NULL); + goto done; } -} -static bool initialize_buffer(struct window *window, struct buffer_hdl *buffer_hdl, - int width, int height) -{ - struct application *app = window->application; - struct buffer_allocator *allocator = &window->allocator; - int allocated_size = 0; - int ret; - long sz = sysconf(_SC_PAGESIZE); + /* Add damage area to cache */ + memcpy(window->dmg_cache.cache + window->dmg_cache.end, + area, + sizeof(lv_area_t)); + window->dmg_cache.end++; + window->dmg_cache.end %= DMG_CACHE_CAPACITY; + window->dmg_cache.size++; - buffer_hdl->size = (((width * height * BYTES_PER_PIXEL) + sz - 1) / sz) * sz; +done: + return; +} - LV_LOG_TRACE("initializing buffer %dx%d (alloc size: %d)", - width, height, buffer_hdl->size); +static void cache_apply_areas(struct window *window, void *dest, void *src, smm_buffer_t *src_buf) +{ + unsigned long offset; + unsigned char start; + lv_coord_t y; + lv_area_t *dmg; + lv_area_t *next_dmg; + smm_buffer_t *next_buf = smm_next(src_buf); + const struct smm_buffer_properties *props = SMM_BUFFER_PROPERTIES(src_buf); + struct graphic_object *obj = SMM_GROUP_PROPERTIES(props->group)->tag[TAG_LOCAL]; - if (allocator->shm_file_free_size < buffer_hdl->size) + if (next_buf == NULL) { - do - { - ret = ftruncate(allocator->shm_mem_fd, - allocator->shm_mem_size + (buffer_hdl->size - allocator->shm_file_free_size)); - } - while ((ret < 0) && (errno == EINTR)); - - if (ret < 0) - { - LV_LOG_ERROR("ftruncate failed: %s", strerror(errno)); - goto err_out; - } - else - { - allocated_size = (buffer_hdl->size - allocator->shm_file_free_size); - } - - LV_ASSERT_MSG((allocated_size >= 0), "allocated_size is negative"); + next_dmg = (window->dmg_cache.cache + window->dmg_cache.end); } - - buffer_hdl->base = mmap(NULL, buffer_hdl->size, - PROT_READ | PROT_WRITE, MAP_SHARED, - allocator->shm_mem_fd, - allocator->shm_mem_size - allocator->shm_file_free_size); - if (buffer_hdl->base == MAP_FAILED) + else { - LV_LOG_ERROR("mmap failed: %s", strerror(errno)); - goto err_inc_free; + next_dmg = SMM_BUFFER_PROPERTIES(next_buf)->tag[TAG_BUFFER_DAMAGE]; } - if (!allocator->shm_pool) + /* Apply all buffer damage areas */ + start = ((lv_area_t *)SMM_BUFFER_PROPERTIES(src_buf)->tag[TAG_BUFFER_DAMAGE] - window->dmg_cache.cache); + while ((window->dmg_cache.cache + start) != next_dmg) { - // Create SHM pool - allocator->shm_pool = wl_shm_create_pool(app->shm, - allocator->shm_mem_fd, - allocator->shm_mem_size + allocated_size); - if (!allocator->shm_pool) + /* Copy an area from source to destination (line-by-line) */ + dmg = (window->dmg_cache.cache + start); + for (y = dmg->y1; y <= dmg->y2; y++) { - LV_LOG_ERROR("cannot create shm pool"); - goto err_unmap; + offset = (dmg->x1 + (y * (obj->width * BYTES_PER_PIXEL))); + memcpy(((char *)dest) + offset, + ((char *)src) + offset, + ((dmg->x2 - dmg->x1 + 1) * BYTES_PER_PIXEL)); } - } - else if (allocated_size > 0) - { - // Resize SHM pool - wl_shm_pool_resize(allocator->shm_pool, - allocator->shm_mem_size + allocated_size); - } - // Create buffer - buffer_hdl->wl_buffer = wl_shm_pool_create_buffer(allocator->shm_pool, - allocator->shm_mem_size - allocator->shm_file_free_size, - width, height, - width * BYTES_PER_PIXEL, - app->format); - if (!buffer_hdl->wl_buffer) - { - LV_LOG_ERROR("cannot create shm buffer"); - goto err_unmap; + start++; + start %= DMG_CACHE_CAPACITY; } - wl_buffer_add_listener(buffer_hdl->wl_buffer, &wl_buffer_listener, buffer_hdl); +} - /* Update size of SHM */ - allocator->shm_mem_size += allocated_size; - allocator->shm_file_free_size = LV_MAX(0, (allocator->shm_file_free_size - buffer_hdl->size)); +static bool sme_new_pool(void *ctx, smm_pool_t *pool) +{ + struct wl_shm_pool *wl_pool; + struct application *app = ctx; + const struct smm_pool_properties *props = SMM_POOL_PROPERTIES(pool); - lv_memset_00(buffer_hdl->base, buffer_hdl->size); + wl_pool = wl_shm_create_pool(app->shm, + props->fd, + props->size); - return true; + SMM_TAG(pool, TAG_LOCAL, wl_pool); + return (wl_pool == NULL); +} + +static void sme_expand_pool(void *ctx, smm_pool_t *pool) +{ + const struct smm_pool_properties *props = SMM_POOL_PROPERTIES(pool); -err_unmap: - munmap(buffer_hdl->base, buffer_hdl->size); + wl_shm_pool_resize(props->tag[TAG_LOCAL], props->size); +} -err_inc_free: - allocator->shm_file_free_size += allocated_size; +static void sme_free_pool(void *ctx, smm_pool_t *pool) +{ + struct wl_shm_pool *wl_pool = SMM_POOL_PROPERTIES(pool)->tag[TAG_LOCAL]; + wl_shm_pool_destroy(wl_pool); +} -err_out: - return false; +static bool sme_new_buffer(void *ctx, smm_buffer_t *buf) +{ + struct wl_buffer *wl_buf; + bool fail_alloc = true; + const struct smm_buffer_properties *props = SMM_BUFFER_PROPERTIES(buf); + struct wl_shm_pool *wl_pool = SMM_POOL_PROPERTIES(props->pool)->tag[TAG_LOCAL]; + struct application *app = ctx; + struct graphic_object *obj = SMM_GROUP_PROPERTIES(props->group)->tag[TAG_LOCAL]; + + wl_buf = wl_shm_pool_create_buffer(wl_pool, + props->offset, + obj->width, + obj->height, + obj->width * BYTES_PER_PIXEL, + app->format); + + if (wl_buf != NULL) { + wl_buffer_add_listener(wl_buf, &wl_buffer_listener, buf); + SMM_TAG(buf, TAG_LOCAL, wl_buf); + SMM_TAG(buf, TAG_BUFFER_DAMAGE, NULL); + fail_alloc = false; + } + + return fail_alloc; } -static bool deinitialize_buffer(struct window *window, struct buffer_hdl *buffer_hdl) +static bool sme_init_buffer(void *ctx, smm_buffer_t *buf) { - struct buffer_allocator *allocator = &window->allocator; + smm_buffer_t *src; + void *src_base; + bool fail_init = true; + bool dmg_missing = false; + void *buf_base = smm_map(buf); + const struct smm_buffer_properties *props = SMM_BUFFER_PROPERTIES(buf); + struct graphic_object *obj = SMM_GROUP_PROPERTIES(props->group)->tag[TAG_LOCAL]; - if (buffer_hdl->wl_buffer) + if (buf_base == NULL) { - wl_buffer_destroy(buffer_hdl->wl_buffer); - buffer_hdl->wl_buffer = NULL; + LV_LOG_ERROR("cannot map in buffer to initialize"); + goto done; } - if (buffer_hdl->size > 0) + /* Determine if all subsequent buffers damage is recorded */ + for (src = smm_next(buf); src != NULL; src = smm_next(src)) { - munmap(buffer_hdl->base, buffer_hdl->size); - allocator->shm_file_free_size += buffer_hdl->size; - buffer_hdl->base = 0; - buffer_hdl->size = 0; + if (SMM_BUFFER_PROPERTIES(src)->tag[TAG_BUFFER_DAMAGE] == NULL) + { + dmg_missing = true; + break; + } } - return true; + if ((smm_next(buf) == NULL) || dmg_missing) + { + /* Missing subsequent buffer damage, initialize by copying the most + * recently acquired buffers data + */ + src = smm_latest(props->group); + if ((src != NULL) && + (src != buf)) + { + /* Map and copy latest buffer data */ + src_base = smm_map(src); + if (src_base == NULL) + { + LV_LOG_ERROR("cannot map most recent buffer to copy"); + goto done; + } + + memcpy(buf_base, + src_base, + (obj->width * BYTES_PER_PIXEL) * obj->height); + } + } + else + { + /* All subsequent buffers damage is recorded, initialize by applying + * their damage to this buffer + */ + for (src = smm_next(buf); src != NULL; src = smm_next(src)) + { + src_base = smm_map(src); + if (src_base == NULL) + { + LV_LOG_ERROR("cannot map source buffer to copy from"); + goto done; + } + + cache_apply_areas(obj->window, buf_base, src_base, src); + } + + /* Purge out-of-date cached damage (up to and including next buffer) */ + src = smm_next(buf); + if (src == NULL) + { + cache_purge(obj->window, src); + } + } + + fail_init = false; +done: + return fail_init; +} + +static void sme_free_buffer(void *ctx, smm_buffer_t *buf) +{ + struct wl_buffer *wl_buf = SMM_BUFFER_PROPERTIES(buf)->tag[TAG_LOCAL]; + wl_buffer_destroy(wl_buf); } static struct graphic_object * create_graphic_obj(struct application *app, struct window *window, @@ -1389,27 +1470,35 @@ static struct graphic_object * create_graphic_obj(struct application *app, struc { struct graphic_object *obj; - obj = lv_mem_alloc(sizeof(*obj)); + obj = lv_malloc(sizeof(*obj)); LV_ASSERT_MALLOC(obj); if (!obj) { - return NULL; + goto err_out; } lv_memset(obj, 0x00, sizeof(struct graphic_object)); - obj->window = window; - obj->type = type; - obj->surface = wl_compositor_create_surface(app->compositor); if (!obj->surface) { LV_LOG_ERROR("cannot create surface for graphic object"); - goto err_out; + goto err_free; + } + + obj->buffer_group = smm_create(); + if (obj->buffer_group == NULL) + { + LV_LOG_ERROR("cannot create buffer group for graphic object"); + goto err_destroy_surface; } + obj->window = window; + obj->type = type; obj->surface_configured = true; + obj->pending_buffer = NULL; wl_surface_set_user_data(obj->surface, obj); + SMM_TAG(obj->buffer_group, TAG_LOCAL, obj); return obj; @@ -1417,7 +1506,7 @@ err_destroy_surface: wl_surface_destroy(obj->surface); err_free: - lv_mem_free(obj); + lv_free(obj); err_out: return NULL; @@ -1431,16 +1520,90 @@ static void destroy_graphic_obj(struct graphic_object * obj) } wl_surface_destroy(obj->surface); - - lv_mem_free(obj); + smm_destroy(obj->buffer_group); + lv_free(obj); } #if LV_WAYLAND_CLIENT_SIDE_DECORATIONS +static bool attach_decoration(struct window *window, struct graphic_object * decoration, + smm_buffer_t *decoration_buffer, struct graphic_object * parent) +{ + struct wl_buffer *wl_buf = SMM_BUFFER_PROPERTIES(decoration_buffer)->tag[TAG_LOCAL]; + + int pos_x, pos_y; + int x, y; + + switch (decoration->type) + { + case OBJECT_TITLEBAR: + pos_x = 0; + pos_y = -TITLE_BAR_HEIGHT; + break; + case OBJECT_BUTTON_CLOSE: + pos_x = parent->width - 1 * (BUTTON_MARGIN + BUTTON_SIZE); + pos_y = -1 * (BUTTON_MARGIN + BUTTON_SIZE + (BORDER_SIZE / 2)); + break; +#if LV_WAYLAND_XDG_SHELL + case OBJECT_BUTTON_MAXIMIZE: + pos_x = parent->width - 2 * (BUTTON_MARGIN + BUTTON_SIZE); + pos_y = -1 * (BUTTON_MARGIN + BUTTON_SIZE + (BORDER_SIZE / 2)); + break; + case OBJECT_BUTTON_MINIMIZE: + pos_x = parent->width - 3 * (BUTTON_MARGIN + BUTTON_SIZE); + pos_y = -1 * (BUTTON_MARGIN + BUTTON_SIZE + (BORDER_SIZE / 2)); + break; +#endif + case OBJECT_BORDER_TOP: + pos_x = -BORDER_SIZE; + pos_y = -(BORDER_SIZE + TITLE_BAR_HEIGHT); + break; + case OBJECT_BORDER_BOTTOM: + pos_x = -BORDER_SIZE; + pos_y = parent->height; + break; + case OBJECT_BORDER_LEFT: + pos_x = -BORDER_SIZE; + pos_y = -TITLE_BAR_HEIGHT; + break; + case OBJECT_BORDER_RIGHT: + pos_x = parent->width; + pos_y = -TITLE_BAR_HEIGHT; + break; + default: + LV_ASSERT_MSG(0, "Invalid object type"); + return false; + } + + decoration->subsurface = wl_subcompositor_get_subsurface(window->application->subcompositor, + decoration->surface, + parent->surface); + if (!decoration->subsurface) + { + LV_LOG_ERROR("cannot get subsurface for decoration"); + goto err_destroy_surface; + } + + wl_subsurface_set_desync(decoration->subsurface); + wl_subsurface_set_position(decoration->subsurface, pos_x, pos_y); + + wl_surface_attach(decoration->surface, wl_buf, 0, 0); + wl_surface_commit(decoration->surface); + + return true; + +err_destroy_surface: + wl_surface_destroy(decoration->surface); + decoration->surface = NULL; + + return false; +} + static bool create_decoration(struct window *window, struct graphic_object * decoration, int window_width, int window_height) { - struct buffer_hdl * buffer = &decoration->buffer; + smm_buffer_t *buf; + void *buf_base; int x, y; switch (decoration->type) @@ -1484,26 +1647,38 @@ static bool create_decoration(struct window *window, return false; } - if (!initialize_buffer(window, buffer, decoration->width, decoration->height)) + smm_resize(decoration->buffer_group, + (decoration->width * BYTES_PER_PIXEL) * decoration->height); + + buf = smm_acquire(decoration->buffer_group); + if (buf == NULL) + { + LV_LOG_ERROR("cannot allocate buffer for decoration"); + return false; + } + + buf_base = smm_map(buf); + if (buf_base == NULL) { - LV_LOG_ERROR("cannot create buffer for decoration"); + LV_LOG_ERROR("cannot map in allocated decoration buffer"); + smm_release(buf); return false; } switch (decoration->type) { case OBJECT_TITLEBAR: - lv_color_fill((lv_color_t *)buffer->base, + lv_color_fill((lv_color_t *)buf_base, lv_color_make(0x66, 0x66, 0x66), (decoration->width * decoration->height)); break; case OBJECT_BUTTON_CLOSE: - lv_color_fill((lv_color_t *)buffer->base, + lv_color_fill((lv_color_t *)buf_base, lv_color_make(0xCC, 0xCC, 0xCC), (decoration->width * decoration->height)); for (y = 0; y < decoration->height; y++) { for (x = 0; x < decoration->width; x++) { - lv_color_t *pixel = ((lv_color_t *)buffer->base + (y * decoration->width) + x); + lv_color_t *pixel = ((lv_color_t *)buf_base + (y * decoration->width) + x); if ((x >= BUTTON_PADDING) && (x < decoration->width - BUTTON_PADDING)) { if ((x == y) || (x == decoration->width - 1 - y)) @@ -1520,13 +1695,13 @@ static bool create_decoration(struct window *window, break; #if LV_WAYLAND_XDG_SHELL case OBJECT_BUTTON_MAXIMIZE: - lv_color_fill((lv_color_t *)buffer->base, + lv_color_fill((lv_color_t *)buf_base, lv_color_make(0xCC, 0xCC, 0xCC), (decoration->width * decoration->height)); for (y = 0; y < decoration->height; y++) { for (x = 0; x < decoration->width; x++) { - lv_color_t *pixel = ((lv_color_t *)buffer->base + (y * decoration->width) + x); + lv_color_t *pixel = ((lv_color_t *)buf_base + (y * decoration->width) + x); if (((x == BUTTON_PADDING) && (y >= BUTTON_PADDING) && (y < decoration->height - BUTTON_PADDING)) || ((x == (decoration->width - BUTTON_PADDING)) && (y >= BUTTON_PADDING) && (y <= decoration->height - BUTTON_PADDING)) || ((y == BUTTON_PADDING) && (x >= BUTTON_PADDING) && (x < decoration->width - BUTTON_PADDING)) || @@ -1539,13 +1714,13 @@ static bool create_decoration(struct window *window, } break; case OBJECT_BUTTON_MINIMIZE: - lv_color_fill((lv_color_t *)buffer->base, + lv_color_fill((lv_color_t *)buf_base, lv_color_make(0xCC, 0xCC, 0xCC), (decoration->width * decoration->height)); for (y = 0; y < decoration->height; y++) { for (x = 0; x < decoration->width; x++) { - lv_color_t *pixel = ((lv_color_t *)buffer->base + (y * decoration->width) + x); + lv_color_t *pixel = ((lv_color_t *)buf_base + (y * decoration->width) + x); if ((x >= BUTTON_PADDING) && (x < decoration->width - BUTTON_PADDING) && (y > decoration->height - (2 * BUTTON_PADDING)) && (y < decoration->height - BUTTON_PADDING)) { @@ -1562,7 +1737,7 @@ static bool create_decoration(struct window *window, case OBJECT_BORDER_LEFT: /* fallthrough */ case OBJECT_BORDER_RIGHT: - lv_color_fill((lv_color_t *)buffer->base, + lv_color_fill((lv_color_t *)buf_base, lv_color_make(0x66, 0x66, 0x66), (decoration->width * decoration->height)); break; default: @@ -1570,80 +1745,7 @@ static bool create_decoration(struct window *window, return false; } - return true; -} - -static bool attach_decoration(struct window *window, struct graphic_object * decoration, - struct graphic_object * parent) -{ - struct buffer_hdl * buffer = &decoration->buffer; - int pos_x, pos_y; - int x, y; - - switch (decoration->type) - { - case OBJECT_TITLEBAR: - pos_x = 0; - pos_y = -TITLE_BAR_HEIGHT; - break; - case OBJECT_BUTTON_CLOSE: - pos_x = parent->width - 1 * (BUTTON_MARGIN + BUTTON_SIZE); - pos_y = -1 * (BUTTON_MARGIN + BUTTON_SIZE + (BORDER_SIZE / 2)); - break; -#if LV_WAYLAND_XDG_SHELL - case OBJECT_BUTTON_MAXIMIZE: - pos_x = parent->width - 2 * (BUTTON_MARGIN + BUTTON_SIZE); - pos_y = -1 * (BUTTON_MARGIN + BUTTON_SIZE + (BORDER_SIZE / 2)); - break; - case OBJECT_BUTTON_MINIMIZE: - pos_x = parent->width - 3 * (BUTTON_MARGIN + BUTTON_SIZE); - pos_y = -1 * (BUTTON_MARGIN + BUTTON_SIZE + (BORDER_SIZE / 2)); - break; -#endif - case OBJECT_BORDER_TOP: - pos_x = -BORDER_SIZE; - pos_y = -(BORDER_SIZE + TITLE_BAR_HEIGHT); - break; - case OBJECT_BORDER_BOTTOM: - pos_x = -BORDER_SIZE; - pos_y = parent->height; - break; - case OBJECT_BORDER_LEFT: - pos_x = -BORDER_SIZE; - pos_y = -TITLE_BAR_HEIGHT; - break; - case OBJECT_BORDER_RIGHT: - pos_x = parent->width; - pos_y = -TITLE_BAR_HEIGHT; - break; - default: - LV_ASSERT_MSG(0, "Invalid object type"); - return false; - } - - decoration->subsurface = wl_subcompositor_get_subsurface(window->application->subcompositor, - decoration->surface, - parent->surface); - if (!decoration->subsurface) - { - LV_LOG_ERROR("cannot get subsurface for decoration"); - goto err_destroy_surface; - } - - wl_subsurface_set_desync(decoration->subsurface); - wl_subsurface_set_position(decoration->subsurface, pos_x, pos_y); - - wl_surface_attach(decoration->surface, buffer->wl_buffer, 0, 0); - wl_surface_commit(decoration->surface); - buffer->busy = true; - - return true; - -err_destroy_surface: - wl_surface_destroy(decoration->surface); - decoration->surface = NULL; - - return false; + return attach_decoration(window, decoration, buf, window->body); } static void detach_decoration(struct window *window, @@ -1659,25 +1761,10 @@ static void detach_decoration(struct window *window, static bool resize_window(struct window *window, int width, int height) { - struct buffer_hdl *buffer = &window->body->buffer; + lv_color_t * buf1 = NULL; LV_LOG_TRACE("resize window %dx%d", width, height); - // De-initialize previous buffers - if (buffer->busy) - { - LV_LOG_WARN("Deinitializing busy window buffer..."); - wl_surface_attach(window->body->surface, NULL, 0, 0); - wl_surface_commit(window->body->surface); - buffer->busy = false; - } - - if (!deinitialize_buffer(window, buffer)) - { - LV_LOG_ERROR("failed to deinitialize window buffer"); - return false; - } - #if LV_WAYLAND_CLIENT_SIDE_DECORATIONS int b; for (b = 0; b < NUM_DECORATIONS; b++) @@ -1685,17 +1772,12 @@ static bool resize_window(struct window *window, int width, int height) if (window->decoration[b] != NULL) { detach_decoration(window, window->decoration[b]); - deinitialize_buffer(window, &window->decoration[b]->buffer); } } #endif - // Initialize backing buffer - if (!initialize_buffer(window, buffer, width, height)) - { - LV_LOG_ERROR("failed to initialize window buffer"); - return false; - } + /* Update size for newly allocated buffers */ + smm_resize(window->body->buffer_group, (width * BYTES_PER_PIXEL) * height); window->width = width; window->height = height; @@ -1713,17 +1795,24 @@ static bool resize_window(struct window *window, int width, int height) { LV_LOG_ERROR("failed to create decoration %d", b); } - else if (!attach_decoration(window, window->decoration[b], window->body)) - { - LV_LOG_ERROR("failed to attach decoration %d", b); - } } } #endif if (window->lv_disp != NULL) { - // Propagate resize to upper layers + /* Resize draw buffer */ + buf1 = lv_malloc(((width * height) / LVGL_DRAW_BUFFER_DIV) * sizeof(lv_color_t)); + if (!buf1) + { + LV_LOG_ERROR("failed to resize draw buffer"); + return false; + } + + lv_free(window->lv_disp_draw_buf.buf1); + lv_disp_draw_buf_init(&window->lv_disp_draw_buf, buf1, NULL, (width * height) / LVGL_DRAW_BUFFER_DIV); + + /* Propagate resize to LVGL */ window->lv_disp_drv.hor_res = width; window->lv_disp_drv.ver_res = height; lv_disp_drv_update(window->lv_disp, &window->lv_disp_drv); @@ -1750,19 +1839,12 @@ static struct window *create_window(struct application *app, int width, int heig window->application = app; - // Initialize buffer allocator - if (!initialize_allocator(&window->allocator, app->xdg_runtime_dir)) - { - LV_LOG_ERROR("cannot init memory allocator"); - goto err_free_window; - } - // Create wayland buffer and surface window->body = create_graphic_obj(app, window, OBJECT_WINDOW, NULL); if (!window->body) { LV_LOG_ERROR("cannot create window body"); - goto err_deinit_allocator; + goto err_free_window; } // Create shell surface @@ -1869,12 +1951,9 @@ err_destroy_shell_surface: err_destroy_surface: wl_surface_destroy(window->body->surface); -err_deinit_allocator: - deinitialize_allocator(&window->allocator); - err_free_window: _lv_ll_remove(&app->window_ll, window); - lv_mem_free(window); + lv_free(window); return NULL; } @@ -1905,98 +1984,125 @@ static void destroy_window(struct window *window) { if (window->decoration[b]) { - deinitialize_buffer(window, &window->decoration[b]->buffer); destroy_graphic_obj(window->decoration[b]); window->decoration[b] = NULL; } } #endif - deinitialize_buffer(window, &window->body->buffer); destroy_graphic_obj(window->body); - - deinitialize_allocator(&window->allocator); } static void _lv_wayland_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p) { + unsigned long offset; + int32_t x; + int32_t y; + void *buf_base; + struct wl_buffer *wl_buf; + lv_coord_t src_width = (area->x2 - area->x1 + 1); + lv_coord_t src_height = (area->y2 - area->y1 + 1); struct window *window = disp_drv->user_data; - struct buffer_hdl *buffer = &window->body->buffer; + smm_buffer_t *buf = window->body->pending_buffer; const lv_coord_t hres = (disp_drv->rotated == 0) ? (disp_drv->hor_res) : (disp_drv->ver_res); const lv_coord_t vres = (disp_drv->rotated == 0) ? (disp_drv->ver_res) : (disp_drv->hor_res); - /* If private data is not set, it means window has not been initialized */ - if (!window) + /* If window has been / is being closed, or is not visible, skip flush */ + if (window->closed || window->shall_close) { - LV_LOG_ERROR("please intialize wayland display using lv_wayland_create_window()"); - return; + goto skip; } - /* If window has been / is being closed, or is not visible, skip rendering */ - else if (window->closed || window->shall_close) - { - lv_disp_flush_ready(disp_drv); - return; - } - /* Return if the area is out the screen */ + /* Skip if the area is out the screen */ else if ((area->x2 < 0) || (area->y2 < 0) || (area->x1 > hres - 1) || (area->y1 > vres - 1)) { - lv_disp_flush_ready(disp_drv); - return; + goto skip; } else if (window->resize_pending) { LV_LOG_TRACE("skip flush since resize is pending"); - lv_disp_flush_ready(disp_drv); - return; + goto skip; } - else if (buffer->busy) + + /* Acquire and map a buffer to attach/commit to surface */ + if (buf == NULL) { - LV_LOG_WARN("skip flush since wayland backing buffer is busy"); - lv_disp_flush_ready(disp_drv); - return; + buf = smm_acquire(window->body->buffer_group); + if (buf == NULL) + { + LV_LOG_ERROR("cannot acquire a window body buffer"); + goto skip; + } + + window->body->pending_buffer = buf; + SMM_TAG(buf, + TAG_BUFFER_DAMAGE, + window->dmg_cache.cache + window->dmg_cache.end); } - int32_t x; - int32_t y; + buf_base = smm_map(buf); + if (buf_base == NULL) + { + LV_LOG_ERROR("cannot map in window body buffer"); + goto skip; + } - for (y = area->y1; y <= area->y2 && y < disp_drv->ver_res; y++) + /* Modify specified area in buffer */ + for (y = area->y1; y <= area->y2; y++) { - for (x = area->x1; x <= area->x2 && x < disp_drv->hor_res; x++) + offset = ((area->x1 + (y * disp_drv->hor_res)) * BYTES_PER_PIXEL); +#if (LV_COLOR_DEPTH == 1) + for (x = 0; x < src_width; x++) { - int offset = (y * disp_drv->hor_res) + x; -#if (LV_COLOR_DEPTH == 32) - uint32_t * const buf = (uint32_t *)buffer->base + offset; - *buf = color_p->full; -#elif (LV_COLOR_DEPTH == 16) - uint16_t * const buf = (uint16_t *)buffer->base + offset; - *buf = color_p->full; -#elif (LV_COLOR_DEPTH == 8) - uint8_t * const buf = (uint8_t *)buffer->base + offset; - *buf = color_p->full; -#elif (LV_COLOR_DEPTH == 1) - uint8_t * const buf = (uint8_t *)buffer->base + offset; - *buf = ((0x07 * color_p->ch.red) << 5) | - ((0x07 * color_p->ch.green) << 2) | - ((0x03 * color_p->ch.blue) << 0); -#endif + uint8_t * const dest = (uint8_t *)buf_base + offset + x; + *dest = ((0x07 * color_p->ch.red) << 5) | + ((0x07 * color_p->ch.green) << 2) | + ((0x03 * color_p->ch.blue) << 0); color_p++; } +#else + memcpy(((char *)buf_base) + offset, + color_p, + src_width * BYTES_PER_PIXEL); + color_p += src_width; +#endif } - wl_surface_damage(window->body->surface, area->x1, area->y1, - (area->x2 - area->x1 + 1), (area->y2 - area->y1 + 1)); + /* Mark surface damage */ + wl_surface_damage(window->body->surface, + area->x1, + area->y1, + src_width, + src_height); + + /* Cache buffer damage for future buffer initializations */ + cache_add_area(window, buf, area); if (lv_disp_flush_is_last(disp_drv)) { if (window->body->surface_configured) { - wl_surface_attach(window->body->surface, buffer->wl_buffer, 0, 0); - wl_surface_commit(window->body->surface); + /* Finally, attach buffer and commit to surface */ + wl_buf = SMM_BUFFER_PROPERTIES(buf)->tag[TAG_LOCAL]; + wl_surface_attach(window->body->surface, wl_buf, 0, 0); + wl_surface_commit(window->body->surface); + window->body->pending_buffer = NULL; } - buffer->busy = true; + window->flush_pending = true; } + goto done; +skip: + if (buf != NULL) { + /* Cleanup any intermediate state (in the event that this flush being + * skipped is in the middle of a flush sequence) + */ + cache_clear(window); + SMM_TAG(buf, TAG_BUFFER_DAMAGE, NULL); + smm_release(buf); + window->body->pending_buffer = NULL; + } +done: lv_disp_flush_ready(disp_drv); } @@ -2029,7 +2135,6 @@ static void _lv_wayland_handle_output(void) } else if (window->shall_close) { - destroy_window(window); window->closed = true; window->shall_close = false; shall_flush = true; @@ -2059,25 +2164,11 @@ static void _lv_wayland_handle_output(void) { window->application->keyboard_obj = NULL; } + destroy_window(window); } else if (window->resize_pending) { - bool do_resize = !window->body->buffer.busy; -#if LV_WAYLAND_CLIENT_SIDE_DECORATIONS - if (!window->application->opt_disable_decorations && !window->fullscreen) - { - int d; - for (d = 0; d < NUM_DECORATIONS; d++) - { - if ((window->decoration[d] != NULL) && (window->decoration[d]->buffer.busy)) - { - do_resize = false; - break; - } - } - } -#endif - if (do_resize && resize_window(window, window->resize_width, window->resize_height)) + if (resize_window(window, window->resize_width, window->resize_height)) { window->resize_width = window->width; window->resize_height = window->height; @@ -2178,6 +2269,16 @@ static void _lv_wayland_touch_read(lv_indev_drv_t *drv, lv_indev_data_t *data) */ void lv_wayland_init(void) { + struct smm_events evs = { + NULL, + sme_new_pool, + sme_expand_pool, + sme_free_pool, + sme_new_buffer, + sme_init_buffer, + sme_free_buffer + }; + application.xdg_runtime_dir = getenv("XDG_RUNTIME_DIR"); LV_ASSERT_MSG(application.xdg_runtime_dir, "cannot get XDG_RUNTIME_DIR"); @@ -2222,6 +2323,9 @@ void lv_wayland_init(void) return; } + smm_init(&evs); + smm_setctx(&application); + #ifdef LV_WAYLAND_CLIENT_SIDE_DECORATIONS const char * env_disable_decorations = getenv("LV_WAYLAND_DISABLE_WINDOWDECORATION"); application.opt_disable_decorations = ((env_disable_decorations != NULL) && @@ -2230,12 +2334,14 @@ void lv_wayland_init(void) _lv_ll_init(&application.window_ll, sizeof(struct window)); +#ifndef LV_WAYLAND_TIMER_HANDLER application.cycle_timer = lv_timer_create(_lv_wayland_cycle, LV_WAYLAND_CYCLE_PERIOD, NULL); LV_ASSERT_MSG(application.cycle_timer, "failed to create cycle timer"); if (!application.cycle_timer) { return; } +#endif } /** @@ -2253,6 +2359,8 @@ void lv_wayland_deinit(void) } } + smm_deinit(); + if (application.shm) { wl_shm_destroy(application.shm); @@ -2327,7 +2435,7 @@ lv_disp_t * lv_wayland_create_window(lv_coord_t hor_res, lv_coord_t ver_res, cha window->close_cb = close_cb; /* Initialize draw buffer */ - buf1 = lv_mem_alloc(hor_res * ver_res * sizeof(lv_color_t)); + buf1 = lv_malloc(((hor_res * ver_res) / LVGL_DRAW_BUFFER_DIV) * sizeof(lv_color_t)); if (!buf1) { LV_LOG_ERROR("failed to allocate draw buffer"); @@ -2335,7 +2443,7 @@ lv_disp_t * lv_wayland_create_window(lv_coord_t hor_res, lv_coord_t ver_res, cha return NULL; } - lv_disp_draw_buf_init(&window->lv_disp_draw_buf, buf1, NULL, hor_res * ver_res); + lv_disp_draw_buf_init(&window->lv_disp_draw_buf, buf1, NULL, (hor_res * ver_res) / LVGL_DRAW_BUFFER_DIV); /* Initialize display driver */ lv_disp_drv_init(&window->lv_disp_drv); @@ -2437,38 +2545,6 @@ bool lv_wayland_window_is_open(lv_disp_t * disp) return open; } -/** - * Check if a Wayland flush is outstanding (i.e. data still needs to be sent to - * the compositor, but the compositor pipe/connection is unable to take more - * data at this time) for a window on the specified display. Otherwise (if - * argument is NULL), check if any window flush is outstanding. - * @return true if a flush is outstanding, false otherwise - */ -bool lv_wayland_window_is_flush_pending(lv_disp_t * disp) -{ - struct window *window; - bool flush_pending = false; - - if (disp == NULL) - { - _LV_LL_READ(&application.window_ll, window) - { - if (window->flush_pending) - { - flush_pending = true; - break; - } - } - } - else - { - window = disp->driver->user_data; - flush_pending = window->flush_pending; - } - - return flush_pending; -} - /** * Set/unset window fullscreen mode * @param disp LVGL display using window to be set/unset fullscreen @@ -2599,13 +2675,6 @@ uint32_t lv_wayland_timer_handler(void) lv_timer_t *input_timer[4]; uint32_t time_till_next; - /* Remove cycle timer (as this function is doing its work) */ - if (application.cycle_timer != NULL) - { - lv_timer_del(application.cycle_timer); - application.cycle_timer = NULL; - } - /* Wayland input handling */ _lv_wayland_handle_input(); @@ -2632,6 +2701,20 @@ uint32_t lv_wayland_timer_handler(void) /* Wayland output handling */ _lv_wayland_handle_output(); + + /* Set 'errno' if a Wayland flush is outstanding (i.e. data still needs to + * be sent to the compositor, but the compositor pipe/connection is unable + * to take more data at this time). + */ + _LV_LL_READ(&application.window_ll, window) + { + if (window->flush_pending) + { + errno = EAGAIN; + break; + } + } + return time_till_next; } #endif diff --git a/lib/lv_drivers/wayland/wayland.h b/lib/lv_drivers/wayland/wayland.h index 578af47..523d188 100644 --- a/lib/lv_drivers/wayland/wayland.h +++ b/lib/lv_drivers/wayland/wayland.h @@ -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); diff --git a/lib/lv_drivers/win32drv/win32drv.c b/lib/lv_drivers/win32drv/win32drv.c index 7a80434..4138741 100644 --- a/lib/lv_drivers/win32drv/win32drv.c +++ b/lib/lv_drivers/win32drv/win32drv.c @@ -12,6 +12,7 @@ #if USE_WIN32DRV #include +#include #include #include #include @@ -142,9 +143,6 @@ static void lv_win32_display_driver_flush_callback( const lv_area_t* area, lv_color_t* color_p); -static void lv_win32_display_refresh_handler( - lv_timer_t* param); - static void lv_win32_pointer_driver_read_callback( lv_indev_drv_t* indev_drv, lv_indev_data_t* data); @@ -157,6 +155,9 @@ static void lv_win32_encoder_driver_read_callback( lv_indev_drv_t* indev_drv, lv_indev_data_t* data); +static lv_win32_window_context_t* lv_win32_get_display_context( + lv_disp_t* display); + static LRESULT CALLBACK lv_win32_window_message_callback( HWND hWnd, UINT uMsg, @@ -166,6 +167,25 @@ static LRESULT CALLBACK lv_win32_window_message_callback( static unsigned int __stdcall lv_win32_window_thread_entrypoint( void* raw_parameter); +static void lv_win32_push_key_to_keyboard_queue( + lv_win32_window_context_t* context, + uint32_t key, + lv_indev_state_t state) +{ + lv_win32_keyboard_queue_item_t* current = + (lv_win32_keyboard_queue_item_t*)(_aligned_malloc( + sizeof(lv_win32_keyboard_queue_item_t), + MEMORY_ALLOCATION_ALIGNMENT)); + if (current) + { + current->key = key; + current->state = state; + InterlockedPushEntrySList( + context->keyboard_queue, + ¤t->ItemEntry); + } +} + /********************** * GLOBAL VARIABLES **********************/ @@ -182,24 +202,6 @@ EXTERN_C lv_indev_t* lv_win32_encoder_device_object = NULL; static HWND g_window_handle = NULL; -static HDC g_buffer_dc_handle = NULL; -static UINT32* g_pixel_buffer = NULL; -static SIZE_T g_pixel_buffer_size = 0; - -static lv_disp_t* g_display = NULL; -static bool volatile g_display_refreshing = false; - -static bool volatile g_mouse_pressed = false; -static LPARAM volatile g_mouse_value = 0; - -static bool volatile g_mousewheel_pressed = false; -static int16_t volatile g_mousewheel_value = 0; - -static bool volatile g_keyboard_pressed = false; -static WPARAM volatile g_keyboard_value = 0; - -static int volatile g_dpi_value = USER_DEFAULT_SCREEN_DPI; - /********************** * MACROS **********************/ @@ -238,6 +240,72 @@ EXTERN_C void lv_win32_add_all_input_devices_to_group( lv_indev_set_group(lv_win32_encoder_device_object, group); } +EXTERN_C lv_win32_window_context_t* lv_win32_get_window_context( + HWND window_handle) +{ + return (lv_win32_window_context_t*)( + GetPropW(window_handle, L"LVGL.SimulatorWindow.WindowContext")); +} + +EXTERN_C bool lv_win32_init_window_class() +{ + WNDCLASSEXW window_class; + window_class.cbSize = sizeof(WNDCLASSEXW); + window_class.style = 0; + window_class.lpfnWndProc = lv_win32_window_message_callback; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = NULL; + window_class.hIcon = NULL; + window_class.hCursor = LoadCursorW(NULL, IDC_ARROW); + window_class.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + window_class.lpszMenuName = NULL; + window_class.lpszClassName = LVGL_SIMULATOR_WINDOW_CLASS; + window_class.hIconSm = NULL; + return RegisterClassExW(&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) +{ + HWND display_window_handle = CreateWindowExW( + WINDOW_EX_STYLE, + LVGL_SIMULATOR_WINDOW_CLASS, + window_title, + WINDOW_STYLE, + CW_USEDEFAULT, + 0, + hor_res, + ver_res, + NULL, + NULL, + instance_handle, + NULL); + if (display_window_handle) + { + SendMessageW( + display_window_handle, + WM_SETICON, + TRUE, + (LPARAM)icon_handle); + SendMessageW( + display_window_handle, + WM_SETICON, + FALSE, + (LPARAM)icon_handle); + + ShowWindow(display_window_handle, show_window_mode); + UpdateWindow(display_window_handle); + } + + return display_window_handle; +} + EXTERN_C bool lv_win32_init( HINSTANCE instance_handle, int show_window_mode, @@ -245,6 +313,11 @@ EXTERN_C bool lv_win32_init( lv_coord_t ver_res, HICON icon_handle) { + if (!lv_win32_init_window_class()) + { + return false; + } + PWINDOW_THREAD_PARAMETER parameter = (PWINDOW_THREAD_PARAMETER)malloc(sizeof(WINDOW_THREAD_PARAMETER)); parameter->window_mutex = CreateEventExW(NULL, NULL, 0, EVENT_ALL_ACCESS); @@ -264,53 +337,16 @@ EXTERN_C bool lv_win32_init( WaitForSingleObjectEx(parameter->window_mutex, INFINITE, FALSE); - static lv_disp_draw_buf_t display_buffer; -#if (LV_COLOR_DEPTH == 32) || \ - (LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP == 0) || \ - (LV_COLOR_DEPTH == 8) || \ - (LV_COLOR_DEPTH == 1) - lv_disp_draw_buf_init( - &display_buffer, - (lv_color_t*)g_pixel_buffer, - NULL, - hor_res * ver_res); -#else - lv_disp_draw_buf_init( - &display_buffer, - (lv_color_t*)malloc(hor_res * ver_res * sizeof(lv_color_t)), - NULL, - hor_res * ver_res); -#endif + lv_win32_window_context_t* context = (lv_win32_window_context_t*)( + lv_win32_get_window_context(g_window_handle)); + if (!context) + { + return false; + } - static lv_disp_drv_t display_driver; - lv_disp_drv_init(&display_driver); - display_driver.hor_res = hor_res; - display_driver.ver_res = ver_res; - display_driver.flush_cb = lv_win32_display_driver_flush_callback; - display_driver.draw_buf = &display_buffer; - display_driver.direct_mode = 1; - g_display = lv_disp_drv_register(&display_driver); - lv_timer_del(g_display->refr_timer); - g_display->refr_timer = NULL; - lv_timer_create(lv_win32_display_refresh_handler, 0, NULL); - - static lv_indev_drv_t pointer_driver; - lv_indev_drv_init(&pointer_driver); - pointer_driver.type = LV_INDEV_TYPE_POINTER; - pointer_driver.read_cb = lv_win32_pointer_driver_read_callback; - lv_win32_pointer_device_object = lv_indev_drv_register(&pointer_driver); - - static lv_indev_drv_t keypad_driver; - lv_indev_drv_init(&keypad_driver); - keypad_driver.type = LV_INDEV_TYPE_KEYPAD; - keypad_driver.read_cb = lv_win32_keypad_driver_read_callback; - lv_win32_keypad_device_object = lv_indev_drv_register(&keypad_driver); - - static lv_indev_drv_t encoder_driver; - lv_indev_drv_init(&encoder_driver); - encoder_driver.type = LV_INDEV_TYPE_ENCODER; - encoder_driver.read_cb = lv_win32_encoder_driver_read_callback; - lv_win32_encoder_device_object = lv_indev_drv_register(&encoder_driver); + lv_win32_pointer_device_object = context->mouse_device_object; + lv_win32_keypad_device_object = context->keyboard_device_object; + lv_win32_encoder_device_object = context->mousewheel_device_object; return true; } @@ -621,161 +657,119 @@ static void lv_win32_display_driver_flush_callback( const lv_area_t* area, lv_color_t* color_p) { - if (lv_disp_flush_is_last(disp_drv)) + lv_win32_window_context_t* context = (lv_win32_window_context_t*)( + lv_win32_get_window_context((HWND)disp_drv->user_data)); + if (context) { + if (lv_disp_flush_is_last(disp_drv)) + { #if (LV_COLOR_DEPTH == 32) || \ (LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP == 0) || \ (LV_COLOR_DEPTH == 8) || \ (LV_COLOR_DEPTH == 1) - UNREFERENCED_PARAMETER(color_p); + UNREFERENCED_PARAMETER(color_p); #elif (LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP != 0) - SIZE_T count = g_pixel_buffer_size / sizeof(UINT16); - PUINT16 source = (PUINT16)color_p; - PUINT16 destination = (PUINT16)g_pixel_buffer; - for (SIZE_T i = 0; i < count; ++i) - { - UINT16 current = *source; - *destination = (LOBYTE(current) << 8) | HIBYTE(current); + SIZE_T count = context->display_framebuffer_size / sizeof(UINT16); + PUINT16 source = (PUINT16)color_p; + PUINT16 destination = (PUINT16)context->display_framebuffer_base; + for (SIZE_T i = 0; i < count; ++i) + { + UINT16 current = *source; + *destination = (LOBYTE(current) << 8) | HIBYTE(current); - ++source; - ++destination; - } + ++source; + ++destination; + } #else - for (int y = area->y1; y <= area->y2; ++y) - { - for (int x = area->x1; x <= area->x2; ++x) + uint32_t* destination = context->display_framebuffer_base; + + for (int y = area->y1; y <= area->y2; ++y) { - g_pixel_buffer[y * disp_drv->hor_res + x] = - lv_color_to32(*color_p); - color_p++; + for (int x = area->x1; x <= area->x2; ++x) + { + destination[y * disp_drv->hor_res + x] = + lv_color_to32(*color_p); + color_p++; + } } - } #endif - InvalidateRect(g_window_handle, NULL, FALSE); + InvalidateRect(disp_drv->user_data, NULL, FALSE); + } } lv_disp_flush_ready(disp_drv); } -static void lv_win32_display_refresh_handler( - lv_timer_t* param) -{ - UNREFERENCED_PARAMETER(param); - - if (!g_display_refreshing) - { - _lv_disp_refr_timer(NULL); - } -} - static void lv_win32_pointer_driver_read_callback( lv_indev_drv_t* indev_drv, lv_indev_data_t* data) { - UNREFERENCED_PARAMETER(indev_drv); - - data->state = (lv_indev_state_t)( - g_mouse_pressed ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL); - - data->point.x = MulDiv( - GET_X_LPARAM(g_mouse_value), - USER_DEFAULT_SCREEN_DPI, - WIN32DRV_MONITOR_ZOOM * g_dpi_value); - data->point.y = MulDiv( - GET_Y_LPARAM(g_mouse_value), - USER_DEFAULT_SCREEN_DPI, - WIN32DRV_MONITOR_ZOOM * g_dpi_value); - - if (data->point.x < 0) - { - data->point.x = 0; - } - if (data->point.x > g_display->driver->hor_res - 1) - { - data->point.x = g_display->driver->hor_res - 1; - } - if (data->point.y < 0) - { - data->point.y = 0; - } - if (data->point.y > g_display->driver->ver_res - 1) + lv_win32_window_context_t* context = (lv_win32_window_context_t*)( + lv_win32_get_display_context(indev_drv->disp)); + if (!context) { - data->point.y = g_display->driver->ver_res - 1; + return; } + + data->state = context->mouse_state; + data->point = context->mouse_point; } static void lv_win32_keypad_driver_read_callback( lv_indev_drv_t* indev_drv, lv_indev_data_t* data) { - UNREFERENCED_PARAMETER(indev_drv); - - data->state = (lv_indev_state_t)( - g_keyboard_pressed ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL); + lv_win32_window_context_t* context = (lv_win32_window_context_t*)( + lv_win32_get_display_context(indev_drv->disp)); + if (!context) + { + return; + } - WPARAM KeyboardValue = g_keyboard_value; + EnterCriticalSection(&context->keyboard_mutex); - switch (KeyboardValue) + lv_win32_keyboard_queue_item_t* current = + (lv_win32_keyboard_queue_item_t*)(InterlockedPopEntrySList( + context->keyboard_queue)); + if (current) { - case VK_UP: - data->key = LV_KEY_UP; - break; - case VK_DOWN: - data->key = LV_KEY_DOWN; - break; - case VK_LEFT: - data->key = LV_KEY_LEFT; - break; - case VK_RIGHT: - data->key = LV_KEY_RIGHT; - break; - case VK_ESCAPE: - data->key = LV_KEY_ESC; - break; - case VK_DELETE: - data->key = LV_KEY_DEL; - break; - case VK_BACK: - data->key = LV_KEY_BACKSPACE; - break; - case VK_RETURN: - data->key = LV_KEY_ENTER; - break; - case VK_NEXT: - data->key = LV_KEY_NEXT; - break; - case VK_PRIOR: - data->key = LV_KEY_PREV; - break; - case VK_HOME: - data->key = LV_KEY_HOME; - break; - case VK_END: - data->key = LV_KEY_END; - break; - default: - if (KeyboardValue >= 'A' && KeyboardValue <= 'Z') - { - KeyboardValue += 0x20; - } + data->key = current->key; + data->state = current->state; - data->key = (uint32_t)KeyboardValue; + _aligned_free(current); - break; + data->continue_reading = true; } + + LeaveCriticalSection(&context->keyboard_mutex); } static void lv_win32_encoder_driver_read_callback( lv_indev_drv_t* indev_drv, lv_indev_data_t* data) { - UNREFERENCED_PARAMETER(indev_drv); + lv_win32_window_context_t* context = (lv_win32_window_context_t*)( + lv_win32_get_display_context(indev_drv->disp)); + if (!context) + { + return; + } + + data->state = context->mousewheel_state; + data->enc_diff = context->mousewheel_enc_diff; + context->mousewheel_enc_diff = 0; +} + +static lv_win32_window_context_t* lv_win32_get_display_context( + lv_disp_t* display) +{ + if (display) + { + return lv_win32_get_window_context((HWND)display->driver->user_data); + } - data->state = (lv_indev_state_t)( - g_mousewheel_pressed ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL); - data->enc_diff = g_mousewheel_value; - g_mousewheel_value = 0; + return NULL; } static LRESULT CALLBACK lv_win32_window_message_callback( @@ -786,155 +780,550 @@ static LRESULT CALLBACK lv_win32_window_message_callback( { switch (uMsg) { + case WM_CREATE: + { + // Note: Return -1 directly because WM_DESTROY message will be sent + // when destroy the window automatically. We free the resource when + // processing the WM_DESTROY message of this window. + + lv_win32_window_context_t* context = (lv_win32_window_context_t*)( + malloc(sizeof(lv_win32_window_context_t))); + if (!context) + { + return -1; + } + + RECT request_content_size; + GetWindowRect(hWnd, &request_content_size); + + context->display_hor_res = + request_content_size.right - request_content_size.left; + context->display_ver_res = + request_content_size.bottom - request_content_size.top; + context->display_dpi = lv_win32_get_dpi_for_window(hWnd); + context->display_framebuffer_context_handle = + lv_win32_create_frame_buffer( + hWnd, + context->display_hor_res, + context->display_ver_res, + &context->display_framebuffer_base, + &context->display_framebuffer_size); +#if (LV_COLOR_DEPTH == 32) || \ + (LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP == 0) || \ + (LV_COLOR_DEPTH == 8) || \ + (LV_COLOR_DEPTH == 1) + lv_disp_draw_buf_init( + &context->display_buffer, + (lv_color_t*)context->display_framebuffer_base, + NULL, + context->display_hor_res * context->display_ver_res); +#else + size_t draw_buffer_size = sizeof(lv_color_t); + draw_buffer_size *= context->display_hor_res; + draw_buffer_size *= context->display_ver_res; + lv_disp_draw_buf_init( + &context->display_buffer, + (lv_color_t*)malloc(draw_buffer_size), + NULL, + context->display_hor_res * context->display_ver_res); +#endif + lv_disp_drv_init(&context->display_driver); + context->display_driver.hor_res = context->display_hor_res; + context->display_driver.ver_res = context->display_ver_res; + context->display_driver.flush_cb = + lv_win32_display_driver_flush_callback; + context->display_driver.draw_buf = &context->display_buffer; + context->display_driver.direct_mode = 1; + context->display_driver.user_data = hWnd; + context->display_device_object = + lv_disp_drv_register(&context->display_driver); + if (!context->display_device_object) + { + return -1; + } + + context->mouse_state = LV_INDEV_STATE_REL; + context->mouse_point.x = 0; + context->mouse_point.y = 0; + lv_indev_drv_init(&context->mouse_driver); + context->mouse_driver.type = LV_INDEV_TYPE_POINTER; + context->mouse_driver.disp = context->display_device_object; + context->mouse_driver.read_cb = + lv_win32_pointer_driver_read_callback; + context->mouse_device_object = + lv_indev_drv_register(&context->mouse_driver); + if (!context->mouse_device_object) + { + return -1; + } + + context->mousewheel_state = LV_INDEV_STATE_REL; + context->mousewheel_enc_diff = 0; + lv_indev_drv_init(&context->mousewheel_driver); + context->mousewheel_driver.type = LV_INDEV_TYPE_ENCODER; + context->mousewheel_driver.disp = context->display_device_object; + context->mousewheel_driver.read_cb = + lv_win32_encoder_driver_read_callback; + context->mousewheel_device_object = + lv_indev_drv_register(&context->mousewheel_driver); + if (!context->mousewheel_device_object) + { + return -1; + } + + InitializeCriticalSection(&context->keyboard_mutex); + context->keyboard_queue = _aligned_malloc( + sizeof(SLIST_HEADER), + MEMORY_ALLOCATION_ALIGNMENT); + if (!context->keyboard_queue) + { + return -1; + } + InitializeSListHead(context->keyboard_queue); + context->keyboard_utf16_high_surrogate = 0; + context->keyboard_utf16_low_surrogate = 0; + lv_indev_drv_init(&context->keyboard_driver); + context->keyboard_driver.type = LV_INDEV_TYPE_KEYPAD; + context->keyboard_driver.disp = context->display_device_object; + context->keyboard_driver.read_cb = + lv_win32_keypad_driver_read_callback; + context->keyboard_device_object = + lv_indev_drv_register(&context->keyboard_driver); + if (!context->keyboard_device_object) + { + return -1; + } + + if (!SetPropW( + hWnd, + L"LVGL.SimulatorWindow.WindowContext", + (HANDLE)(context))) + { + return -1; + } + + RECT calculated_window_size; + + calculated_window_size.left = 0; + calculated_window_size.right = MulDiv( + context->display_hor_res * WIN32DRV_MONITOR_ZOOM, + context->display_dpi, + USER_DEFAULT_SCREEN_DPI); + calculated_window_size.top = 0; + calculated_window_size.bottom = MulDiv( + context->display_ver_res * WIN32DRV_MONITOR_ZOOM, + context->display_dpi, + USER_DEFAULT_SCREEN_DPI); + + AdjustWindowRectEx( + &calculated_window_size, + WINDOW_STYLE, + FALSE, + WINDOW_EX_STYLE); + OffsetRect( + &calculated_window_size, + -calculated_window_size.left, + -calculated_window_size.top); + + SetWindowPos( + hWnd, + NULL, + 0, + 0, + calculated_window_size.right, + calculated_window_size.bottom, + SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE); + + lv_win32_register_touch_window(hWnd, 0); + + lv_win32_enable_child_window_dpi_message(hWnd); + + break; + } case WM_MOUSEMOVE: case WM_LBUTTONDOWN: case WM_LBUTTONUP: case WM_MBUTTONDOWN: case WM_MBUTTONUP: { - g_mouse_value = lParam; + lv_win32_window_context_t* context = (lv_win32_window_context_t*)( + lv_win32_get_window_context(hWnd)); + if (!context) + { + return 0; + } + + context->mouse_point.x = MulDiv( + GET_X_LPARAM(lParam), + USER_DEFAULT_SCREEN_DPI, + WIN32DRV_MONITOR_ZOOM * context->display_dpi); + context->mouse_point.y = MulDiv( + GET_Y_LPARAM(lParam), + USER_DEFAULT_SCREEN_DPI, + WIN32DRV_MONITOR_ZOOM * context->display_dpi); + if (context->mouse_point.x < 0) + { + context->mouse_point.x = 0; + } + if (context->mouse_point.x > context->display_hor_res - 1) + { + context->mouse_point.x = context->display_hor_res - 1; + } + if (context->mouse_point.y < 0) + { + context->mouse_point.y = 0; + } + if (context->mouse_point.y > context->display_ver_res - 1) + { + context->mouse_point.y = context->display_ver_res - 1; + } + if (uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONUP) { - g_mouse_pressed = (uMsg == WM_LBUTTONDOWN); + context->mouse_state = ( + uMsg == WM_LBUTTONDOWN + ? LV_INDEV_STATE_PR + : LV_INDEV_STATE_REL); } else if (uMsg == WM_MBUTTONDOWN || uMsg == WM_MBUTTONUP) { - g_mousewheel_pressed = (uMsg == WM_MBUTTONDOWN); + context->mousewheel_state = ( + uMsg == WM_MBUTTONDOWN + ? LV_INDEV_STATE_PR + : LV_INDEV_STATE_REL); } return 0; } case WM_KEYDOWN: case WM_KEYUP: { - g_keyboard_pressed = (uMsg == WM_KEYDOWN); - g_keyboard_value = wParam; + lv_win32_window_context_t* context = (lv_win32_window_context_t*)( + lv_win32_get_window_context(hWnd)); + if (context) + { + EnterCriticalSection(&context->keyboard_mutex); + + bool skip_translation = false; + uint32_t translated_key = 0; + + switch (wParam) + { + case VK_UP: + translated_key = LV_KEY_UP; + break; + case VK_DOWN: + translated_key = LV_KEY_DOWN; + break; + case VK_LEFT: + translated_key = LV_KEY_LEFT; + break; + case VK_RIGHT: + translated_key = LV_KEY_RIGHT; + break; + case VK_ESCAPE: + translated_key = LV_KEY_ESC; + break; + case VK_DELETE: + translated_key = LV_KEY_DEL; + break; + case VK_BACK: + translated_key = LV_KEY_BACKSPACE; + break; + case VK_RETURN: + translated_key = LV_KEY_ENTER; + break; + case VK_TAB: + case VK_NEXT: + translated_key = LV_KEY_NEXT; + break; + case VK_PRIOR: + translated_key = LV_KEY_PREV; + break; + case VK_HOME: + translated_key = LV_KEY_HOME; + break; + case VK_END: + translated_key = LV_KEY_END; + break; + default: + skip_translation = true; + break; + } + + if (!skip_translation) + { + lv_win32_push_key_to_keyboard_queue( + context, + translated_key, + ((uMsg == WM_KEYUP) + ? LV_INDEV_STATE_REL + : LV_INDEV_STATE_PR)); + } + + LeaveCriticalSection(&context->keyboard_mutex); + } + + break; + } + case WM_CHAR: + { + lv_win32_window_context_t* context = (lv_win32_window_context_t*)( + lv_win32_get_window_context(hWnd)); + if (context) + { + EnterCriticalSection(&context->keyboard_mutex); + + uint16_t raw_code_point = (uint16_t)(wParam); + + if (raw_code_point >= 0x20 && raw_code_point != 0x7F) + { + if (IS_HIGH_SURROGATE(raw_code_point)) + { + context->keyboard_utf16_high_surrogate = raw_code_point; + } + else if (IS_LOW_SURROGATE(raw_code_point)) + { + context->keyboard_utf16_low_surrogate = raw_code_point; + } + + uint32_t code_point = raw_code_point; + + if (context->keyboard_utf16_high_surrogate && + context->keyboard_utf16_low_surrogate) + { + uint16_t high_surrogate = + context->keyboard_utf16_high_surrogate; + uint16_t low_surrogate = + context->keyboard_utf16_low_surrogate; + + code_point = (low_surrogate & 0x03FF); + code_point += (((high_surrogate & 0x03FF) + 0x40) << 10); + + context->keyboard_utf16_high_surrogate = 0; + context->keyboard_utf16_low_surrogate = 0; + } + + uint32_t lvgl_code_point = + _lv_txt_unicode_to_encoded(code_point); + + lv_win32_push_key_to_keyboard_queue( + context, + lvgl_code_point, + LV_INDEV_STATE_PR); + lv_win32_push_key_to_keyboard_queue( + context, + lvgl_code_point, + LV_INDEV_STATE_REL); + } + + LeaveCriticalSection(&context->keyboard_mutex); + } + break; } case WM_MOUSEWHEEL: { - g_mousewheel_value = -(GET_WHEEL_DELTA_WPARAM(wParam) / WHEEL_DELTA); + lv_win32_window_context_t* context = (lv_win32_window_context_t*)( + lv_win32_get_window_context(hWnd)); + if (context) + { + context->mousewheel_enc_diff = + -(GET_WHEEL_DELTA_WPARAM(wParam) / WHEEL_DELTA); + } + break; } case WM_TOUCH: { - UINT cInputs = LOWORD(wParam); - HTOUCHINPUT hTouchInput = (HTOUCHINPUT)(lParam); - - PTOUCHINPUT pInputs = malloc(cInputs * sizeof(TOUCHINPUT)); - if (pInputs) + lv_win32_window_context_t* context = (lv_win32_window_context_t*)( + lv_win32_get_window_context(hWnd)); + if (context) { - if (lv_win32_get_touch_input_info( - hTouchInput, - cInputs, - pInputs, - sizeof(TOUCHINPUT))) + UINT cInputs = LOWORD(wParam); + HTOUCHINPUT hTouchInput = (HTOUCHINPUT)(lParam); + + PTOUCHINPUT pInputs = malloc(cInputs * sizeof(TOUCHINPUT)); + if (pInputs) { - for (UINT i = 0; i < cInputs; ++i) + if (lv_win32_get_touch_input_info( + hTouchInput, + cInputs, + pInputs, + sizeof(TOUCHINPUT))) { - POINT Point; - Point.x = TOUCH_COORD_TO_PIXEL(pInputs[i].x); - Point.y = TOUCH_COORD_TO_PIXEL(pInputs[i].y); - if (!ScreenToClient(hWnd, &Point)) + for (UINT i = 0; i < cInputs; ++i) { - continue; + POINT Point; + Point.x = TOUCH_COORD_TO_PIXEL(pInputs[i].x); + Point.y = TOUCH_COORD_TO_PIXEL(pInputs[i].y); + if (!ScreenToClient(hWnd, &Point)) + { + continue; + } + + context->mouse_point.x = MulDiv( + Point.x, + USER_DEFAULT_SCREEN_DPI, + WIN32DRV_MONITOR_ZOOM * context->display_dpi); + context->mouse_point.y = MulDiv( + Point.y, + USER_DEFAULT_SCREEN_DPI, + WIN32DRV_MONITOR_ZOOM * context->display_dpi); + + DWORD MousePressedMask = + TOUCHEVENTF_MOVE | TOUCHEVENTF_DOWN; + + context->mouse_state = ( + pInputs[i].dwFlags & MousePressedMask + ? LV_INDEV_STATE_PR + : LV_INDEV_STATE_REL); } - - uint16_t x = (uint16_t)(Point.x & 0xffff); - uint16_t y = (uint16_t)(Point.y & 0xffff); - - DWORD MousePressedMask = - TOUCHEVENTF_MOVE | TOUCHEVENTF_DOWN; - - g_mouse_value = (y << 16) | x; - g_mouse_pressed = (pInputs[i].dwFlags & MousePressedMask); } + + free(pInputs); } - free(pInputs); + lv_win32_close_touch_input_handle(hTouchInput); } - lv_win32_close_touch_input_handle(hTouchInput); - break; } case WM_DPICHANGED: { - g_dpi_value = HIWORD(wParam); - - LPRECT SuggestedRect = (LPRECT)lParam; + lv_win32_window_context_t* context = (lv_win32_window_context_t*)( + lv_win32_get_window_context(hWnd)); + if (context) + { + context->display_dpi = HIWORD(wParam); - SetWindowPos( - hWnd, - NULL, - SuggestedRect->left, - SuggestedRect->top, - SuggestedRect->right, - SuggestedRect->bottom, - SWP_NOZORDER | SWP_NOACTIVATE); - - RECT ClientRect; - GetClientRect(hWnd, &ClientRect); - - int WindowWidth = MulDiv( - g_display->driver->hor_res * WIN32DRV_MONITOR_ZOOM, - g_dpi_value, - USER_DEFAULT_SCREEN_DPI); - int WindowHeight = MulDiv( - g_display->driver->ver_res * WIN32DRV_MONITOR_ZOOM, - g_dpi_value, - USER_DEFAULT_SCREEN_DPI); + LPRECT SuggestedRect = (LPRECT)lParam; - SetWindowPos( - hWnd, - NULL, - SuggestedRect->left, - SuggestedRect->top, - SuggestedRect->right + (WindowWidth - ClientRect.right), - SuggestedRect->bottom + (WindowHeight - ClientRect.bottom), - SWP_NOZORDER | SWP_NOACTIVATE); + SetWindowPos( + hWnd, + NULL, + SuggestedRect->left, + SuggestedRect->top, + SuggestedRect->right, + SuggestedRect->bottom, + SWP_NOZORDER | SWP_NOACTIVATE); + + RECT ClientRect; + GetClientRect(hWnd, &ClientRect); + + int WindowWidth = MulDiv( + context->display_hor_res * WIN32DRV_MONITOR_ZOOM, + context->display_dpi, + USER_DEFAULT_SCREEN_DPI); + int WindowHeight = MulDiv( + context->display_ver_res * WIN32DRV_MONITOR_ZOOM, + context->display_dpi, + USER_DEFAULT_SCREEN_DPI); + + SetWindowPos( + hWnd, + NULL, + SuggestedRect->left, + SuggestedRect->top, + SuggestedRect->right + (WindowWidth - ClientRect.right), + SuggestedRect->bottom + (WindowHeight - ClientRect.bottom), + SWP_NOZORDER | SWP_NOACTIVATE); + } break; } case WM_PAINT: { - g_display_refreshing = true; - PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); - if (g_display) + lv_win32_window_context_t* context = (lv_win32_window_context_t*)( + lv_win32_get_window_context(hWnd)); + if (context) { - SetStretchBltMode(hdc, HALFTONE); - - StretchBlt( - hdc, - ps.rcPaint.left, - ps.rcPaint.top, - ps.rcPaint.right - ps.rcPaint.left, - ps.rcPaint.bottom - ps.rcPaint.top, - g_buffer_dc_handle, - 0, - 0, - MulDiv( + if (context->display_framebuffer_context_handle) + { + SetStretchBltMode(hdc, HALFTONE); + + StretchBlt( + hdc, + ps.rcPaint.left, + ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left, - USER_DEFAULT_SCREEN_DPI, - WIN32DRV_MONITOR_ZOOM * g_dpi_value), - MulDiv( ps.rcPaint.bottom - ps.rcPaint.top, - USER_DEFAULT_SCREEN_DPI, - WIN32DRV_MONITOR_ZOOM * g_dpi_value), - SRCCOPY); + context->display_framebuffer_context_handle, + 0, + 0, + MulDiv( + ps.rcPaint.right - ps.rcPaint.left, + USER_DEFAULT_SCREEN_DPI, + WIN32DRV_MONITOR_ZOOM * context->display_dpi), + MulDiv( + ps.rcPaint.bottom - ps.rcPaint.top, + USER_DEFAULT_SCREEN_DPI, + WIN32DRV_MONITOR_ZOOM * context->display_dpi), + SRCCOPY); + } } EndPaint(hWnd, &ps); - g_display_refreshing = false; - break; } case WM_DESTROY: + { + lv_win32_window_context_t* context = (lv_win32_window_context_t*)( + RemovePropW(hWnd, L"LVGL.SimulatorWindow.WindowContext")); + if (context) + { + lv_disp_t* display_device_object = context->display_device_object; + context->display_device_object = NULL; + lv_disp_remove(display_device_object); +#if (LV_COLOR_DEPTH == 32) || \ + (LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP == 0) || \ + (LV_COLOR_DEPTH == 8) || \ + (LV_COLOR_DEPTH == 1) +#else + free(context->display_buffer.buf1); +#endif + DeleteDC(context->display_framebuffer_context_handle); + + lv_indev_t* mouse_device_object = + context->mouse_device_object; + context->mouse_device_object = NULL; + lv_indev_delete(mouse_device_object); + + lv_indev_t* mousewheel_device_object = + context->mousewheel_device_object; + context->mousewheel_device_object = NULL; + lv_indev_delete(mousewheel_device_object); + + lv_indev_t* keyboard_device_object = + context->keyboard_device_object; + context->keyboard_device_object = NULL; + lv_indev_delete(keyboard_device_object); + do + { + PSLIST_ENTRY current = InterlockedPopEntrySList( + context->keyboard_queue); + if (!current) + { + _aligned_free(context->keyboard_queue); + context->keyboard_queue = NULL; + break; + } + + _aligned_free(current); + + } while (true); + DeleteCriticalSection(&context->keyboard_mutex); + + free(context); + } + PostQuitMessage(0); + break; + } default: return DefWindowProcW(hWnd, uMsg, wParam, lParam); } @@ -948,95 +1337,18 @@ static unsigned int __stdcall lv_win32_window_thread_entrypoint( PWINDOW_THREAD_PARAMETER parameter = (PWINDOW_THREAD_PARAMETER)raw_parameter; - WNDCLASSEXW window_class; - window_class.cbSize = sizeof(WNDCLASSEXW); - window_class.style = 0; - window_class.lpfnWndProc = lv_win32_window_message_callback; - window_class.cbClsExtra = 0; - window_class.cbWndExtra = 0; - window_class.hInstance = parameter->instance_handle; - window_class.hIcon = parameter->icon_handle; - window_class.hCursor = LoadCursorW(NULL, IDC_ARROW); - window_class.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); - window_class.lpszMenuName = NULL; - window_class.lpszClassName = L"lv_sim_visual_studio"; - window_class.hIconSm = parameter->icon_handle; - if (!RegisterClassExW(&window_class)) - { - return 0; - } - - HWND window_handle = CreateWindowExW( - WINDOW_EX_STYLE, - window_class.lpszClassName, - L"LVGL Simulator for Windows Desktop", - WINDOW_STYLE, - CW_USEDEFAULT, - 0, - CW_USEDEFAULT, - 0, - NULL, - NULL, + g_window_handle = lv_win32_create_display_window( + L"LVGL Simulator for Windows Desktop (Display 1)", + parameter->hor_res, + parameter->ver_res, parameter->instance_handle, - NULL); - - if (!window_handle) + parameter->icon_handle, + parameter->show_window_mode); + if (!g_window_handle) { return 0; } - g_dpi_value = lv_win32_get_dpi_for_window(window_handle); - - RECT window_size; - - window_size.left = 0; - window_size.right = MulDiv( - parameter->hor_res * WIN32DRV_MONITOR_ZOOM, - g_dpi_value, - USER_DEFAULT_SCREEN_DPI); - window_size.top = 0; - window_size.bottom = MulDiv( - parameter->ver_res * WIN32DRV_MONITOR_ZOOM, - g_dpi_value, - USER_DEFAULT_SCREEN_DPI); - - AdjustWindowRectEx( - &window_size, - WINDOW_STYLE, - FALSE, - WINDOW_EX_STYLE); - OffsetRect( - &window_size, - -window_size.left, - -window_size.top); - - SetWindowPos( - window_handle, - NULL, - 0, - 0, - window_size.right, - window_size.bottom, - SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE); - - lv_win32_register_touch_window(window_handle, 0); - - lv_win32_enable_child_window_dpi_message(window_handle); - - HDC hNewBufferDC = lv_win32_create_frame_buffer( - window_handle, - parameter->hor_res, - parameter->ver_res, - &g_pixel_buffer, - &g_pixel_buffer_size); - - DeleteDC(g_buffer_dc_handle); - g_buffer_dc_handle = hNewBufferDC; - - ShowWindow(window_handle, parameter->show_window_mode); - UpdateWindow(window_handle); - g_window_handle = window_handle; - SetEvent(parameter->window_mutex); MSG message; diff --git a/lib/lv_drivers/win32drv/win32drv.h b/lib/lv_drivers/win32drv/win32drv.h index d20c664..5f8736f 100644 --- a/lib/lv_drivers/win32drv/win32drv.h +++ b/lib/lv_drivers/win32drv/win32drv.h @@ -20,7 +20,11 @@ #if USE_WIN32DRV -#include +#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 #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, diff --git a/lib/lv_drivers/x11/x11.c b/lib/lv_drivers/x11/x11.c new file mode 100644 index 0000000..952328b --- /dev/null +++ b/lib/lv_drivers/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/lib/lv_drivers/x11/x11.h b/lib/lv_drivers/x11/x11.h new file mode 100644 index 0000000..89d6475 --- /dev/null +++ b/lib/lv_drivers/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 */