From 23bad412998b3de458e6ff45c26a3d1184ba738c Mon Sep 17 00:00:00 2001 From: anaGrad <146175511+anaGrad@users.noreply.github.com> Date: Mon, 2 Jun 2025 17:29:38 +0300 Subject: [PATCH] feat(wayland): add dmabuf support with g2d (#8122) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Ana Grad Signed-off-by: Nicușor Cîțu Signed-off-by: Prabhu Sundararaj Signed-off-by: Cosmin Radu Co-authored-by: Nicușor Cîțu Co-authored-by: Prabhu Sundararaj Co-authored-by: Cosmin Radu --- Kconfig | 25 + lv_conf_template.h | 8 +- scripts/lv_conf_internal_gen.py | 6 + src/draw/nxp/g2d/lv_draw_buf_g2d.c | 1 - src/draw/nxp/g2d/lv_draw_g2d.c | 99 +- src/draw/nxp/g2d/lv_draw_g2d.h | 7 +- src/draw/nxp/g2d/lv_draw_g2d_fill.c | 37 +- src/draw/nxp/g2d/lv_draw_g2d_img.c | 26 +- src/draw/nxp/g2d/lv_g2d_buf_map.c | 14 +- src/draw/nxp/g2d/lv_g2d_utils.c | 13 +- src/draw/nxp/g2d/lv_g2d_utils.h | 7 +- src/drivers/wayland/lv_wayland.c | 3000 ++--------------- src/drivers/wayland/lv_wayland.h | 118 +- src/drivers/wayland/lv_wayland_private.h | 374 ++ src/drivers/wayland/lv_wayland_smm.c | 3 +- src/drivers/wayland/lv_wayland_smm.h | 2 +- src/drivers/wayland/lv_wl_cache.c | 133 + src/drivers/wayland/lv_wl_dmabuf.c | 360 ++ src/drivers/wayland/lv_wl_keyboard.c | 301 ++ src/drivers/wayland/lv_wl_keyboard.h | 52 + src/drivers/wayland/lv_wl_pointer.c | 245 ++ src/drivers/wayland/lv_wl_pointer.h | 54 + src/drivers/wayland/lv_wl_pointer_axis.c | 79 + src/drivers/wayland/lv_wl_pointer_axis.h | 54 + src/drivers/wayland/lv_wl_seat.c | 100 + src/drivers/wayland/lv_wl_shell.c | 184 + src/drivers/wayland/lv_wl_shm.c | 468 +++ src/drivers/wayland/lv_wl_touch.c | 275 ++ src/drivers/wayland/lv_wl_touch.h | 54 + src/drivers/wayland/lv_wl_window.c | 489 +++ src/drivers/wayland/lv_wl_window.h | 84 + .../wayland/lv_wl_window_decorations.c | 361 ++ src/drivers/wayland/lv_wl_xdg_shell.c | 467 +++ src/lv_conf_internal.h | 36 +- src/lv_conf_kconfig.h | 12 + tests/CMakeLists.txt | 1 + 36 files changed, 4609 insertions(+), 2940 deletions(-) create mode 100644 src/drivers/wayland/lv_wayland_private.h create mode 100644 src/drivers/wayland/lv_wl_cache.c create mode 100644 src/drivers/wayland/lv_wl_dmabuf.c create mode 100644 src/drivers/wayland/lv_wl_keyboard.c create mode 100644 src/drivers/wayland/lv_wl_keyboard.h create mode 100644 src/drivers/wayland/lv_wl_pointer.c create mode 100644 src/drivers/wayland/lv_wl_pointer.h create mode 100644 src/drivers/wayland/lv_wl_pointer_axis.c create mode 100644 src/drivers/wayland/lv_wl_pointer_axis.h create mode 100644 src/drivers/wayland/lv_wl_seat.c create mode 100644 src/drivers/wayland/lv_wl_shell.c create mode 100644 src/drivers/wayland/lv_wl_shm.c create mode 100644 src/drivers/wayland/lv_wl_touch.c create mode 100644 src/drivers/wayland/lv_wl_touch.h create mode 100644 src/drivers/wayland/lv_wl_window.c create mode 100644 src/drivers/wayland/lv_wl_window.h create mode 100644 src/drivers/wayland/lv_wl_window_decorations.c create mode 100644 src/drivers/wayland/lv_wl_xdg_shell.c diff --git a/Kconfig b/Kconfig index 3e39d428ef..3c660a878a 100644 --- a/Kconfig +++ b/Kconfig @@ -1853,6 +1853,31 @@ menu "LVGL configuration" bool "Support the legacy wl_shell instead of the default XDG Shell protocol" depends on LV_USE_WAYLAND default n + config LV_WAYLAND_BUF_COUNT + int "Use 1 for single buffer with partial render mode or 2 for double buffer with full render mode" + depends on LV_USE_WAYLAND + default 1 + choice + prompt "Wayland rendering mode" + depends on LV_USE_WAYLAND + default LV_WAYLAND_RENDER_MODE_PARTIAL + + config LV_WAYLAND_RENDER_MODE_PARTIAL + bool "Use the buffer(s) to render the screen is smaller parts" + depends on !LV_WAYLAND_USE_DMABUF + + config LV_WAYLAND_RENDER_MODE_DIRECT + bool "Only the changed areas will be updated with 2 screen sized buffers" + depends on LV_WAYLAND_USE_DMABUF + + config LV_WAYLAND_RENDER_MODE_FULL + bool "Always redraw the whole screen even if only one pixel has been changed with 2 screen sized buffers" + depends on LV_WAYLAND_USE_DMABUF + endchoice + + config LV_WAYLAND_USE_DMABUF + bool "Use DMA buffers for frame buffers" + depends on LV_USE_WAYLAND && LV_USE_DRAW_G2D config LV_USE_LINUX_FBDEV bool "Use Linux framebuffer device" diff --git a/lv_conf_template.h b/lv_conf_template.h index 006dfd0f2e..edf1da0884 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -1188,8 +1188,12 @@ /** Use Wayland to open a window and handle input on Linux or BSD desktops */ #define LV_USE_WAYLAND 0 #if LV_USE_WAYLAND - #define LV_WAYLAND_WINDOW_DECORATIONS 0 /**< Draw client side window decorations only necessary on Mutter/GNOME */ - #define LV_WAYLAND_WL_SHELL 0 /**< Use the legacy wl_shell protocol instead of the default XDG shell */ + #define LV_WAYLAND_BUF_COUNT 1 /**< Use 1 for single buffer with partial render mode or 2 for double buffer with full render mode*/ + #define LV_WAYLAND_USE_DMABUF 0 /**< Use DMA buffers for frame buffers. Requires LV_DRAW_USE_G2D */ + #define LV_WAYLAND_RENDER_MODE LV_DISPLAY_RENDER_MODE_PARTIAL /**< DMABUF supports LV_DISPLAY_RENDER_MODE_FULL and LV_DISPLAY_RENDER_MODE_DIRECT*/ + /**< When LV_WAYLAND_USE_DMABUF is disabled, only LV_DISPLAY_RENDER_MODE_PARTIAL is supported*/ + #define LV_WAYLAND_WINDOW_DECORATIONS 0 /**< Draw client side window decorations only necessary on Mutter/GNOME. Not supported using DMABUF*/ + #define LV_WAYLAND_WL_SHELL 0 /**< Use the legacy wl_shell protocol instead of the default XDG shell*/ #endif /** Driver for /dev/fb */ diff --git a/scripts/lv_conf_internal_gen.py b/scripts/lv_conf_internal_gen.py index fd42d11166..de7a032fbf 100755 --- a/scripts/lv_conf_internal_gen.py +++ b/scripts/lv_conf_internal_gen.py @@ -209,6 +209,12 @@ LV_EXPORT_CONST_INT(LV_DRAW_BUF_ALIGN); #define LV_LOG_TRACE_ANIM 0 #endif /*LV_USE_LOG*/ +#if LV_USE_WAYLAND == 0 + #define LV_WAYLAND_USE_DMABUF 0 + #define LV_WAYLAND_WINDOW_DECORATIONS 0 + #define LV_WAYLAND_WL_SHELL 0 +#endif /* LV_USE_WAYLAND */ + #if LV_USE_SYSMON == 0 #define LV_USE_PERF_MONITOR 0 #define LV_USE_MEM_MONITOR 0 diff --git a/src/draw/nxp/g2d/lv_draw_buf_g2d.c b/src/draw/nxp/g2d/lv_draw_buf_g2d.c index 99e6dd1900..8653ea5296 100644 --- a/src/draw/nxp/g2d/lv_draw_buf_g2d.c +++ b/src/draw/nxp/g2d/lv_draw_buf_g2d.c @@ -83,7 +83,6 @@ static void _buf_free(void * buf) static void _invalidate_cache(const lv_draw_buf_t * draw_buf, const lv_area_t * area) { - LV_UNUSED(area); struct g2d_buf * buf = g2d_search_buf_map(draw_buf->data); G2D_ASSERT_MSG(buf, "Failed to find buffer in map."); diff --git a/src/draw/nxp/g2d/lv_draw_g2d.c b/src/draw/nxp/g2d/lv_draw_g2d.c index 3d3de38e1c..42c6e40ec8 100644 --- a/src/draw/nxp/g2d/lv_draw_g2d.c +++ b/src/draw/nxp/g2d/lv_draw_g2d.c @@ -51,7 +51,7 @@ static int32_t _g2d_delete(lv_draw_unit_t * draw_unit); static void _g2d_render_thread_cb(void * ptr); #endif -static void _g2d_execute_drawing(lv_draw_g2d_unit_t * u); +static void _g2d_execute_drawing(lv_draw_task_t * t); /********************** * STATIC VARIABLES @@ -73,12 +73,17 @@ void lv_draw_g2d_init(void) draw_g2d_unit->base_unit.evaluate_cb = _g2d_evaluate; draw_g2d_unit->base_unit.dispatch_cb = _g2d_dispatch; draw_g2d_unit->base_unit.delete_cb = _g2d_delete; + draw_g2d_unit->base_unit.name = "G2D"; g2d_create_buf_map(); if(g2d_open(&draw_g2d_unit->g2d_handle)) { LV_LOG_ERROR("g2d_open fail.\n"); } #if LV_USE_G2D_DRAW_THREAD - lv_thread_init(&draw_g2d_unit->thread, "g2ddraw", LV_DRAW_THREAD_PRIO, _g2d_render_thread_cb, 2 * 1024, draw_g2d_unit); + lv_draw_sw_thread_dsc_t * thread_dsc = &draw_g2d_unit->thread_dsc; + thread_dsc->idx = 0; + thread_dsc->draw_unit = (void *) draw_g2d_unit; + lv_thread_init(&thread_dsc->thread, "g2ddraw", LV_DRAW_THREAD_PRIO, _g2d_render_thread_cb, LV_DRAW_THREAD_STACK_SIZE, + thread_dsc); #endif } @@ -97,6 +102,7 @@ static inline bool _g2d_dest_cf_supported(lv_color_format_t cf) switch(cf) { case LV_COLOR_FORMAT_ARGB8888: + case LV_COLOR_FORMAT_XRGB8888: is_cf_supported = true; break; default: @@ -106,14 +112,24 @@ static inline bool _g2d_dest_cf_supported(lv_color_format_t cf) return is_cf_supported; } -static inline bool _g2d_src_cf_supported(lv_color_format_t cf) +static inline bool _g2d_src_cf_supported(lv_draw_unit_t * drawunit, lv_color_format_t cf) { bool is_cf_supported = false; switch(cf) { case LV_COLOR_FORMAT_ARGB8888: + case LV_COLOR_FORMAT_XRGB8888: is_cf_supported = true; break; + case LV_COLOR_FORMAT_RGB565: { + int32_t hw_pxp = 0; + lv_draw_g2d_unit_t * u = (lv_draw_g2d_unit_t *)drawunit; + g2d_query_hardware(u->g2d_handle, G2D_HARDWARE_PXP, &hw_pxp); + if(!hw_pxp) { + is_cf_supported = true; + } + } + break; default: break; } @@ -123,7 +139,10 @@ static inline bool _g2d_src_cf_supported(lv_color_format_t cf) static bool _g2d_draw_img_supported(const lv_draw_image_dsc_t * draw_dsc) { - const lv_image_dsc_t * img_dsc = draw_dsc->src; + bool is_tiled = draw_dsc->tile; + /* Tiled image (repeat image) is currently not supported. */ + if(is_tiled) + return false; bool has_recolor = (draw_dsc->recolor_opa > LV_OPA_MIN); bool has_rotation = (draw_dsc->rotation != 0); @@ -165,7 +184,7 @@ static int32_t _g2d_evaluate(lv_draw_unit_t * u, lv_draw_task_t * t) case LV_DRAW_TASK_TYPE_IMAGE: { const lv_draw_image_dsc_t * draw_dsc = (lv_draw_image_dsc_t *) t->draw_dsc; - if(!_g2d_src_cf_supported(draw_dsc->header.cf)) + if(!_g2d_src_cf_supported(u, draw_dsc->header.cf)) return 0; if(!_g2d_draw_img_supported(draw_dsc)) @@ -189,9 +208,17 @@ static int32_t _g2d_dispatch(lv_draw_unit_t * draw_unit, lv_layer_t * layer) { lv_draw_g2d_unit_t * draw_g2d_unit = (lv_draw_g2d_unit_t *) draw_unit; +#if LV_USE_OS + lv_draw_sw_thread_dsc_t * thread_dsc = &draw_g2d_unit->thread_dsc; + + /* Return immediately if it's busy with draw task. */ + if(thread_dsc->task_act) + return 0; +#else /* Return immediately if it's busy with draw task. */ if(draw_g2d_unit->task_act) return 0; +#endif /* Try to get an ready to draw. */ lv_draw_task_t * t = lv_draw_get_available_task(layer, NULL, DRAW_UNIT_ID_G2D); @@ -203,14 +230,18 @@ static int32_t _g2d_dispatch(lv_draw_unit_t * draw_unit, lv_layer_t * layer) return LV_DRAW_UNIT_IDLE; t->state = LV_DRAW_TASK_STATE_IN_PROGRESS; - draw_g2d_unit->task_act = t; + t->draw_unit = draw_unit; #if LV_USE_G2D_DRAW_THREAD + thread_dsc->task_act = t; + /* Let the render thread work. */ - if(draw_g2d_unit->inited) - lv_thread_sync_signal(&draw_g2d_unit->sync); + if(thread_dsc->inited) + lv_thread_sync_signal(&thread_dsc->sync); #else - _g2d_execute_drawing(draw_g2d_unit); + draw_g2d_unit->task_act = t; + + _g2d_execute_drawing(t); draw_g2d_unit->task_act->state = LV_DRAW_TASK_STATE_READY; draw_g2d_unit->task_act = NULL; @@ -228,36 +259,27 @@ static int32_t _g2d_delete(lv_draw_unit_t * draw_unit) lv_result_t res = LV_RESULT_OK; #if LV_USE_G2D_DRAW_THREAD + lv_draw_sw_thread_dsc_t * thread_dsc = &draw_g2d_unit->thread_dsc; LV_LOG_INFO("Cancel G2D draw thread."); - draw_g2d_unit->exit_status = true; + thread_dsc->exit_status = true; - if(draw_g2d_unit->inited) - lv_thread_sync_signal(&draw_g2d_unit->sync); + if(thread_dsc->inited) + lv_thread_sync_signal(&thread_dsc->sync); - res = lv_thread_delete(&draw_g2d_unit->thread); + res = lv_thread_delete(&thread_dsc->thread); #endif g2d_close(draw_g2d_unit->g2d_handle); return res; } -static void _g2d_execute_drawing(lv_draw_g2d_unit_t * u) +static void _g2d_execute_drawing(lv_draw_task_t * t) { - lv_draw_task_t * t = u->task_act; lv_layer_t * layer = t->target_layer; lv_draw_buf_t * draw_buf = layer->draw_buf; - t->draw_unit = (lv_draw_unit_t *)u; - - lv_area_t draw_area; - if(!lv_area_intersect(&draw_area, &t->area, &t->clip_area)) - return; /*Fully clipped, nothing to do*/ - - /* Make area relative to the buffer */ - lv_area_move(&draw_area, -layer->buf_area.x1, -layer->buf_area.y1); - /* Invalidate only the drawing area */ - lv_draw_buf_invalidate_cache(draw_buf, &draw_area); + lv_draw_buf_invalidate_cache(draw_buf, NULL); switch(t->type) { case LV_DRAW_TASK_TYPE_FILL: @@ -269,47 +291,44 @@ static void _g2d_execute_drawing(lv_draw_g2d_unit_t * u) default: break; } - - /* Invalidate only the drawing area */ - lv_draw_buf_invalidate_cache(draw_buf, &draw_area); } #if LV_USE_G2D_DRAW_THREAD static void _g2d_render_thread_cb(void * ptr) { - lv_draw_g2d_unit_t * u = ptr; + lv_draw_sw_thread_dsc_t * thread_dsc = ptr; - lv_thread_sync_init(&u->sync); - u->inited = true; + lv_thread_sync_init(&thread_dsc->sync); + thread_dsc->inited = true; while(1) { /* Wait for sync if there is no task set. */ - while(u->task_act == NULL) { - if(u->exit_status) + while(thread_dsc->task_act == NULL) { + if(thread_dsc->exit_status) break; - lv_thread_sync_wait(&u->sync); + lv_thread_sync_wait(&thread_dsc->sync); } - if(u->exit_status) { + if(thread_dsc->exit_status) { LV_LOG_INFO("Ready to exit G2D draw thread."); break; } - _g2d_execute_drawing(u); + _g2d_execute_drawing(thread_dsc->task_act); /* Signal the ready state to dispatcher. */ - u->task_act->state = LV_DRAW_TASK_STATE_READY; + thread_dsc->task_act->state = LV_DRAW_TASK_STATE_READY; /* Cleanup. */ - u->task_act = NULL; + thread_dsc->task_act = NULL; /* The draw unit is free now. Request a new dispatching as it can get a new task. */ lv_draw_dispatch_request(); } - u->inited = false; - lv_thread_sync_delete(&u->sync); + thread_dsc->inited = false; + lv_thread_sync_delete(&thread_dsc->sync); LV_LOG_INFO("Exit G2D draw thread."); } #endif diff --git a/src/draw/nxp/g2d/lv_draw_g2d.h b/src/draw/nxp/g2d/lv_draw_g2d.h index 151a16d034..061aa3c316 100644 --- a/src/draw/nxp/g2d/lv_draw_g2d.h +++ b/src/draw/nxp/g2d/lv_draw_g2d.h @@ -34,7 +34,12 @@ extern "C" { **********************/ typedef struct lv_draw_g2d_unit { - lv_draw_sw_unit_t; + lv_draw_unit_t base_unit; +#if LV_USE_OS + lv_draw_sw_thread_dsc_t thread_dsc; +#else + lv_draw_task_t * task_act; +#endif void * g2d_handle; } lv_draw_g2d_unit_t; diff --git a/src/draw/nxp/g2d/lv_draw_g2d_fill.c b/src/draw/nxp/g2d/lv_draw_g2d_fill.c index a4e647c8e1..5863e4d6c6 100644 --- a/src/draw/nxp/g2d/lv_draw_g2d_fill.c +++ b/src/draw/nxp/g2d/lv_draw_g2d_fill.c @@ -35,12 +35,11 @@ **********************/ /* Blit simple w/ opa and alpha channel */ -static void _g2d_fill(void * g2d_handle, struct g2d_buf * dst_buf, struct g2d_surface * dst_surf); -static void _g2d_fill_with_opa(void * g2d_handle, struct g2d_buf * dst_buf, struct g2d_surface * dst_surf, - struct g2d_buf * src_buf, struct g2d_surface * src_surf); +static void _g2d_fill(void * g2d_handle, struct g2d_surface * dst_surf); +static void _g2d_fill_with_opa(void * g2d_handle, struct g2d_surface * dst_surf, struct g2d_surface * src_surf); static void _g2d_set_src_surf(struct g2d_surface * src_surf, struct g2d_buf * buf, const lv_area_t * area, - int32_t stride, lv_color_t color, lv_opa_t opa); + lv_color_t color, lv_opa_t opa); static void _g2d_set_dst_surf(struct g2d_surface * dst_surf, struct g2d_buf * buf, const lv_area_t * area, int32_t stride, lv_color_t color); @@ -88,26 +87,21 @@ void lv_draw_g2d_fill(lv_draw_task_t * t) struct g2d_buf * dst_buf = g2d_search_buf_map(draw_buf->data); bool has_opa = (dsc->opa < (lv_opa_t)LV_OPA_MAX); - struct g2d_surface * dst_surf = lv_malloc(sizeof(struct g2d_surface)); - G2D_ASSERT_MSG(dst_surf, "Failed to alloc destination surface."); - _g2d_set_dst_surf(dst_surf, dst_buf, &blend_area, stride, dsc->color); + struct g2d_surface dst_surf; + _g2d_set_dst_surf(&dst_surf, dst_buf, &blend_area, stride, dsc->color); if(has_opa) { struct g2d_buf * tmp_buf = g2d_alloc(lv_area_get_width(&blend_area) * lv_area_get_height(&blend_area) * sizeof( lv_color32_t), 1); G2D_ASSERT_MSG(tmp_buf, "Failed to alloc temporary buffer."); - struct g2d_surface * src_surf = lv_malloc(sizeof(struct g2d_surface)); - G2D_ASSERT_MSG(src_surf, "Failed to alloc source surface."); - _g2d_set_src_surf(src_surf, tmp_buf, &blend_area, stride, dsc->color, dsc->opa); - _g2d_fill_with_opa(u->g2d_handle, dst_buf, dst_surf, tmp_buf, src_surf); + struct g2d_surface src_surf; + _g2d_set_src_surf(&src_surf, tmp_buf, &blend_area, dsc->color, dsc->opa); + _g2d_fill_with_opa(u->g2d_handle, &dst_surf, &src_surf); g2d_free(tmp_buf); - lv_free(src_surf); } else { - _g2d_fill(u->g2d_handle, dst_buf, dst_surf); + _g2d_fill(u->g2d_handle, &dst_surf); } - - lv_free(dst_surf); } /********************** @@ -115,7 +109,7 @@ void lv_draw_g2d_fill(lv_draw_task_t * t) **********************/ static void _g2d_set_src_surf(struct g2d_surface * src_surf, struct g2d_buf * buf, const lv_area_t * area, - int32_t stride, lv_color_t color, lv_opa_t opa) + lv_color_t color, lv_opa_t opa) { int32_t width = lv_area_get_width(area); int32_t height = lv_area_get_height(area); @@ -126,7 +120,7 @@ static void _g2d_set_src_surf(struct g2d_surface * src_surf, struct g2d_buf * bu src_surf->top = 0; src_surf->right = width; src_surf->bottom = height; - src_surf->stride = stride; + src_surf->stride = width; src_surf->width = width; src_surf->height = height; @@ -162,13 +156,9 @@ static void _g2d_set_dst_surf(struct g2d_surface * dst_surf, struct g2d_buf * bu dst_surf->blendfunc = G2D_ONE_MINUS_SRC_ALPHA | G2D_PRE_MULTIPLIED_ALPHA; } -static void _g2d_fill_with_opa(void * g2d_handle, struct g2d_buf * dst_buf, struct g2d_surface * dst_surf, - struct g2d_buf * src_buf, struct g2d_surface * src_surf) +static void _g2d_fill_with_opa(void * g2d_handle, struct g2d_surface * dst_surf, struct g2d_surface * src_surf) { - g2d_cache_op(src_buf, G2D_CACHE_FLUSH); - g2d_clear(g2d_handle, src_surf); - g2d_flush(g2d_handle); g2d_enable(g2d_handle, G2D_BLEND); g2d_enable(g2d_handle, G2D_GLOBAL_ALPHA); @@ -178,10 +168,9 @@ static void _g2d_fill_with_opa(void * g2d_handle, struct g2d_buf * dst_buf, stru g2d_disable(g2d_handle, G2D_BLEND); } -static void _g2d_fill(void * g2d_handle, struct g2d_buf * dst_buf, struct g2d_surface * dst_surf) +static void _g2d_fill(void * g2d_handle, struct g2d_surface * dst_surf) { g2d_clear(g2d_handle, dst_surf); - g2d_flush(g2d_handle); g2d_finish(g2d_handle); } diff --git a/src/draw/nxp/g2d/lv_draw_g2d_img.c b/src/draw/nxp/g2d/lv_draw_g2d_img.c index b1d0df459d..3b70934849 100644 --- a/src/draw/nxp/g2d/lv_draw_g2d_img.c +++ b/src/draw/nxp/g2d/lv_draw_g2d_img.c @@ -44,8 +44,7 @@ static void _g2d_set_dst_surf(struct g2d_surface * dst_surf, struct g2d_buf * bu int32_t stride, lv_color_format_t cf, const lv_draw_image_dsc_t * dsc); /* Blit simple w/ opa and alpha channel */ -static void _g2d_blit(void * g2d_handle, struct g2d_buf * dst_buf, struct g2d_surface * dst_surf, - struct g2d_buf * src_buf, struct g2d_surface * src_surf); +static void _g2d_blit(void * g2d_handle, struct g2d_surface * dst_surf, struct g2d_surface * src_surf); /********************** * STATIC VARIABLES @@ -105,18 +104,13 @@ void lv_draw_g2d_img(lv_draw_task_t * t) int32_t dest_stride = draw_buf->header.stride / (lv_color_format_get_bpp(draw_buf->header.cf) / 8); lv_color_format_t dest_cf = draw_buf->header.cf; - struct g2d_surface * src_surf = lv_malloc(sizeof(struct g2d_surface)); - G2D_ASSERT_MSG(src_surf, "Failed to alloc source surface."); - struct g2d_surface * dst_surf = lv_malloc(sizeof(struct g2d_surface)); - G2D_ASSERT_MSG(dst_surf, "Failed to alloc destination surface."); + struct g2d_surface src_surf; + struct g2d_surface dst_surf; - _g2d_set_src_surf(src_surf, src_buf, &src_area, src_stride, src_cf, dsc->opa); - _g2d_set_dst_surf(dst_surf, dst_buf, &blend_area, dest_stride, dest_cf, dsc); + _g2d_set_src_surf(&src_surf, src_buf, &src_area, src_stride, src_cf, dsc->opa); + _g2d_set_dst_surf(&dst_surf, dst_buf, &blend_area, dest_stride, dest_cf, dsc); - _g2d_blit(u->g2d_handle, dst_buf, dst_surf, src_buf, src_surf); - - lv_free(src_surf); - lv_free(dst_surf); + _g2d_blit(u->g2d_handle, &dst_surf, &src_surf); } /********************** @@ -130,7 +124,8 @@ static struct g2d_buf * _g2d_handle_src_buf(const lv_image_dsc_t * img_dsc) if(src_buf == NULL) { src_buf = g2d_alloc(img_dsc->data_size, 1); G2D_ASSERT_MSG(src_buf, "Failed to alloc source buffer."); - memcpy((int *)src_buf->buf_vaddr, img_dsc->data, img_dsc->data_size); + memcpy((uint8_t *)src_buf->buf_vaddr, img_dsc->data, img_dsc->data_size); + g2d_cache_op(src_buf, G2D_CACHE_FLUSH); g2d_insert_buf_map((void *)img_dsc->data, src_buf); } @@ -207,11 +202,8 @@ static void _g2d_set_dst_surf(struct g2d_surface * dst_surf, struct g2d_buf * bu dst_surf->blendfunc = G2D_ONE_MINUS_SRC_ALPHA | G2D_PRE_MULTIPLIED_ALPHA; } -static void _g2d_blit(void * g2d_handle, struct g2d_buf * dst_buf, struct g2d_surface * dst_surf, - struct g2d_buf * src_buf, struct g2d_surface * src_surf) +static void _g2d_blit(void * g2d_handle, struct g2d_surface * dst_surf, struct g2d_surface * src_surf) { - g2d_cache_op(src_buf, G2D_CACHE_FLUSH); - g2d_enable(g2d_handle, G2D_BLEND); g2d_enable(g2d_handle, G2D_GLOBAL_ALPHA); g2d_blit(g2d_handle, src_surf, dst_surf); diff --git a/src/draw/nxp/g2d/lv_g2d_buf_map.c b/src/draw/nxp/g2d/lv_g2d_buf_map.c index fd308cddba..00de516f6c 100644 --- a/src/draw/nxp/g2d/lv_g2d_buf_map.c +++ b/src/draw/nxp/g2d/lv_g2d_buf_map.c @@ -57,11 +57,6 @@ void g2d_create_buf_map(void) table->count = 0; table->items = (lv_map_item_t **) lv_malloc_zeroed(table->size * sizeof(lv_map_item_t *)); table->overflow_list = (lv_array_t **) lv_malloc_zeroed(table->size * sizeof(lv_array_t *)); - - for(int i = 0; i < table->size; i++) { - table->items[i] = NULL; - table->overflow_list[i] = NULL; - } } void g2d_free_buf_map(void) @@ -86,7 +81,6 @@ void g2d_insert_buf_map(void * key, struct g2d_buf * value) { lv_map_item_t * item = _map_create_item(key, value); int index = _map_hash_function(key); - lv_map_item_t * curr_item = table->items[index]; if(table->items[index] == NULL) { /* Key not found. */ @@ -174,11 +168,11 @@ void g2d_print_table(void) for(int i = 0; i < table->size; i++) { if(table->items[i]) { - LV_LOG("Index:%d, Key:%p, Value:%p\n", i, table->items[i] -> key, table->items[i]->value); + LV_LOG("Index:%d, Key:%p, Value:%p\n", i, table->items[i] -> key, (void *)table->items[i]->value); if(table->overflow_list[i]) { - for(int j = 0 ; j < lv_array_size(table->overflow_list[i]); j++) { + for(uint32_t j = 0 ; j < lv_array_size(table->overflow_list[i]); j++) { lv_map_item_t * item = (lv_map_item_t *)lv_array_at(table->overflow_list[i], j); - LV_LOG("Index:%d, Key:%p, Value:%p\n", i, item -> key, item->value); + LV_LOG("Index:%d, Key:%p, Value:%p\n", i, item -> key, (void *)item->value); } } @@ -217,7 +211,7 @@ static void _handle_collision(unsigned long index, lv_map_item_t * item) } else { lv_array_t * list = (lv_array_t *)table->overflow_list[index]; - for(int i = 0; i < lv_array_size(list); i++) { + for(uint32_t i = 0; i < lv_array_size(list); i++) { lv_map_item_t * it = (lv_map_item_t *)lv_array_at(list, i); if(it->key == item->key) { /* Key exists, update value. */ diff --git a/src/draw/nxp/g2d/lv_g2d_utils.c b/src/draw/nxp/g2d/lv_g2d_utils.c index 006a809587..c353040d23 100644 --- a/src/draw/nxp/g2d/lv_g2d_utils.c +++ b/src/draw/nxp/g2d/lv_g2d_utils.c @@ -16,7 +16,7 @@ #include "lv_g2d_utils.h" #if LV_USE_DRAW_G2D -#include"g2d.h" +#include "lv_g2d_buf_map.h" #include "lv_draw_g2d.h" /********************* @@ -39,9 +39,9 @@ * GLOBAL FUNCTIONS **********************/ -enum g2d_format g2d_get_buf_format(lv_color_format_t cf) +g2d_format_t g2d_get_buf_format(lv_color_format_t cf) { - enum g2d_format color_f = G2D_RGB565; + g2d_format_t color_f = G2D_RGB565; switch(cf) { case LV_COLOR_FORMAT_RGB565: @@ -83,6 +83,13 @@ uint32_t g2d_rgba_to_u32(lv_color_t color) return (uint32_t)((color.red) + (color.green << 8) + (color.blue << 16) + ((uint32_t)0xff << 24)); } +int32_t g2d_get_buf_fd(const lv_draw_buf_t * draw_buf) +{ + struct g2d_buf * buf = g2d_search_buf_map(draw_buf->data); + G2D_ASSERT_MSG(buf, "Failed to find buffer in map."); + return g2d_buf_export_fd(buf); +} + /********************** * STATIC FUNCTIONS **********************/ diff --git a/src/draw/nxp/g2d/lv_g2d_utils.h b/src/draw/nxp/g2d/lv_g2d_utils.h index f4bb172dfd..f3923e1503 100644 --- a/src/draw/nxp/g2d/lv_g2d_utils.h +++ b/src/draw/nxp/g2d/lv_g2d_utils.h @@ -23,6 +23,8 @@ extern "C" { #if LV_USE_DRAW_G2D #include "../../sw/lv_draw_sw_private.h" +#include "g2d.h" +#include "g2dExt.h" /********************* * DEFINES @@ -46,14 +48,17 @@ extern "C" { * TYPEDEFS **********************/ +typedef enum g2d_format g2d_format_t; + /********************** * GLOBAL PROTOTYPES **********************/ -enum g2d_format g2d_get_buf_format(lv_color_format_t cf); +g2d_format_t g2d_get_buf_format(lv_color_format_t cf); uint32_t g2d_rgba_to_u32(lv_color_t color); +int32_t g2d_get_buf_fd(const lv_draw_buf_t * draw_buf); /********************** * MACROS **********************/ diff --git a/src/drivers/wayland/lv_wayland.c b/src/drivers/wayland/lv_wayland.c index a04c768aaa..3f6bdfe2f5 100644 --- a/src/drivers/wayland/lv_wayland.c +++ b/src/drivers/wayland/lv_wayland.c @@ -1,28 +1,51 @@ -/******************************************************************* - * - * @file lv_wayland.c - The Wayland client for LVGL applications - * Based on the original file from the repository. - * - * Porting to LVGL 9.1 - * EDGEMTech Ltd. by Erik Tagirov (erik.tagirov@edgemtech.ch) - * - * See LICENCE.txt for details - * - ******************************************************************/ - -typedef int dummy_t; /* Make GCC on windows happy, avoid empty translation unit */ - -#ifndef _WIN32 +/** + * @file lv_wayland.c + */ /********************* * INCLUDES *********************/ + #include "lv_wayland.h" -#include "lv_wayland_smm.h" #if LV_USE_WAYLAND -#include +#if LV_WAYLAND_BUF_COUNT < 1 || LV_WAYLAND_BUF_COUNT > 2 + #error "Invalid LV_WAYLAND_BUF_COUNT. Expected either 1 or 2" +#endif + +#if !LV_WAYLAND_USE_DMABUF && LV_WAYLAND_BUF_COUNT != 1 + #error "Wayland doesn't support more than 1 LV_WAYLAND_BUF_COUNT without DMABUF" +#endif + +#if LV_WAYLAND_USE_DMABUF && !LV_USE_DRAW_G2D + #error "LV_WAYLAND_USE_DMABUF requires LV_USE_DRAW_G2D" +#endif + +#if LV_WAYLAND_USE_DMABUF && LV_WAYLAND_WINDOW_DECORATIONS + #error "LV_WAYLAND_USE_DMABUF doesn't support LV_WAYLAND_WINDOW_DECORATIONS" +#endif + +#ifndef LV_DISPLAY_RENDER_MODE_PARTIAL + /* FIXME: Hacky fix else building fails with -Wundef=error*/ + #define LV_DISPLAY_RENDER_MODE_PARTIAL 0 + #define LV_DISPLAY_RENDER_MODE_DIRECT 1 + #define LV_DISPLAY_RENDER_MODE_FULL 2 +#endif + +#if LV_WAYLAND_USE_DMABUF && LV_WAYLAND_RENDER_MODE == LV_DISPLAY_RENDER_MODE_PARTIAL + #error "LV_WAYLAND_USE_DMABUF doesn't support LV_DISPLAY_RENDER_MODE_PARTIAL" +#endif + +#if !LV_WAYLAND_USE_DMABUF && LV_WAYLAND_RENDER_MODE != LV_DISPLAY_RENDER_MODE_PARTIAL + #error "Wayland without DMABUF only supports LV_DISPLAY_RENDER_MODE_PARTIAL" +#endif + +#if (LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1) + #error[wayland] Unsupported LV_COLOR_DEPTH +#endif + +#include "lv_wayland_private.h" #include #include #include @@ -40,1215 +63,263 @@ typedef int dummy_t; /* Make GCC on windows happy, avoid empty translation un #include #include -#include "lvgl.h" - -#if !LV_WAYLAND_WL_SHELL - #include "wayland_xdg_shell.h" - #define LV_WAYLAND_XDG_SHELL 1 -#else - #define LV_WAYLAND_XDG_SHELL 0 +#if LV_WAYLAND_USE_DMABUF + #include #endif +#include "lvgl.h" /********************* * DEFINES *********************/ -#define LVGL_DRAW_BUFFER_DIV (8) -#define DMG_CACHE_CAPACITY (32) -#define TAG_LOCAL (0) -#define TAG_BUFFER_DAMAGE (1) - -#if LV_WAYLAND_WINDOW_DECORATIONS - #define TITLE_BAR_HEIGHT 24 - #define BORDER_SIZE 2 - #define BUTTON_MARGIN LV_MAX((TITLE_BAR_HEIGHT / 6), BORDER_SIZE) - #define BUTTON_PADDING LV_MAX((TITLE_BAR_HEIGHT / 8), BORDER_SIZE) - #define BUTTON_SIZE (TITLE_BAR_HEIGHT - (2 * BUTTON_MARGIN)) -#endif - -#ifndef LV_WAYLAND_CYCLE_PERIOD - #define LV_WAYLAND_CYCLE_PERIOD LV_MIN(LV_DEF_REFR_PERIOD,1) -#endif - -#define SHM_FORMAT_UNKNOWN 0xFFFFFF - -#if (LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1) - #error [wayland] Unsupported LV_COLOR_DEPTH -#endif /********************** * TYPEDEFS **********************/ -enum object_type { - OBJECT_TITLEBAR = 0, - OBJECT_BUTTON_CLOSE, -#if LV_WAYLAND_XDG_SHELL - OBJECT_BUTTON_MAXIMIZE, - OBJECT_BUTTON_MINIMIZE, -#endif - OBJECT_BORDER_TOP, - OBJECT_BORDER_BOTTOM, - OBJECT_BORDER_LEFT, - OBJECT_BORDER_RIGHT, - OBJECT_WINDOW, -}; +struct lv_wayland_context lv_wl_ctx; -#define FIRST_DECORATION (OBJECT_TITLEBAR) -#define LAST_DECORATION (OBJECT_BORDER_RIGHT) -#define NUM_DECORATIONS (LAST_DECORATION-FIRST_DECORATION+1) +/********************** + * STATIC PROTOTYPES + **********************/ - -struct window; -struct input { - struct { - uint32_t x; - uint32_t y; - lv_indev_state_t left_button; - lv_indev_state_t right_button; - lv_indev_state_t wheel_button; - int16_t wheel_diff; - } pointer; - - struct { - lv_key_t key; - lv_indev_state_t state; - } keyboard; - -#if LV_USE_GESTURE_RECOGNITION - lv_indev_touch_data_t touches[10]; - uint8_t touch_event_cnt; - uint8_t primary_id; -#endif -}; - -struct seat { - struct wl_touch * wl_touch; - struct wl_pointer * wl_pointer; - struct wl_keyboard * wl_keyboard; - - struct { - struct xkb_keymap * keymap; - struct xkb_state * state; - } xkb; -}; - -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 input input; -}; - -struct application { - struct wl_display * display; - struct wl_registry * registry; - struct wl_compositor * compositor; - struct wl_subcompositor * subcompositor; - struct wl_shm * shm; - struct wl_seat * wl_seat; - - struct wl_cursor_theme * cursor_theme; - struct wl_surface * cursor_surface; - -#if LV_WAYLAND_WL_SHELL - struct wl_shell * wl_shell; -#endif - -#if LV_WAYLAND_XDG_SHELL - struct xdg_wm_base * xdg_wm; -#endif - -#ifdef LV_WAYLAND_WINDOW_DECORATIONS - bool opt_disable_decorations; -#endif - - uint32_t shm_format; - - struct xkb_context * xkb_context; - - struct seat seat; - - struct graphic_object * touch_obj; - struct graphic_object * pointer_obj; - struct graphic_object * keyboard_obj; - - lv_ll_t window_ll; - lv_timer_t * cycle_timer; - - bool cursor_flush_pending; - struct pollfd wayland_pfd; -}; - -struct window { - lv_display_t * lv_disp; - lv_draw_buf_t * lv_disp_draw_buf; - - lv_indev_t * lv_indev_pointer; - lv_indev_t * lv_indev_pointeraxis; - lv_indev_t * lv_indev_touch; - lv_indev_t * lv_indev_keyboard; - - lv_wayland_display_close_f_t close_cb; - - struct application * application; - -#if LV_WAYLAND_WL_SHELL - struct wl_shell_surface * wl_shell_surface; -#endif - -#if LV_WAYLAND_XDG_SHELL - struct xdg_surface * xdg_surface; - struct xdg_toplevel * xdg_toplevel; - uint32_t wm_capabilities; -#endif - - 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_WINDOW_DECORATIONS - struct graphic_object * decoration[NUM_DECORATIONS]; -#endif - - int width; - int height; - - bool resize_pending; - int resize_width; - int resize_height; - - bool flush_pending; - bool shall_close; - bool closed; - bool maximized; - bool fullscreen; - uint32_t frame_counter; - bool frame_done; -}; - -/********************************* - * STATIC VARIABLES and FUNCTIONS - *********************************/ - -static struct application application; -static bool wayland_initialized = false; - -static void color_fill(void * pixels, lv_color_t color, uint32_t width, uint32_t height); -static void color_fill_XRGB8888(void * pixels, lv_color_t color, uint32_t width, uint32_t height); -static void color_fill_RGB565(void * pixels, lv_color_t color, uint32_t width, uint32_t height); - -static const struct wl_callback_listener wl_surface_frame_listener; -static bool resize_window(struct window * window, int width, int height); -static struct graphic_object * create_graphic_obj(struct application * app, struct window * window, - enum object_type type, - struct graphic_object * parent); +static void handle_global(void * data, struct wl_registry * registry, uint32_t name, const char * interface, + uint32_t version); +static void handle_global_remove(void * data, struct wl_registry * registry, uint32_t name); +static void handle_input(void); +static void handle_output(void); static uint32_t tick_get_cb(void); -static void wayland_init(void); -static void wayland_deinit(void); +/********************** + * STATIC VARIABLES + **********************/ + +static bool is_wayland_initialized = false; +static const struct wl_registry_listener registry_listener = {.global = handle_global, + .global_remove = handle_global_remove +}; + +/********************** + * GLOBAL FUNCTIONS + **********************/ /** - * The frame callback called when the compositor has finished rendering - * a frame.It increments the frame counter and sets up the callback - * for the next frame the frame counter is used to avoid needlessly - * committing frames too fast on a slow system - * - * NOTE: this function is invoked by the wayland-server library within the compositor - * the event is added to the queue, and then upon the next timer call it's - * called indirectly from _lv_wayland_handle_input (via wl_display_dispatch_queue) - * @param void data the user object defined that was tied to this event during - * the configuration of the callback - * @param struct wl_callback The callback that needs to be destroyed and re-created - * @param time Timestamp of the event (unused) + * Get Wayland display file descriptor + * @return Wayland display file descriptor */ -static void graphic_obj_frame_done(void * data, struct wl_callback * cb, uint32_t time) +int lv_wayland_get_fd(void) +{ + return wl_display_get_fd(lv_wl_ctx.display); +} + +uint32_t lv_wayland_timer_handler(void) { - struct graphic_object * obj; struct window * window; - LV_UNUSED(time); + /* Wayland input handling - it will also trigger the frame done handler */ + handle_input(); - wl_callback_destroy(cb); + /* Ready input timers (to probe for any input received) */ + LV_LL_READ(&lv_wl_ctx.window_ll, window) { + LV_LOG_TRACE("handle timer frame: %d", window->frame_counter); - obj = (struct graphic_object *)data; - window = obj->window; - window->frame_counter++; + if(window != NULL && window->resize_pending) { + if(lv_wayland_window_resize(window, window->resize_width, window->resize_height) == LV_RESULT_OK) { + window->resize_width = window->width; + window->resize_height = window->height; + window->resize_pending = false; - LV_LOG_TRACE("frame: %d done, new frame: %d", - window->frame_counter - 1, window->frame_counter); - - window->frame_done = true; - -} - -static const struct wl_callback_listener wl_surface_frame_listener = { - .done = graphic_obj_frame_done, -}; - -static inline bool _is_digit(char ch) -{ - return (ch >= '0') && (ch <= '9'); -} - -/* - * shm_format - * @description called by the compositor to advertise the supported - * color formats for SHM buffers, there is a call per supported format - */ -static void shm_format(void * data, struct wl_shm * wl_shm, uint32_t format) -{ - struct application * app = data; - - LV_UNUSED(wl_shm); - - LV_LOG_TRACE("Supported color space fourcc.h code: %08X", format); - - if(LV_COLOR_DEPTH == 32 && format == WL_SHM_FORMAT_ARGB8888) { - - /* Wayland compositors MUST support ARGB8888 */ - app->shm_format = format; - - } - else if(LV_COLOR_DEPTH == 32 && - format == WL_SHM_FORMAT_XRGB8888 && - app->shm_format != WL_SHM_FORMAT_ARGB8888) { - - /* Select XRGB only if the compositor doesn't support transprancy */ - app->shm_format = format; - - } - else if(LV_COLOR_DEPTH == 16 && format == WL_SHM_FORMAT_RGB565) { - - app->shm_format = format; - - } -} - -static const struct wl_shm_listener shm_listener = { - shm_format -}; - -static void pointer_handle_enter(void * data, struct wl_pointer * pointer, - uint32_t serial, struct wl_surface * surface, - wl_fixed_t sx, wl_fixed_t sy) -{ - struct application * app = data; - const char * cursor = "left_ptr"; - int pos_x = wl_fixed_to_int(sx); - int pos_y = wl_fixed_to_int(sy); - - if(!surface) { - app->pointer_obj = NULL; - return; - } - - app->pointer_obj = wl_surface_get_user_data(surface); - - app->pointer_obj->input.pointer.x = pos_x; - app->pointer_obj->input.pointer.y = pos_y; - -#if (LV_WAYLAND_WINDOW_DECORATIONS && LV_WAYLAND_XDG_SHELL) - if(!app->pointer_obj->window->xdg_toplevel || app->opt_disable_decorations) { - return; - } - - struct window * window = app->pointer_obj->window; - - switch(app->pointer_obj->type) { - case OBJECT_BORDER_TOP: - if(window->maximized) { - // do nothing - } - else if(pos_x < (BORDER_SIZE * 5)) { - cursor = "top_left_corner"; - } - else if(pos_x >= (window->width + BORDER_SIZE - (BORDER_SIZE * 5))) { - cursor = "top_right_corner"; } else { - cursor = "top_side"; - } - break; - case OBJECT_BORDER_BOTTOM: - if(window->maximized) { - // do nothing - } - else if(pos_x < (BORDER_SIZE * 5)) { - cursor = "bottom_left_corner"; - } - else if(pos_x >= (window->width + BORDER_SIZE - (BORDER_SIZE * 5))) { - cursor = "bottom_right_corner"; - } - else { - cursor = "bottom_side"; - } - break; - case OBJECT_BORDER_LEFT: - if(window->maximized) { - // do nothing - } - else if(pos_y < (BORDER_SIZE * 5)) { - cursor = "top_left_corner"; - } - else if(pos_y >= (window->height + BORDER_SIZE - (BORDER_SIZE * 5))) { - cursor = "bottom_left_corner"; - } - else { - cursor = "left_side"; - } - break; - case OBJECT_BORDER_RIGHT: - if(window->maximized) { - // do nothing - } - else if(pos_y < (BORDER_SIZE * 5)) { - cursor = "top_right_corner"; - } - else if(pos_y >= (window->height + BORDER_SIZE - (BORDER_SIZE * 5))) { - cursor = "bottom_right_corner"; - } - else { - cursor = "right_side"; - } - break; - default: - break; - } -#endif - if(app->cursor_surface) { - struct wl_cursor_image * cursor_image = wl_cursor_theme_get_cursor(app->cursor_theme, cursor)->images[0]; - wl_pointer_set_cursor(pointer, serial, app->cursor_surface, cursor_image->hotspot_x, cursor_image->hotspot_y); - wl_surface_attach(app->cursor_surface, wl_cursor_image_get_buffer(cursor_image), 0, 0); - wl_surface_damage(app->cursor_surface, 0, 0, cursor_image->width, cursor_image->height); - wl_surface_commit(app->cursor_surface); - app->cursor_flush_pending = true; - } -} - -static void pointer_handle_leave(void * data, struct wl_pointer * pointer, - uint32_t serial, struct wl_surface * surface) -{ - struct application * app = data; - - LV_UNUSED(pointer); - LV_UNUSED(serial); - - if(!surface || (app->pointer_obj == wl_surface_get_user_data(surface))) { - app->pointer_obj = NULL; - } -} - -static void pointer_handle_motion(void * data, struct wl_pointer * pointer, - uint32_t time, wl_fixed_t sx, wl_fixed_t sy) -{ - struct application * app = data; - - LV_UNUSED(pointer); - LV_UNUSED(time); - - if(!app->pointer_obj) { - return; - } - - app->pointer_obj->input.pointer.x = LV_MAX(0, LV_MIN(wl_fixed_to_int(sx), app->pointer_obj->width - 1)); - app->pointer_obj->input.pointer.y = LV_MAX(0, LV_MIN(wl_fixed_to_int(sy), app->pointer_obj->height - 1)); -} - -static void pointer_handle_button(void * data, struct wl_pointer * wl_pointer, - uint32_t serial, uint32_t time, uint32_t button, - uint32_t state) -{ - struct application * app = data; - - LV_UNUSED(serial); - LV_UNUSED(wl_pointer); - LV_UNUSED(time); - - const lv_indev_state_t lv_state = - (state == WL_POINTER_BUTTON_STATE_PRESSED) ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED; - - if(!app->pointer_obj) { - return; - } - - -#if LV_WAYLAND_WINDOW_DECORATIONS - struct window * window; - window = app->pointer_obj->window; - int pos_x = app->pointer_obj->input.pointer.x; - int pos_y = app->pointer_obj->input.pointer.y; -#endif - - switch(app->pointer_obj->type) { - case OBJECT_WINDOW: - switch(button) { - case BTN_LEFT: - app->pointer_obj->input.pointer.left_button = lv_state; - break; - case BTN_RIGHT: - app->pointer_obj->input.pointer.right_button = lv_state; - break; - case BTN_MIDDLE: - app->pointer_obj->input.pointer.wheel_button = lv_state; - break; - default: - break; + LV_LOG_TRACE("Failed to resize window frame: %d", window->frame_counter); } - - break; -#if LV_WAYLAND_WINDOW_DECORATIONS - case OBJECT_TITLEBAR: - if((button == BTN_LEFT) && (state == WL_POINTER_BUTTON_STATE_PRESSED)) { -#if LV_WAYLAND_XDG_SHELL - if(window->xdg_toplevel) { - xdg_toplevel_move(window->xdg_toplevel, app->wl_seat, serial); - window->flush_pending = true; - } -#endif -#if LV_WAYLAND_WL_SHELL - if(window->wl_shell_surface) { - wl_shell_surface_move(window->wl_shell_surface, app->wl_seat, serial); - window->flush_pending = true; - } -#endif - } - break; - case OBJECT_BUTTON_CLOSE: - if((button == BTN_LEFT) && (state == WL_POINTER_BUTTON_STATE_RELEASED)) { - window->shall_close = true; - } - break; -#if LV_WAYLAND_XDG_SHELL - case OBJECT_BUTTON_MAXIMIZE: - if((button == BTN_LEFT) && (state == WL_POINTER_BUTTON_STATE_RELEASED)) { - if(window->xdg_toplevel) { - if(window->maximized) { - xdg_toplevel_unset_maximized(window->xdg_toplevel); - } - else { - xdg_toplevel_set_maximized(window->xdg_toplevel); - } - window->maximized ^= true; - window->flush_pending = true; - } - } - break; - case OBJECT_BUTTON_MINIMIZE: - if((button == BTN_LEFT) && (state == WL_POINTER_BUTTON_STATE_RELEASED)) { - if(window->xdg_toplevel) { - xdg_toplevel_set_minimized(window->xdg_toplevel); - window->flush_pending = true; - } - } - break; - case OBJECT_BORDER_TOP: - if((button == BTN_LEFT) && (state == WL_POINTER_BUTTON_STATE_PRESSED)) { - if(window->xdg_toplevel && !window->maximized) { - uint32_t edge; - if(pos_x < (BORDER_SIZE * 5)) { - edge = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT; - } - else if(pos_x >= (window->width + BORDER_SIZE - (BORDER_SIZE * 5))) { - edge = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT; - } - else { - edge = XDG_TOPLEVEL_RESIZE_EDGE_TOP; - } - xdg_toplevel_resize(window->xdg_toplevel, - window->application->wl_seat, serial, edge); - window->flush_pending = true; - } - } - break; - case OBJECT_BORDER_BOTTOM: - if((button == BTN_LEFT) && (state == WL_POINTER_BUTTON_STATE_PRESSED)) { - if(window->xdg_toplevel && !window->maximized) { - uint32_t edge; - if(pos_x < (BORDER_SIZE * 5)) { - edge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT; - } - else if(pos_x >= (window->width + BORDER_SIZE - (BORDER_SIZE * 5))) { - edge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT; - } - else { - edge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM; - } - xdg_toplevel_resize(window->xdg_toplevel, - window->application->wl_seat, serial, edge); - window->flush_pending = true; - } - } - break; - case OBJECT_BORDER_LEFT: - if((button == BTN_LEFT) && (state == WL_POINTER_BUTTON_STATE_PRESSED)) { - if(window->xdg_toplevel && !window->maximized) { - uint32_t edge; - if(pos_y < (BORDER_SIZE * 5)) { - edge = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT; - } - else if(pos_y >= (window->height + BORDER_SIZE - (BORDER_SIZE * 5))) { - edge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT; - } - else { - edge = XDG_TOPLEVEL_RESIZE_EDGE_LEFT; - } - xdg_toplevel_resize(window->xdg_toplevel, - window->application->wl_seat, serial, edge); - window->flush_pending = true; - } - } - break; - case OBJECT_BORDER_RIGHT: - if((button == BTN_LEFT) && (state == WL_POINTER_BUTTON_STATE_PRESSED)) { - if(window->xdg_toplevel && !window->maximized) { - uint32_t edge; - if(pos_y < (BORDER_SIZE * 5)) { - edge = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT; - } - else if(pos_y >= (window->height + BORDER_SIZE - (BORDER_SIZE * 5))) { - edge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT; - } - else { - edge = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT; - } - xdg_toplevel_resize(window->xdg_toplevel, - window->application->wl_seat, serial, edge); - window->flush_pending = true; - } - } - break; -#endif // LV_WAYLAND_XDG_SHELL -#endif // LV_WAYLAND_WINDOW_DECORATIONS - default: - break; - } -} - -static void pointer_handle_axis(void * data, struct wl_pointer * wl_pointer, - uint32_t time, uint32_t axis, wl_fixed_t value) -{ - struct application * app = data; - const int diff = wl_fixed_to_int(value); - - LV_UNUSED(time); - LV_UNUSED(wl_pointer); - - if(!app->pointer_obj) { - return; - } - - if(axis == 0) { - if(diff > 0) { - app->pointer_obj->input.pointer.wheel_diff++; } - else if(diff < 0) { - app->pointer_obj->input.pointer.wheel_diff--; - } - } -} + else if(window->shall_close == true) { -static const struct wl_pointer_listener pointer_listener = { - .enter = pointer_handle_enter, - .leave = pointer_handle_leave, - .motion = pointer_handle_motion, - .button = pointer_handle_button, - .axis = pointer_handle_axis, -}; - -static lv_key_t keycode_xkb_to_lv(xkb_keysym_t xkb_key) -{ - lv_key_t key = 0; - - if(((xkb_key >= XKB_KEY_space) && (xkb_key <= XKB_KEY_asciitilde))) { - key = xkb_key; - } - else if(((xkb_key >= XKB_KEY_KP_0) && (xkb_key <= XKB_KEY_KP_9))) { - key = (xkb_key & 0x003f); - } - else { - switch(xkb_key) { - case XKB_KEY_BackSpace: - key = LV_KEY_BACKSPACE; - break; - case XKB_KEY_Return: - case XKB_KEY_KP_Enter: - key = LV_KEY_ENTER; - break; - case XKB_KEY_Escape: - key = LV_KEY_ESC; - break; - case XKB_KEY_Delete: - case XKB_KEY_KP_Delete: - key = LV_KEY_DEL; - break; - case XKB_KEY_Home: - case XKB_KEY_KP_Home: - key = LV_KEY_HOME; - break; - case XKB_KEY_Left: - case XKB_KEY_KP_Left: - key = LV_KEY_LEFT; - break; - case XKB_KEY_Up: - case XKB_KEY_KP_Up: - key = LV_KEY_UP; - break; - case XKB_KEY_Right: - case XKB_KEY_KP_Right: - key = LV_KEY_RIGHT; - break; - case XKB_KEY_Down: - case XKB_KEY_KP_Down: - key = LV_KEY_DOWN; - break; - case XKB_KEY_Prior: - case XKB_KEY_KP_Prior: - key = LV_KEY_PREV; - break; - case XKB_KEY_Next: - case XKB_KEY_KP_Next: - case XKB_KEY_Tab: - case XKB_KEY_KP_Tab: - key = LV_KEY_NEXT; - break; - case XKB_KEY_End: - case XKB_KEY_KP_End: - key = LV_KEY_END; - break; - default: - break; + /* Destroy graphical context and execute close_cb */ + handle_output(); + lv_wayland_deinit(); + return 0; } } - return key; -} + /* LVGL handling */ + uint32_t idle_time = lv_timer_handler(); -static void keyboard_handle_keymap(void * data, struct wl_keyboard * keyboard, - uint32_t format, int fd, uint32_t size) -{ - struct application * app = data; + /* Wayland output handling */ + handle_output(); - struct xkb_keymap * keymap; - struct xkb_state * state; - char * map_str; - - LV_UNUSED(keyboard); - - if(format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { - close(fd); - return; - } - - map_str = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); - if(map_str == MAP_FAILED) { - close(fd); - return; - } - - /* Set up XKB keymap */ - keymap = xkb_keymap_new_from_string(app->xkb_context, map_str, - XKB_KEYMAP_FORMAT_TEXT_V1, 0); - munmap(map_str, size); - close(fd); - - if(!keymap) { - LV_LOG_ERROR("failed to compile keymap"); - return; - } - - /* Set up XKB state */ - state = xkb_state_new(keymap); - if(!state) { - LV_LOG_ERROR("failed to create XKB state"); - xkb_keymap_unref(keymap); - return; - } - - xkb_keymap_unref(app->seat.xkb.keymap); - xkb_state_unref(app->seat.xkb.state); - app->seat.xkb.keymap = keymap; - app->seat.xkb.state = state; -} - -static void keyboard_handle_enter(void * data, struct wl_keyboard * keyboard, - uint32_t serial, struct wl_surface * surface, - struct wl_array * keys) -{ - struct application * app = data; - - LV_UNUSED(keyboard); - LV_UNUSED(serial); - LV_UNUSED(keys); - - if(!surface) { - app->keyboard_obj = NULL; - } - else { - app->keyboard_obj = wl_surface_get_user_data(surface); - } -} - -static void keyboard_handle_leave(void * data, struct wl_keyboard * keyboard, - uint32_t serial, struct wl_surface * surface) -{ - struct application * app = data; - - LV_UNUSED(serial); - LV_UNUSED(keyboard); - - if(!surface || (app->keyboard_obj == wl_surface_get_user_data(surface))) { - app->keyboard_obj = NULL; - } -} - -static void keyboard_handle_key(void * data, struct wl_keyboard * keyboard, - uint32_t serial, uint32_t time, uint32_t key, - uint32_t state) -{ - struct application * app = data; - const uint32_t code = (key + 8); - const xkb_keysym_t * syms; - xkb_keysym_t sym = XKB_KEY_NoSymbol; - - LV_UNUSED(serial); - LV_UNUSED(time); - LV_UNUSED(keyboard); - - if(!app->keyboard_obj || !app->seat.xkb.state) { - return; - } - - if(xkb_state_key_get_syms(app->seat.xkb.state, code, &syms) == 1) { - sym = syms[0]; - } - - const lv_key_t lv_key = keycode_xkb_to_lv(sym); - const lv_indev_state_t lv_state = - (state == WL_KEYBOARD_KEY_STATE_PRESSED) ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED; - - if(lv_key != 0) { - app->keyboard_obj->input.keyboard.key = lv_key; - app->keyboard_obj->input.keyboard.state = lv_state; - } -} - -static void keyboard_handle_modifiers(void * data, struct wl_keyboard * keyboard, - uint32_t serial, uint32_t mods_depressed, - uint32_t mods_latched, uint32_t mods_locked, - uint32_t group) -{ - struct application * app = data; - - LV_UNUSED(serial); - LV_UNUSED(keyboard); - - /* If we're not using a keymap, then we don't handle PC-style modifiers */ - if(!app->seat.xkb.keymap) { - return; - } - - xkb_state_update_mask(app->seat.xkb.state, - mods_depressed, mods_latched, mods_locked, 0, 0, group); -} - -static const struct wl_keyboard_listener keyboard_listener = { - .keymap = keyboard_handle_keymap, - .enter = keyboard_handle_enter, - .leave = keyboard_handle_leave, - .key = keyboard_handle_key, - .modifiers = keyboard_handle_modifiers, -}; - -#if LV_USE_GESTURE_RECOGNITION - -static void touch_handle_down(void * data, struct wl_touch * wl_touch, - uint32_t serial, uint32_t time, struct wl_surface * surface, - int32_t id, wl_fixed_t x_w, wl_fixed_t y_w) -{ - struct application * app = data; - uint8_t i; - - LV_UNUSED(id); - LV_UNUSED(time); - LV_UNUSED(serial); - LV_UNUSED(wl_touch); - - if(!surface) { - app->touch_obj = NULL; - return; - } - - /* Create the touch down event */ - app->touch_obj = wl_surface_get_user_data(surface); - i = app->touch_obj->input.touch_event_cnt; - - app->touch_obj->input.touches[i].point.x = wl_fixed_to_int(x_w); - app->touch_obj->input.touches[i].point.y = wl_fixed_to_int(y_w); - app->touch_obj->input.touches[i].id = id; - app->touch_obj->input.touches[i].timestamp = time; - app->touch_obj->input.touches[i].state = LV_INDEV_STATE_PRESSED; - app->touch_obj->input.touch_event_cnt++; - -#if LV_WAYLAND_WINDOW_DECORATIONS - struct window * window = app->touch_obj->window; - switch(app->touch_obj->type) { - case OBJECT_TITLEBAR: -#if LV_WAYLAND_XDG_SHELL - if(window->xdg_toplevel) { - xdg_toplevel_move(window->xdg_toplevel, app->wl_seat, serial); - window->flush_pending = true; - } -#endif -#if LV_WAYLAND_WL_SHELL - if(window->wl_shell_surface) { - wl_shell_surface_move(window->wl_shell_surface, app->wl_seat, serial); - window->flush_pending = true; - } -#endif + /* 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(&lv_wl_ctx.window_ll, window) { + if(window->flush_pending) { + errno = EAGAIN; break; - default: - break; - } -#endif -} - -static void touch_handle_up(void * data, struct wl_touch * wl_touch, - uint32_t serial, uint32_t time, int32_t id) -{ - struct application * app = data; - uint8_t i; - - LV_UNUSED(serial); - LV_UNUSED(time); - LV_UNUSED(id); - LV_UNUSED(wl_touch); - -#if LV_USE_GESTURE_RECOGNITION - /* Create a released event */ - i = app->touch_obj->input.touch_event_cnt; - - app->touch_obj->input.touches[i].point.x = 0; - app->touch_obj->input.touches[i].point.y = 0; - app->touch_obj->input.touches[i].id = id; - app->touch_obj->input.touches[i].timestamp = time; - app->touch_obj->input.touches[i].state = LV_INDEV_STATE_RELEASED; - - app->touch_obj->input.touch_event_cnt++; -#endif - -#if LV_WAYLAND_WINDOW_DECORATIONS - struct window * window = app->touch_obj->window; - switch(app->touch_obj->type) { - case OBJECT_BUTTON_CLOSE: - window->shall_close = true; - break; -#if LV_WAYLAND_XDG_SHELL - case OBJECT_BUTTON_MAXIMIZE: - if(window->xdg_toplevel) { - if(window->maximized) { - xdg_toplevel_unset_maximized(window->xdg_toplevel); - } - else { - xdg_toplevel_set_maximized(window->xdg_toplevel); - } - window->maximized ^= true; - } - break; - case OBJECT_BUTTON_MINIMIZE: - if(window->xdg_toplevel) { - xdg_toplevel_set_minimized(window->xdg_toplevel); - window->flush_pending = true; - } -#endif /* LV_WAYLAND_XDG_SHELL */ - default: - break; - } -#endif /* LV_WAYLAND_WINDOW_DECORATIONS */ - -} - -static void touch_handle_motion(void * data, struct wl_touch * wl_touch, - uint32_t time, int32_t id, wl_fixed_t x_w, wl_fixed_t y_w) -{ - struct application * app = data; - lv_indev_touch_data_t * touch; - lv_indev_touch_data_t * cur; - uint8_t i; - - LV_UNUSED(time); - LV_UNUSED(id); - LV_UNUSED(wl_touch); - - /* Update the contact point of the corresponding id with the latest coordinate */ - touch = &app->touch_obj->input.touches[0]; - cur = NULL; - - for(i = 0; i < app->touch_obj->input.touch_event_cnt; i++) { - if(touch->id == id) { - cur = touch; - } - touch++; - } - - if(cur == NULL) { - - i = app->touch_obj->input.touch_event_cnt; - app->touch_obj->input.touches[i].point.x = wl_fixed_to_int(x_w); - app->touch_obj->input.touches[i].point.y = wl_fixed_to_int(y_w); - app->touch_obj->input.touches[i].id = id; - app->touch_obj->input.touches[i].timestamp = time; - app->touch_obj->input.touches[i].state = LV_INDEV_STATE_PRESSED; - app->touch_obj->input.touch_event_cnt++; - - } - else { - - cur->point.x = wl_fixed_to_int(x_w); - cur->point.y = wl_fixed_to_int(y_w); - cur->id = id; - cur->timestamp = time; - } - -} - -static void touch_handle_frame(void * data, struct wl_touch * wl_touch) -{ - LV_UNUSED(wl_touch); - LV_UNUSED(data); - -} - -static void touch_handle_cancel(void * data, struct wl_touch * wl_touch) -{ - LV_UNUSED(wl_touch); - LV_UNUSED(data); -} - -static const struct wl_touch_listener touch_listener = { - .down = touch_handle_down, - .up = touch_handle_up, - .motion = touch_handle_motion, - .frame = touch_handle_frame, - .cancel = touch_handle_cancel, -}; - -#endif /* END LV_USE_GESTURE_RECOGNITION */ - -static void seat_handle_capabilities(void * data, struct wl_seat * wl_seat, enum wl_seat_capability caps) -{ - struct application * app = data; - struct seat * seat = &app->seat; - - if((caps & WL_SEAT_CAPABILITY_POINTER) && !seat->wl_pointer) { - seat->wl_pointer = wl_seat_get_pointer(wl_seat); - wl_pointer_add_listener(seat->wl_pointer, &pointer_listener, app); - app->cursor_surface = wl_compositor_create_surface(app->compositor); - if(!app->cursor_surface) { - LV_LOG_WARN("failed to create cursor surface"); } } - else if(!(caps & WL_SEAT_CAPABILITY_POINTER) && seat->wl_pointer) { - wl_pointer_destroy(seat->wl_pointer); - if(app->cursor_surface) { - wl_surface_destroy(app->cursor_surface); - } - seat->wl_pointer = NULL; - } - if((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !seat->wl_keyboard) { - seat->wl_keyboard = wl_seat_get_keyboard(wl_seat); - wl_keyboard_add_listener(seat->wl_keyboard, &keyboard_listener, app); - } - else if(!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && seat->wl_keyboard) { - wl_keyboard_destroy(seat->wl_keyboard); - seat->wl_keyboard = NULL; - } - -#if LV_USE_GESTURE_RECOGNITION - if((caps & WL_SEAT_CAPABILITY_TOUCH) && !seat->wl_touch) { - seat->wl_touch = wl_seat_get_touch(wl_seat); - wl_touch_add_listener(seat->wl_touch, &touch_listener, app); - } -#endif - else if(!(caps & WL_SEAT_CAPABILITY_TOUCH) && seat->wl_touch) { - wl_touch_destroy(seat->wl_touch); - seat->wl_touch = NULL; - } + return idle_time; } -static const struct wl_seat_listener seat_listener = { - .capabilities = seat_handle_capabilities, -}; +/********************** + * PRIVATE FUNCTIONS + **********************/ -static void draw_window(struct window * window, uint32_t width, uint32_t height) +void lv_wayland_init(void) { -#if LV_WAYLAND_WINDOW_DECORATIONS - if(application.opt_disable_decorations == false) { - int d; - for(d = 0; d < NUM_DECORATIONS; d++) { - window->decoration[d] = create_graphic_obj(&application, window, (FIRST_DECORATION + d), window->body); - if(!window->decoration[d]) { - LV_LOG_ERROR("Failed to create decoration %d", d); - } - } + if(is_wayland_initialized) { + return; + } + + // Create XKB context + lv_wl_ctx.xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + LV_ASSERT_MSG(lv_wl_ctx.xkb_context, "failed to create XKB context"); + if(lv_wl_ctx.xkb_context == NULL) { + return; + } + + // Connect to Wayland display + lv_wl_ctx.display = wl_display_connect(NULL); + LV_ASSERT_MSG(lv_wl_ctx.display, "failed to connect to Wayland server"); + if(lv_wl_ctx.display == NULL) { + return; + } + +#if LV_WAYLAND_USE_DMABUF + lv_wayland_dmabuf_initalize_context(&lv_wl_ctx.dmabuf_ctx); +#endif + lv_wayland_shm_initalize_context(&lv_wl_ctx.shm_ctx); + + /* Add registry listener and wait for registry reception */ + lv_wl_ctx.registry = wl_display_get_registry(lv_wl_ctx.display); + wl_registry_add_listener(lv_wl_ctx.registry, ®istry_listener, &lv_wl_ctx); + wl_display_dispatch(lv_wl_ctx.display); + wl_display_roundtrip(lv_wl_ctx.display); + + LV_ASSERT_MSG(lv_wl_ctx.compositor, "Wayland compositor not available"); + if(lv_wl_ctx.compositor == NULL) { + return; + } + + bool shm_ready = lv_wayland_shm_is_ready(&lv_wl_ctx.shm_ctx); + LV_ASSERT_MSG(shm_ready, "Couldn't initialize wayland SHM"); + if(!shm_ready) { + LV_LOG_ERROR("Couldn't initialize wayland SHM"); + return; + } + lv_wl_ctx.cursor_theme = lv_wayland_shm_load_cursor_theme(&lv_wl_ctx.shm_ctx); + if(!lv_wl_ctx.cursor_theme) { + LV_LOG_WARN("Failed to initialize the cursor theme"); + } + +#if LV_WAYLAND_USE_DMABUF + bool dmabuf_ready = lv_wayland_dmabuf_is_ready(&lv_wl_ctx.dmabuf_ctx); + LV_ASSERT_MSG(dmabuf_ready, "Couldn't initialize wayland DMABUF"); + if(!dmabuf_ready) { + LV_LOG_ERROR("Couldn't initialize wayland DMABUF"); + return; } #endif - /* First resize */ - if(!resize_window(window, width, height)) { - LV_LOG_ERROR("Failed to resize window"); -#if LV_WAYLAND_XDG_SHELL - if(window->xdg_toplevel) { - xdg_toplevel_destroy(window->xdg_toplevel); - } +#ifdef LV_WAYLAND_WINDOW_DECORATIONS + const char * env_disable_decorations = getenv("LV_WAYLAND_DISABLE_WINDOWDECORATION"); + lv_wl_ctx.opt_disable_decorations = ((env_disable_decorations != NULL) && (env_disable_decorations[0] != '0')); #endif - } - lv_refr_now(window->lv_disp); + lv_ll_init(&lv_wl_ctx.window_ll, sizeof(struct window)); + lv_tick_set_cb(tick_get_cb); + + /* Used to wait for events when the window is minimized or hidden */ + lv_wl_ctx.wayland_pfd.fd = wl_display_get_fd(lv_wl_ctx.display); + lv_wl_ctx.wayland_pfd.events = POLLIN; + + is_wayland_initialized = true; } +void lv_wayland_deinit(void) +{ + struct window * window = NULL; + + LV_LL_READ(&lv_wl_ctx.window_ll, window) { + if(!window->closed) { + lv_wayland_window_destroy(window); + } + + /* TODO: This should probably be moved inside lv_wayland_window_destroy but not sure about the if condition */ +#if LV_WAYLAND_USE_DMABUF + lv_wayland_dmabuf_destroy_draw_buffers(&lv_wl_ctx.dmabuf_ctx, window); +#else + lv_wayland_shm_delete_draw_buffers(&lv_wl_ctx.shm_ctx, window); +#endif + lv_display_delete(window->lv_disp); + } + + lv_wayland_shm_deinit(&lv_wl_ctx.shm_ctx); +#if LV_WAYLAND_USE_DMABUF + lv_wayland_dmabuf_deinit(&lv_wl_ctx.dmabuf_ctx); +#endif + #if LV_WAYLAND_WL_SHELL -static void wl_shell_handle_ping(void * data, struct wl_shell_surface * shell_surface, uint32_t serial) -{ - return wl_shell_surface_pong(shell_surface, serial); -} - -static void wl_shell_handle_configure(void * data, struct wl_shell_surface * shell_surface, - uint32_t edges, int32_t width, int32_t height) -{ - struct window * window = (struct window *)data; - - LV_UNUSED(edges); - - if((width <= 0) || (height <= 0)) { - return; - } - else if((width != window->width) || (height != window->height)) { - window->resize_width = width; - window->resize_height = height; - window->resize_pending = true; - } -} - -static const struct wl_shell_surface_listener shell_surface_listener = { - .ping = wl_shell_handle_ping, - .configure = wl_shell_handle_configure, -}; + lv_wayland_wl_shell_deinit(); +#elif LV_WAYLAND_XDG_SHELL + lv_wayland_xdg_shell_deinit(); #endif -#if LV_WAYLAND_XDG_SHELL -static void xdg_surface_handle_configure(void * data, struct xdg_surface * xdg_surface, uint32_t serial) -{ - struct window * window = (struct window *)data; - - xdg_surface_ack_configure(xdg_surface, serial); - - if(window->body->surface_configured == false) { - /* This branch is executed at launch */ - if(window->resize_pending == false) { - /* Use the size passed to the create_window function */ - draw_window(window, window->width, window->height); - } - else { - - /* Handle early maximization or fullscreen, */ - /* by using the size communicated by the compositor */ - /* when the initial xdg configure event arrives */ - draw_window(window, window->resize_width, window->resize_height); - window->width = window->resize_width; - window->height = window->resize_height; - window->resize_pending = false; - } + if(lv_wl_ctx.wl_seat) { + wl_seat_destroy(lv_wl_ctx.wl_seat); } - window->body->surface_configured = true; + + if(lv_wl_ctx.subcompositor) { + wl_subcompositor_destroy(lv_wl_ctx.subcompositor); + } + + if(lv_wl_ctx.compositor) { + wl_compositor_destroy(lv_wl_ctx.compositor); + } + + wl_registry_destroy(lv_wl_ctx.registry); + wl_display_flush(lv_wl_ctx.display); + wl_display_disconnect(lv_wl_ctx.display); + + lv_ll_clear(&lv_wl_ctx.window_ll); } -static const struct xdg_surface_listener xdg_surface_listener = { - .configure = xdg_surface_handle_configure, -}; - -static void xdg_toplevel_handle_configure(void * data, struct xdg_toplevel * xdg_toplevel, - int32_t width, int32_t height, struct wl_array * states) +void lv_wayland_wait_flush_cb(lv_display_t * disp) { - struct window * window = (struct window *)data; - - LV_UNUSED(xdg_toplevel); - LV_UNUSED(states); - LV_UNUSED(width); - LV_UNUSED(height); - - LV_LOG_TRACE("w:%d h:%d", width, height); - LV_LOG_TRACE("current body w:%d h:%d", window->body->width, window->body->height); - LV_LOG_TRACE("window w:%d h:%d", window->width, window->height); - - - if((width <= 0) || (height <= 0)) { - LV_LOG_TRACE("will not resize to w:%d h:%d", width, height); + struct window * window = lv_display_get_user_data(disp); + /* TODO: Figure out why we need this */ + if(window->frame_counter == 0) { return; } - - if((width != window->width) || (height != window->height)) { - window->resize_width = width; - window->resize_height = height; - window->resize_pending = true; - LV_LOG_TRACE("resize_pending is set, will resize to w:%d h:%d", width, height); - } - else { - LV_LOG_TRACE("resize_pending not set w:%d h:%d", width, height); + uint32_t initial_frame_counter = window->frame_counter; + while(initial_frame_counter == window->frame_counter) { + poll(&lv_wl_ctx.wayland_pfd, 1, -1); + handle_input(); } } -static void xdg_toplevel_handle_close(void * data, struct xdg_toplevel * xdg_toplevel) -{ - struct window * window = (struct window *)data; - window->shall_close = true; +/********************** + * STATIC FUNCTIONS + **********************/ - LV_UNUSED(xdg_toplevel); +static uint32_t tick_get_cb(void) +{ + struct timespec t; + clock_gettime(CLOCK_MONOTONIC, &t); + uint64_t time_ms = t.tv_sec * 1000 + (t.tv_nsec / 1000000); + return time_ms; } - -static const struct xdg_toplevel_listener xdg_toplevel_listener = { - .configure = xdg_toplevel_handle_configure, - .close = xdg_toplevel_handle_close, -}; - -static void xdg_wm_base_ping(void * data, struct xdg_wm_base * xdg_wm_base, uint32_t serial) +static void handle_global(void * data, struct wl_registry * registry, uint32_t name, const char * interface, + uint32_t version) { - LV_UNUSED(data); - - xdg_wm_base_pong(xdg_wm_base, serial); - - return; -} - -static const struct xdg_wm_base_listener xdg_wm_base_listener = { - .ping = xdg_wm_base_ping -}; -#endif - - -static void handle_global(void * data, struct wl_registry * registry, - uint32_t name, const char * interface, uint32_t version) -{ - struct application * app = data; + struct lv_wayland_context * app = data; LV_UNUSED(version); LV_UNUSED(data); @@ -1260,13 +331,13 @@ static void handle_global(void * data, struct wl_registry * registry, app->subcompositor = wl_registry_bind(registry, name, &wl_subcompositor_interface, 1); } else if(strcmp(interface, wl_shm_interface.name) == 0) { - app->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); - wl_shm_add_listener(app->shm, &shm_listener, app); - app->cursor_theme = wl_cursor_theme_load(NULL, 32, app->shm); + + lv_wayland_shm_set_interface(&app->shm_ctx, app->registry, name, interface, version); + } else if(strcmp(interface, wl_seat_interface.name) == 0) { app->wl_seat = wl_registry_bind(app->registry, name, &wl_seat_interface, 1); - wl_seat_add_listener(app->wl_seat, &seat_listener, app); + wl_seat_add_listener(app->wl_seat, lv_wayland_seat_get_listener(), app); } #if LV_WAYLAND_WL_SHELL else if(strcmp(interface, wl_shell_interface.name) == 0) { @@ -1277,7 +348,14 @@ static void handle_global(void * data, struct wl_registry * registry, else if(strcmp(interface, xdg_wm_base_interface.name) == 0) { /* supporting version 2 of the XDG protocol - ensures greater compatibility */ app->xdg_wm = wl_registry_bind(app->registry, name, &xdg_wm_base_interface, 2); - xdg_wm_base_add_listener(app->xdg_wm, &xdg_wm_base_listener, app); + xdg_wm_base_add_listener(app->xdg_wm, lv_wayland_xdg_shell_get_wm_base_listener(), app); + } +#endif +#if LV_WAYLAND_USE_DMABUF + else if(strcmp(interface, zwp_linux_dmabuf_v1_interface.name) == 0) { + lv_wayland_dmabuf_set_interface(&app->dmabuf_ctx, app->registry, name, interface, version); + + wl_display_roundtrip(app->display); } #endif } @@ -1288,992 +366,26 @@ static void handle_global_remove(void * data, struct wl_registry * registry, uin LV_UNUSED(data); LV_UNUSED(registry); LV_UNUSED(name); - } -static const struct wl_registry_listener registry_listener = { - .global = handle_global, - .global_remove = handle_global_remove -}; - -static void handle_wl_buffer_release(void * data, struct wl_buffer * wl_buffer) +static void handle_input(void) { - const struct smm_buffer_properties * props; - struct graphic_object * obj; - struct window * window; - smm_buffer_t * buf; - - buf = (smm_buffer_t *)data; - props = SMM_BUFFER_PROPERTIES(buf); - obj = SMM_GROUP_PROPERTIES(props->group)->tag[TAG_LOCAL]; - window = obj->window; - - LV_LOG_TRACE("releasing buffer %p wl_buffer %p w:%d h:%d frame: %d", (smm_buffer_t *)data, (void *)wl_buffer, - obj->width, - obj->height, window->frame_counter); - smm_release((smm_buffer_t *)data); -} - -static const struct wl_buffer_listener wl_buffer_listener = { - .release = handle_wl_buffer_release, -}; - -static void cache_clear(struct window * window) -{ - window->dmg_cache.start = window->dmg_cache.end; - window->dmg_cache.size = 0; -} - -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) { - cache_clear(window); - } - else { - 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--; - } - } -} - -static void cache_add_area(struct window * window, smm_buffer_t * buf, const lv_area_t * area) -{ - if(SMM_BUFFER_PROPERTIES(buf)->tag[TAG_BUFFER_DAMAGE] == NULL) { - /* Buffer damage beyond cache capacity */ - goto done; - } - - if((window->dmg_cache.start == window->dmg_cache.end) && - (window->dmg_cache.size)) { - /* 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; - } - - /* 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++; - -done: - return; -} - -static void cache_apply_areas(struct window * window, void * dest, void * src, smm_buffer_t * src_buf) -{ - unsigned long offset; - unsigned char start; - int32_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]; - uint8_t bpp; - - if(next_buf == NULL) { - next_dmg = (window->dmg_cache.cache + window->dmg_cache.end); - } - else { - next_dmg = SMM_BUFFER_PROPERTIES(next_buf)->tag[TAG_BUFFER_DAMAGE]; - } - - bpp = lv_color_format_get_size(LV_COLOR_FORMAT_NATIVE); - - /* 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) { - /* Copy an area from source to destination (line-by-line) */ - dmg = (window->dmg_cache.cache + start); - for(y = dmg->y1; y <= dmg->y2; y++) { - offset = (dmg->x1 + (y * obj->width)) * bpp; - - memcpy(((char *)dest) + offset, - ((char *)src) + offset, - ((dmg->x2 - dmg->x1 + 1) * bpp)); - } - - - start++; - start %= DMG_CACHE_CAPACITY; - } - -} - -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_UNUSED(ctx); - - wl_pool = wl_shm_create_pool(app->shm, - props->fd, - props->size); - - 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); - - LV_UNUSED(ctx); - - wl_shm_pool_resize(props->tag[TAG_LOCAL], props->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]; - - LV_UNUSED(ctx); - - wl_shm_pool_destroy(wl_pool); -} - -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]; - uint8_t bpp; - - LV_LOG_TRACE("create new buffer of width %d height %d", obj->width, obj->height); - - bpp = lv_color_format_get_size(LV_COLOR_FORMAT_NATIVE); - wl_buf = wl_shm_pool_create_buffer(wl_pool, - props->offset, - obj->width, - obj->height, - obj->width * bpp, - app->shm_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 sme_init_buffer(void * ctx, smm_buffer_t * buf) -{ - 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]; - uint8_t bpp; - - LV_UNUSED(ctx); - - if(buf_base == NULL) { - LV_LOG_ERROR("cannot map in buffer to initialize"); - goto done; - } - - /* Determine if all subsequent buffers damage is recorded */ - for(src = smm_next(buf); src != NULL; src = smm_next(src)) { - if(SMM_BUFFER_PROPERTIES(src)->tag[TAG_BUFFER_DAMAGE] == NULL) { - dmg_missing = true; - break; - } - } - - bpp = lv_color_format_get_size(LV_COLOR_FORMAT_NATIVE); - - 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 * bpp) * 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]; - - LV_UNUSED(ctx); - - wl_buffer_destroy(wl_buf); -} - -static struct graphic_object * create_graphic_obj(struct application * app, struct window * window, - enum object_type type, - struct graphic_object * parent) -{ - struct graphic_object * obj; - - LV_UNUSED(parent); - - obj = lv_malloc(sizeof(*obj)); - LV_ASSERT_MALLOC(obj); - if(!obj) { - goto err_out; - } - - lv_memset(obj, 0x00, sizeof(struct graphic_object)); - - obj->surface = wl_compositor_create_surface(app->compositor); - if(!obj->surface) { - LV_LOG_ERROR("cannot create surface for graphic object"); - 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; - -err_destroy_surface: - wl_surface_destroy(obj->surface); - -err_free: - lv_free(obj); - -err_out: - return NULL; -} - -static void destroy_graphic_obj(struct graphic_object * obj) -{ - if(obj->subsurface) { - wl_subsurface_destroy(obj->subsurface); - } - - wl_surface_destroy(obj->surface); - smm_destroy(obj->buffer_group); - lv_free(obj); -} - -#if LV_WAYLAND_WINDOW_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; - - 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; - } - - /* Enable this, to make it function on weston 10.0.2 */ - /* It's not elegant but it forces weston to size the surfaces before */ - /* the conversion to a subsurface takes place */ - - /* Likely related to this issue, some patches were merged into 10.0.0 */ - /* https://gitlab.freedesktop.org/wayland/weston/-/issues/446 */ - /* Moreover, it crashes on GNOME */ - -#if 0 - wl_surface_attach(decoration->surface, wl_buf, 0, 0); - wl_surface_commit(decoration->surface); -#endif - - if(decoration->subsurface == NULL) { - /* Create the subsurface only once */ - - 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_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; -} - -/* - * Fills a buffer with a color - * @description Used to draw the decorations, by writing directly to the SHM buffer, - * most wayland compositors support the ARGB8888, XRGB8888, RGB565 formats - * - * For color depths usually not natively supported by wayland i.e RGB332, Grayscale - * A conversion is performed to match the format of the SHM buffer read by the compositor. - * - * This function can also be used as a visual debugging aid to see how damage is applied - * - * @param pixels pointer to the buffer to fill - * @param lv_color_t color the color that will be used for the fill - * @param width width of the filled area - * @param height height of the filled area - * - */ -static void color_fill(void * pixels, lv_color_t color, uint32_t width, uint32_t height) -{ - - switch(application.shm_format) { - case WL_SHM_FORMAT_ARGB8888: - color_fill_XRGB8888(pixels, color, width, height); - break; - case WL_SHM_FORMAT_RGB565: - color_fill_RGB565(pixels, color, width, height); - break; - default: - LV_ASSERT_MSG(0, "Unsupported WL_SHM_FORMAT"); - break; - } -} - -static void color_fill_XRGB8888(void * pixels, lv_color_t color, uint32_t width, uint32_t height) -{ - unsigned char * buf = pixels; - unsigned char * buf_end; - - buf_end = (unsigned char *)((uint32_t *)buf + width * height); - - while(buf < buf_end) { - *(buf++) = color.blue; - *(buf++) = color.green; - *(buf++) = color.red; - *(buf++) = 0xFF; - } - -} - -static void color_fill_RGB565(void * pixels, lv_color_t color, uint32_t width, uint32_t height) -{ - uint16_t * buf = pixels; - uint16_t * buf_end; - - buf_end = (uint16_t *)buf + width * height; - - while(buf < buf_end) { - *(buf++) = lv_color_to_u16(color); - } -} - -static bool create_decoration(struct window * window, - struct graphic_object * decoration, - int window_width, int window_height) -{ - smm_buffer_t * buf; - void * buf_base; - int x, y; - lv_color_t * pixel; - uint8_t bpp; - - switch(decoration->type) { - case OBJECT_TITLEBAR: - decoration->width = window_width; - decoration->height = TITLE_BAR_HEIGHT; - break; - case OBJECT_BUTTON_CLOSE: - decoration->width = BUTTON_SIZE; - decoration->height = BUTTON_SIZE; - break; -#if LV_WAYLAND_XDG_SHELL - case OBJECT_BUTTON_MAXIMIZE: - decoration->width = BUTTON_SIZE; - decoration->height = BUTTON_SIZE; - break; - case OBJECT_BUTTON_MINIMIZE: - decoration->width = BUTTON_SIZE; - decoration->height = BUTTON_SIZE; - break; -#endif - case OBJECT_BORDER_TOP: - decoration->width = window_width + 2 * (BORDER_SIZE); - decoration->height = BORDER_SIZE; - break; - case OBJECT_BORDER_BOTTOM: - decoration->width = window_width + 2 * (BORDER_SIZE); - decoration->height = BORDER_SIZE; - break; - case OBJECT_BORDER_LEFT: - decoration->width = BORDER_SIZE; - decoration->height = window_height + TITLE_BAR_HEIGHT; - break; - case OBJECT_BORDER_RIGHT: - decoration->width = BORDER_SIZE; - decoration->height = window_height + TITLE_BAR_HEIGHT; - break; - default: - LV_ASSERT_MSG(0, "Invalid object type"); - return false; - } - - bpp = lv_color_format_get_size(LV_COLOR_FORMAT_NATIVE); - - LV_LOG_TRACE("decoration window %dx%d", decoration->width, decoration->height); - - smm_resize(decoration->buffer_group, - (decoration->width * bpp) * 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 map in allocated decoration buffer"); - smm_release(buf); - return false; - } - - switch(decoration->type) { - case OBJECT_TITLEBAR: - color_fill(buf_base, lv_color_make(0x66, 0x66, 0x66), decoration->width, decoration->height); - break; - case OBJECT_BUTTON_CLOSE: - color_fill(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++) { - pixel = (lv_color_t *)((unsigned char *)buf_base + (y * (decoration->width * bpp)) + x * bpp); - if((x >= BUTTON_PADDING) && (x < decoration->width - BUTTON_PADDING)) { - if((x == y) || (x == decoration->width - 1 - y)) { - color_fill(pixel, lv_color_make(0x33, 0x33, 0x33), 1, 1); - } - else if((x == y - 1) || (x == decoration->width - y)) { - color_fill(pixel, lv_color_make(0x66, 0x66, 0x66), 1, 1); - } - } - } - } - break; -#if LV_WAYLAND_XDG_SHELL - case OBJECT_BUTTON_MAXIMIZE: - color_fill(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++) { - pixel = (lv_color_t *)((unsigned char *)buf_base + (y * (decoration->width * bpp)) + x * bpp); - 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)) || - ((y == (BUTTON_PADDING + 1)) && (x >= BUTTON_PADDING) && (x < decoration->width - BUTTON_PADDING)) || - ((y == (decoration->height - BUTTON_PADDING)) && (x >= BUTTON_PADDING) && (x < decoration->width - BUTTON_PADDING))) { - color_fill(pixel, lv_color_make(0x33, 0x33, 0x33), 1, 1); - } - } - } - break; - case OBJECT_BUTTON_MINIMIZE: - color_fill(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++) { - pixel = (lv_color_t *)((unsigned char *)buf_base + (y * (decoration->width * bpp)) + x * bpp); - if((x >= BUTTON_PADDING) && (x < decoration->width - BUTTON_PADDING) && - (y > decoration->height - (2 * BUTTON_PADDING)) && (y < decoration->height - BUTTON_PADDING)) { - color_fill(pixel, lv_color_make(0x33, 0x33, 0x33), 1, 1); - } - } - } - break; -#endif - case OBJECT_BORDER_TOP: - /* fallthrough */ - case OBJECT_BORDER_BOTTOM: - /* fallthrough */ - case OBJECT_BORDER_LEFT: - /* fallthrough */ - case OBJECT_BORDER_RIGHT: - color_fill(buf_base, lv_color_make(0x66, 0x66, 0x66), decoration->width, decoration->height); - break; - default: - LV_ASSERT_MSG(0, "Invalid object type"); - return false; - } - - return attach_decoration(window, decoration, buf, window->body); -} - -static void detach_decoration(struct window * window, - struct graphic_object * decoration) -{ - - LV_UNUSED(window); - - if(decoration->subsurface) { - wl_subsurface_destroy(decoration->subsurface); - decoration->subsurface = NULL; - } -} -#endif - -static bool resize_window(struct window * window, int width, int height) -{ - struct smm_buffer_t * body_buf1; - struct smm_buffer_t * body_buf2; - uint32_t stride; - uint8_t bpp; -#if LV_WAYLAND_WINDOW_DECORATIONS - int b; -#endif - - - window->width = width; - window->height = height; - -#if LV_WAYLAND_WINDOW_DECORATIONS - if(!window->application->opt_disable_decorations && !window->fullscreen) { - width -= (2 * BORDER_SIZE); - height -= (TITLE_BAR_HEIGHT + (2 * BORDER_SIZE)); - } -#endif - - bpp = lv_color_format_get_size(LV_COLOR_FORMAT_NATIVE); - - /* Update size for newly allocated buffers */ - smm_resize(window->body->buffer_group, ((width * bpp) * height) * 2); - - window->body->width = width; - window->body->height = height; - - /* Pre-allocate two buffers for the window body here */ - body_buf1 = smm_acquire(window->body->buffer_group); - body_buf2 = smm_acquire(window->body->buffer_group); - - if(smm_map(body_buf2) == NULL) { - LV_LOG_ERROR("Cannot pre-allocate backing buffers for window body"); - wl_surface_destroy(window->body->surface); - return false; - } - - /* Moves the buffers to the the unused list of the group */ - smm_release(body_buf1); - smm_release(body_buf2); - - -#if LV_WAYLAND_WINDOW_DECORATIONS - if(!window->application->opt_disable_decorations && !window->fullscreen) { - for(b = 0; b < NUM_DECORATIONS; b++) { - if(!create_decoration(window, window->decoration[b], - window->body->width, window->body->height)) { - LV_LOG_ERROR("failed to create decoration %d", b); - } - } - - } - else if(!window->application->opt_disable_decorations) { - /* Entering fullscreen, detach decorations to prevent xdg_wm_base error 4 */ - /* requested geometry larger than the configured fullscreen state */ - for(b = 0; b < NUM_DECORATIONS; b++) { - detach_decoration(window, window->decoration[b]); - } - - } -#endif - - LV_LOG_TRACE("resize window:%dx%d body:%dx%d frame: %d rendered: %d", - window->width, window->height, - window->body->width, window->body->height, - window->frame_counter, window->frame_done); - - width = window->body->width; - height = window->body->height; - - if(window->lv_disp != NULL) { - /* Resize draw buffer */ - stride = lv_draw_buf_width_to_stride(width, - lv_display_get_color_format(window->lv_disp)); - - window->lv_disp_draw_buf = lv_draw_buf_reshape( - window->lv_disp_draw_buf, - lv_display_get_color_format(window->lv_disp), - width, height / LVGL_DRAW_BUFFER_DIV, stride); - - lv_display_set_resolution(window->lv_disp, width, height); - - window->body->input.pointer.x = LV_MIN((int32_t)window->body->input.pointer.x, (width - 1)); - window->body->input.pointer.y = LV_MIN((int32_t)window->body->input.pointer.y, (height - 1)); - } - - return true; -} - -/* Create a window - * @description Creates the graphical context for the window body, and then create a toplevel - * wayland surface and commit it to obtain an XDG configuration event - * @param width the height of the window w/decorations - * @param height the width of the window w/decorations -*/ -static struct window * create_window(struct application * app, int width, int height, const char * title) -{ - struct window * window; - - window = lv_ll_ins_tail(&app->window_ll); - LV_ASSERT_MALLOC(window); - if(!window) { - return NULL; - } - - lv_memset(window, 0x00, sizeof(struct window)); - - window->application = app; - - // Create wayland buffer and surface - window->body = create_graphic_obj(app, window, OBJECT_WINDOW, NULL); - window->width = width; - window->height = height; - - if(!window->body) { - LV_LOG_ERROR("cannot create window body"); - goto err_free_window; - } - - // Create shell surface - if(0) { - // Needed for #if madness below - } -#if LV_WAYLAND_XDG_SHELL - else if(app->xdg_wm) { - window->xdg_surface = xdg_wm_base_get_xdg_surface(app->xdg_wm, window->body->surface); - if(!window->xdg_surface) { - LV_LOG_ERROR("cannot create XDG surface"); - goto err_destroy_surface; - } - - xdg_surface_add_listener(window->xdg_surface, &xdg_surface_listener, window); - - window->xdg_toplevel = xdg_surface_get_toplevel(window->xdg_surface); - if(!window->xdg_toplevel) { - LV_LOG_ERROR("cannot get XDG toplevel surface"); - goto err_destroy_shell_surface; - } - - xdg_toplevel_add_listener(window->xdg_toplevel, &xdg_toplevel_listener, window); - xdg_toplevel_set_title(window->xdg_toplevel, title); - xdg_toplevel_set_app_id(window->xdg_toplevel, title); - - // XDG surfaces need to be configured before a buffer can be attached. - // An (XDG) surface commit (without an attached buffer) triggers this - // configure event - window->body->surface_configured = false; - wl_surface_commit(window->body->surface); - wl_display_roundtrip(application.display); - } -#endif -#if LV_WAYLAND_WL_SHELL - else if(app->wl_shell) { - window->wl_shell_surface = wl_shell_get_shell_surface(app->wl_shell, window->body->surface); - if(!window->wl_shell_surface) { - LV_LOG_ERROR("cannot create WL shell surface"); - goto err_destroy_surface; - } - - wl_shell_surface_add_listener(window->wl_shell_surface, &shell_surface_listener, window); - wl_shell_surface_set_toplevel(window->wl_shell_surface); - wl_shell_surface_set_title(window->wl_shell_surface, title); - - /* For wl_shell, just draw the window, weston doesn't send it */ - draw_window(window, window->width, window->height); - } -#endif - else { - LV_LOG_ERROR("No shell available"); - goto err_destroy_surface; - } - - - return window; - -err_destroy_shell_surface: -#if LV_WAYLAND_WL_SHELL - if(window->wl_shell_surface) { - wl_shell_surface_destroy(window->wl_shell_surface); - } -#endif -#if LV_WAYLAND_XDG_SHELL - if(window->xdg_surface) { - xdg_surface_destroy(window->xdg_surface); - } -#endif - -err_destroy_surface: - wl_surface_destroy(window->body->surface); - -err_free_window: - lv_ll_remove(&app->window_ll, window); - lv_free(window); - return NULL; -} - -static void destroy_window(struct window * window) -{ - if(!window) { - return; - } - -#if LV_WAYLAND_WL_SHELL - if(window->wl_shell_surface) { - wl_shell_surface_destroy(window->wl_shell_surface); - } -#endif -#if LV_WAYLAND_XDG_SHELL - if(window->xdg_toplevel) { - xdg_toplevel_destroy(window->xdg_toplevel); - xdg_surface_destroy(window->xdg_surface); - } -#endif - -#if LV_WAYLAND_WINDOW_DECORATIONS - int b; - for(b = 0; b < NUM_DECORATIONS; b++) { - if(window->decoration[b]) { - destroy_graphic_obj(window->decoration[b]); - window->decoration[b] = NULL; - } - } -#endif - - destroy_graphic_obj(window->body); -} - -static void _lv_wayland_flush(lv_display_t * disp, const lv_area_t * area, unsigned char * color_p) -{ - void * buf_base; - struct wl_buffer * wl_buf; - int32_t src_width; - int32_t src_height; - struct window * window; - struct application * app; - smm_buffer_t * buf; - struct wl_callback * cb; - lv_display_rotation_t rot; - uint8_t bpp; - int32_t x; - int32_t y; - int32_t w; - int32_t h; - int32_t hres; - int32_t vres; - - window = lv_display_get_user_data(disp); - app = window->application; - buf = window->body->pending_buffer; - src_width = lv_area_get_width(area); - src_height = lv_area_get_height(area); - bpp = lv_color_format_get_size(LV_COLOR_FORMAT_NATIVE); - - rot = lv_display_get_rotation(disp); - w = lv_display_get_horizontal_resolution(disp); - h = lv_display_get_vertical_resolution(disp); - - /* TODO actually test what happens if the rotation is 90 or 270 or 180 ? */ - hres = (rot == LV_DISPLAY_ROTATION_0) ? w : h; - vres = (rot == LV_DISPLAY_ROTATION_0) ? h : w; - - /* If window has been / is being closed, or is not visible, skip flush */ - if(window->closed || window->shall_close) { - goto skip; - } - /* Skip if the area is out the screen */ - else if((area->x2 < 0) || (area->y2 < 0) || (area->x1 > hres - 1) || (area->y1 > vres - 1)) { - goto skip; - } - - /* Acquire and map a buffer to attach/commit to surface */ - if(buf == NULL) { - 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); - } - - buf_base = smm_map(buf); - if(buf_base == NULL) { - LV_LOG_ERROR("cannot map in window body buffer"); - goto skip; - } - - /* Modify specified area in buffer */ - for(y = 0; y < src_height; ++y) { - if(app->shm_format == WL_SHM_FORMAT_ARGB8888) { - for(x = 0; x < src_width; ++x) { - lv_color_premultiply((lv_color32_t *)color_p + x); - } - } - memcpy(((char *)buf_base) + ((((area->y1 + y) * hres) + area->x1) * bpp), - color_p, src_width * bpp); - color_p += src_width * bpp; - } - - /* Mark surface damage */ - wl_surface_damage(window->body->surface, - area->x1, - area->y1, - src_width, - src_height); - - cache_add_area(window, buf, area); - - - if(lv_display_flush_is_last(disp)) { - /* 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; - window->frame_done = false; - - cb = wl_surface_frame(window->body->surface); - wl_callback_add_listener(cb, &wl_surface_frame_listener, window->body); - LV_LOG_TRACE("last flush frame: %d", window->frame_counter); - - window->flush_pending = true; - } - - lv_display_flush_ready(disp); - return; -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; - } -} - -static void _lv_wayland_handle_input(void) -{ - int prepare_read = wl_display_prepare_read(application.display); + int prepare_read = -1; while(prepare_read != 0) { - wl_display_dispatch_pending(application.display); + wl_display_dispatch_pending(lv_wl_ctx.display); + prepare_read = wl_display_prepare_read(lv_wl_ctx.display); } - - wl_display_read_events(application.display); - wl_display_dispatch_pending(application.display); + wl_display_read_events(lv_wl_ctx.display); + wl_display_dispatch_pending(lv_wl_ctx.display); } -static void _lv_wayland_handle_output(void) +static void handle_output(void) { struct window * window; - bool shall_flush = application.cursor_flush_pending; + bool shall_flush = lv_wl_ctx.cursor_flush_pending; - LV_LL_READ(&application.window_ll, window) { + LV_LL_READ(&lv_wl_ctx.window_ll, window) { if((window->shall_close) && (window->close_cb != NULL)) { window->shall_close = window->close_cb(window->lv_disp); } @@ -2282,629 +394,45 @@ static void _lv_wayland_handle_output(void) continue; } else if(window->shall_close) { - window->closed = true; + window->closed = true; window->shall_close = false; - shall_flush = true; + shall_flush = true; - window->body->input.pointer.x = 0; - window->body->input.pointer.y = 0; - window->body->input.pointer.left_button = LV_INDEV_STATE_RELEASED; + window->body->input.pointer.x = 0; + window->body->input.pointer.y = 0; + window->body->input.pointer.left_button = LV_INDEV_STATE_RELEASED; window->body->input.pointer.right_button = LV_INDEV_STATE_RELEASED; window->body->input.pointer.wheel_button = LV_INDEV_STATE_RELEASED; - window->body->input.pointer.wheel_diff = 0; - if(window->application->pointer_obj == window->body) { - window->application->pointer_obj = NULL; + window->body->input.pointer.wheel_diff = 0; + if(window->wl_ctx->pointer_obj == window->body) { + window->wl_ctx->pointer_obj = NULL; } - window->body->input.keyboard.key = 0; + window->body->input.keyboard.key = 0; window->body->input.keyboard.state = LV_INDEV_STATE_RELEASED; - if(window->application->keyboard_obj == window->body) { - window->application->keyboard_obj = NULL; + if(window->wl_ctx->keyboard_obj == window->body) { + window->wl_ctx->keyboard_obj = NULL; } - destroy_window(window); + lv_wayland_window_destroy(window); } shall_flush |= window->flush_pending; } if(shall_flush) { - if(wl_display_flush(application.display) == -1) { + if(wl_display_flush(lv_wl_ctx.display) == -1) { if(errno != EAGAIN) { LV_LOG_ERROR("failed to flush wayland display"); } } else { /* All data flushed */ - application.cursor_flush_pending = false; - LV_LL_READ(&application.window_ll, window) { + lv_wl_ctx.cursor_flush_pending = false; + LV_LL_READ(&lv_wl_ctx.window_ll, window) { window->flush_pending = false; } } } } -static void _lv_wayland_pointer_read(lv_indev_t * drv, lv_indev_data_t * data) -{ - struct window * window = lv_display_get_user_data(lv_indev_get_display(drv)); - - if(!window || window->closed) { - return; - } - - data->point.x = window->body->input.pointer.x; - data->point.y = window->body->input.pointer.y; - data->state = window->body->input.pointer.left_button; -} - -static void _lv_wayland_pointeraxis_read(lv_indev_t * drv, lv_indev_data_t * data) -{ - struct window * window = lv_display_get_user_data(lv_indev_get_display(drv)); - - if(!window || window->closed) { - return; - } - - data->state = window->body->input.pointer.wheel_button; - data->enc_diff = window->body->input.pointer.wheel_diff; - - window->body->input.pointer.wheel_diff = 0; -} - -static void _lv_wayland_keyboard_read(lv_indev_t * drv, lv_indev_data_t * data) -{ - struct window * window = lv_display_get_user_data(lv_indev_get_display(drv)); - if(!window || window->closed) { - return; - } - - data->key = window->body->input.keyboard.key; - data->state = window->body->input.keyboard.state; -} - -#if LV_USE_GESTURE_RECOGNITION - -static void _lv_wayland_touch_read(lv_indev_t * drv, lv_indev_data_t * data) -{ - - struct window * window = lv_display_get_user_data(lv_indev_get_display(drv)); - - if(!window || window->closed) { - return; - } - - /* Collect touches if there are any - send them to the gesture recognizer */ - lv_indev_gesture_recognizers_update(drv, &window->body->input.touches[0], - window->body->input.touch_event_cnt); - - LV_LOG_TRACE("collected touch events: %d", window->body->input.touch_event_cnt); - - window->body->input.touch_event_cnt = 0; - - /* Set the gesture information, before returning to LVGL */ - lv_indev_gesture_recognizers_set_data(drv, data); - -} - -#endif /* END LV_USE_GESTURE_RECOGNITION */ - -/********************** - * GLOBAL FUNCTIONS - **********************/ - -/** - * Initialize Wayland driver - */ -static void wayland_init(void) -{ - /* Prevent reinitializing wayland */ - if(wayland_initialized) { - return; - } - - struct smm_events evs = { - NULL, - sme_new_pool, - sme_expand_pool, - sme_free_pool, - sme_new_buffer, - sme_init_buffer, - sme_free_buffer - }; - - // Create XKB context - application.xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - LV_ASSERT_MSG(application.xkb_context, "failed to create XKB context"); - if(application.xkb_context == NULL) { - return; - } - - // Connect to Wayland display - application.display = wl_display_connect(NULL); - LV_ASSERT_MSG(application.display, "failed to connect to Wayland server"); - if(application.display == NULL) { - return; - } - - /* Add registry listener and wait for registry reception */ - application.shm_format = SHM_FORMAT_UNKNOWN; - application.registry = wl_display_get_registry(application.display); - wl_registry_add_listener(application.registry, ®istry_listener, &application); - wl_display_dispatch(application.display); - wl_display_roundtrip(application.display); - - LV_ASSERT_MSG(application.compositor, "Wayland compositor not available"); - if(application.compositor == NULL) { - return; - } - - LV_ASSERT_MSG(application.shm, "Wayland SHM not available"); - if(application.shm == NULL) { - return; - } - - LV_ASSERT_MSG((application.shm_format != SHM_FORMAT_UNKNOWN), "WL_SHM_FORMAT not available"); - if(application.shm_format == SHM_FORMAT_UNKNOWN) { - LV_LOG_TRACE("Unable to match a suitable SHM format for selected LVGL color depth"); - return; - } - - smm_init(&evs); - smm_setctx(&application); - -#ifdef LV_WAYLAND_WINDOW_DECORATIONS - const char * env_disable_decorations = getenv("LV_WAYLAND_DISABLE_WINDOWDECORATION"); - application.opt_disable_decorations = ((env_disable_decorations != NULL) && - (env_disable_decorations[0] != '0')); -#endif - - lv_ll_init(&application.window_ll, sizeof(struct window)); - - lv_tick_set_cb(tick_get_cb); - - /* Used to wait for events when the window is minimized or hidden */ - application.wayland_pfd.fd = wl_display_get_fd(application.display); - application.wayland_pfd.events = POLLIN; - wayland_initialized = true; - -} - -/** - * De-initialize Wayland driver - */ -static void wayland_deinit(void) -{ - struct window * window = NULL; - - LV_LL_READ(&application.window_ll, window) { - if(!window->closed) { - destroy_window(window); - } - - lv_draw_buf_destroy(window->lv_disp_draw_buf); - lv_display_delete(window->lv_disp); - } - - smm_deinit(); - - if(application.shm) { - wl_shm_destroy(application.shm); - } - -#if LV_WAYLAND_XDG_SHELL - if(application.xdg_wm) { - xdg_wm_base_destroy(application.xdg_wm); - } -#endif - -#if LV_WAYLAND_WL_SHELL - if(application.wl_shell) { - wl_shell_destroy(application.wl_shell); - } -#endif - - if(application.wl_seat) { - wl_seat_destroy(application.wl_seat); - } - - if(application.subcompositor) { - wl_subcompositor_destroy(application.subcompositor); - } - - if(application.compositor) { - wl_compositor_destroy(application.compositor); - } - - wl_registry_destroy(application.registry); - wl_display_flush(application.display); - wl_display_disconnect(application.display); - - lv_ll_clear(&application.window_ll); - -} - -static uint32_t tick_get_cb(void) -{ - struct timespec t; - clock_gettime(CLOCK_MONOTONIC, &t); - uint64_t time_ms = t.tv_sec * 1000 + (t.tv_nsec / 1000000); - return time_ms; -} - -/** - * Get Wayland display file descriptor - * @return Wayland display file descriptor - */ -int lv_wayland_get_fd(void) -{ - return wl_display_get_fd(application.display); -} - -/** - * Create wayland window - * @param hor_res initial horizontal window body size in pixels - * @param ver_res initial vertical window body size in pixels - * @param title window title - * @param close_cb function to be called when the window gets closed by the user (optional) - * @return new display backed by a Wayland window, or NULL on error - */ -lv_display_t * lv_wayland_window_create(uint32_t hor_res, uint32_t ver_res, char * title, - lv_wayland_display_close_f_t close_cb) -{ - struct window * window; - int32_t window_width; - int32_t window_height; - int32_t stride; - - wayland_init(); - - window_width = hor_res; - window_height = ver_res; - -#if LV_WAYLAND_WINDOW_DECORATIONS - - /* Decorations are enabled, calculate the body size */ - if(!application.opt_disable_decorations) { - window_width = hor_res + (2 * BORDER_SIZE); - window_height = ver_res + (TITLE_BAR_HEIGHT + (2 * BORDER_SIZE)); - } - -#endif - - window = create_window(&application, window_width, window_height, title); - if(!window) { - LV_LOG_ERROR("failed to create wayland window"); - return NULL; - } -#if LV_WAYLAND_XDG_SHELL - LV_ASSERT_MSG(window->body->surface_configured, "Failed to receive the xdg_surface configuration event"); -#endif - - window->close_cb = close_cb; - - /* Initialize display driver */ - window->lv_disp = lv_display_create(hor_res, ver_res); - if(window->lv_disp == NULL) { - LV_LOG_ERROR("failed to create lvgl display"); - return NULL; - } - - stride = lv_draw_buf_width_to_stride(hor_res, - lv_display_get_color_format(window->lv_disp)); - - window->lv_disp_draw_buf = lv_draw_buf_create( - hor_res, - ver_res / LVGL_DRAW_BUFFER_DIV, - lv_display_get_color_format(window->lv_disp), - stride); - - - lv_display_set_draw_buffers(window->lv_disp, window->lv_disp_draw_buf, NULL); - lv_display_set_render_mode(window->lv_disp, LV_DISPLAY_RENDER_MODE_PARTIAL); - lv_display_set_flush_cb(window->lv_disp, _lv_wayland_flush); - lv_display_set_user_data(window->lv_disp, window); - - /* Register input */ - window->lv_indev_pointer = lv_indev_create(); - lv_indev_set_type(window->lv_indev_pointer, LV_INDEV_TYPE_POINTER); - lv_indev_set_read_cb(window->lv_indev_pointer, _lv_wayland_pointer_read); - lv_indev_set_display(window->lv_indev_pointer, window->lv_disp); - - if(!window->lv_indev_pointer) { - LV_LOG_ERROR("failed to register pointer indev"); - } - - window->lv_indev_pointeraxis = lv_indev_create(); - lv_indev_set_type(window->lv_indev_pointeraxis, LV_INDEV_TYPE_ENCODER); - lv_indev_set_read_cb(window->lv_indev_pointeraxis, _lv_wayland_pointeraxis_read); - lv_indev_set_display(window->lv_indev_pointeraxis, window->lv_disp); - - if(!window->lv_indev_pointeraxis) { - LV_LOG_ERROR("failed to register pointeraxis indev"); - } - -#if LV_USE_GESTURE_RECOGNITION - - window->lv_indev_touch = lv_indev_create(); - lv_indev_set_type(window->lv_indev_touch, LV_INDEV_TYPE_POINTER); - lv_indev_set_read_cb(window->lv_indev_touch, _lv_wayland_touch_read); - lv_indev_set_display(window->lv_indev_touch, window->lv_disp); - - if(!window->lv_indev_touch) { - LV_LOG_ERROR("failed to register touch indev"); - } - -#endif /* END LV_USE_GESTURE_RECOGNITION */ - - window->lv_indev_keyboard = lv_indev_create(); - lv_indev_set_type(window->lv_indev_keyboard, LV_INDEV_TYPE_KEYPAD); - lv_indev_set_read_cb(window->lv_indev_keyboard, _lv_wayland_keyboard_read); - lv_indev_set_display(window->lv_indev_keyboard, window->lv_disp); - - if(!window->lv_indev_keyboard) { - LV_LOG_ERROR("failed to register keyboard indev"); - } - - return window->lv_disp; -} - -/** - * Close wayland window - * @param disp LVGL display using window to be closed - */ -void lv_wayland_window_close(lv_display_t * disp) -{ - struct window * window = lv_display_get_user_data(disp); - if(!window || window->closed) { - return; - } - window->shall_close = true; - window->close_cb = NULL; - wayland_deinit(); -} - -/** - * Check if a Wayland window is open on the specified display. Otherwise (if - * argument is NULL), check if any Wayland window is open. - * @return true if window open, false otherwise - */ -bool lv_wayland_window_is_open(lv_display_t * disp) -{ - struct window * window; - bool open = false; - - if(disp == NULL) { - LV_LL_READ(&application.window_ll, window) { - if(!window->closed) { - open = true; - break; - } - } - } - else { - window = lv_display_get_user_data(disp); - open = (!window->closed); - } - - return open; -} -/** - * Set/unset window maximization mode - * @description Maximization is nearly the same as fullscreen, except - * window decorations and the compositor's panels must remain visible - * @param disp LVGL display using window to be set/unset maximization - * @param Maximization requested status (true = maximized) - */ -void lv_wayland_window_set_maximized(lv_display_t * disp, bool maximized) -{ - struct window * window = lv_display_get_user_data(disp); - if(!window || window->closed) { - return; - } - - if(window->maximized != maximized) { - -#if LV_WAYLAND_WL_SHELL - if(window->wl_shell_surface) { - if(maximized) { - /* Maximizing the wl_shell is possible, but requires binding to wl_output */ - /* The wl_shell has been deperacted */ - //wl_shell_surface_set_maximized(window->wl_shell_surface); - } - else { - wl_shell_surface_set_toplevel(window->wl_shell_surface); - } - window->maximized = maximized; - window->flush_pending = true; - } -#endif - -#if LV_WAYLAND_XDG_SHELL - if(window->xdg_toplevel) { - if(maximized) { - xdg_toplevel_set_maximized(window->xdg_toplevel); - } - else { - xdg_toplevel_unset_maximized(window->xdg_toplevel); - } - - window->maximized = maximized; - window->flush_pending = true; - } -#endif - } -} - -/** - * Set/unset window fullscreen mode - * @param disp LVGL display using window to be set/unset fullscreen - * @param fullscreen requested status (true = fullscreen) - */ -void lv_wayland_window_set_fullscreen(lv_display_t * disp, bool fullscreen) -{ - struct window * window = lv_display_get_user_data(disp); - if(!window || window->closed) { - return; - } - - if(window->fullscreen != fullscreen) { - if(0) { - // Needed for #if madness below - } -#if LV_WAYLAND_XDG_SHELL - else if(window->xdg_toplevel) { - if(fullscreen) { - xdg_toplevel_set_fullscreen(window->xdg_toplevel, NULL); - } - else { - xdg_toplevel_unset_fullscreen(window->xdg_toplevel); - } - window->fullscreen = fullscreen; - window->flush_pending = true; - } -#endif -#if LV_WAYLAND_WL_SHELL - else if(window->wl_shell_surface) { - if(fullscreen) { - wl_shell_surface_set_fullscreen(window->wl_shell_surface, - WL_SHELL_SURFACE_FULLSCREEN_METHOD_SCALE, - 0, NULL); - } - else { - wl_shell_surface_set_toplevel(window->wl_shell_surface); - } - window->fullscreen = fullscreen; - window->flush_pending = true; - } -#endif - else { - LV_LOG_WARN("Wayland fullscreen mode not supported"); - } - } -} - -/** - * Get pointer input device for given LVGL display - * @param disp LVGL display - * @return input device connected to pointer events, or NULL on error - */ -lv_indev_t * lv_wayland_get_pointer(lv_display_t * disp) -{ - struct window * window = lv_display_get_user_data(disp); - if(!window) { - return NULL; - } - return window->lv_indev_pointer; -} - -/** - * Get pointer axis input device for given LVGL display - * @param disp LVGL display - * @return input device connected to pointer axis events, or NULL on error - */ -lv_indev_t * lv_wayland_get_pointeraxis(lv_display_t * disp) -{ - struct window * window = lv_display_get_user_data(disp); - if(!window) { - return NULL; - } - return window->lv_indev_pointeraxis; -} - -/** - * Get keyboard input device for given LVGL display - * @param disp LVGL display - * @return input device connected to keyboard, or NULL on error - */ -lv_indev_t * lv_wayland_get_keyboard(lv_display_t * disp) -{ - struct window * window = lv_display_get_user_data(disp); - if(!window) { - return NULL; - } - return window->lv_indev_keyboard; -} - -/** - * Get touchscreen input device for given LVGL display - * @param disp LVGL display - * @return input device connected to touchscreen, or NULL on error - */ -lv_indev_t * lv_wayland_get_touchscreen(lv_display_t * disp) -{ - struct window * window = lv_display_get_user_data(disp); - if(!window) { - return NULL; - } - return window->lv_indev_touch; -} - -/** - * Wayland specific timer handler (use in place of LVGL lv_timer_handler) - */ -bool lv_wayland_timer_handler(void) -{ - struct window * window; - - /* Wayland input handling - it will also trigger the frame done handler */ - _lv_wayland_handle_input(); - - /* Ready input timers (to probe for any input received) */ - LV_LL_READ(&application.window_ll, window) { - LV_LOG_TRACE("handle timer frame: %d", window->frame_counter); - - if(window != NULL && window->frame_done == false - && window->frame_counter > 0) { - /* The last frame was not rendered */ - LV_LOG_TRACE("The window is hidden or minimized"); - - /* Simply blocks until a frame done message arrives */ - poll(&application.wayland_pfd, 1, -1); - - /* Resume lvgl on the next cycle */ - return false; - - } - else if(window != NULL && window->resize_pending) { - if(resize_window(window, window->resize_width, window->resize_height)) { - window->resize_width = window->width; - window->resize_height = window->height; - window->resize_pending = false; - - } - else { - - LV_LOG_TRACE("Failed to resize window frame: %d", - window->frame_counter); - } - } - else if(window->shall_close == true) { - - /* Destroy graphical context and execute close_cb */ - _lv_wayland_handle_output(); - wayland_deinit(); - return false; - } - } - - /* LVGL handling */ - lv_timer_handler(); - - /* 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 true; -} - #endif /* LV_USE_WAYLAND */ -#endif /* _WIN32 */ diff --git a/src/drivers/wayland/lv_wayland.h b/src/drivers/wayland/lv_wayland.h index 2e30fb276a..9d6ebf2406 100644 --- a/src/drivers/wayland/lv_wayland.h +++ b/src/drivers/wayland/lv_wayland.h @@ -1,22 +1,10 @@ -/******************************************************************* - * - * @file lv_wayland.h - Public functions of the LVGL Wayland client - * - * Based on the original file from the repository. - * - * Porting to LVGL 9.1 - * 2024 EDGEMTech Ltd. - * - * See LICENCE.txt for details - * - * Author(s): EDGEMTech Ltd, Erik Tagirov (erik.tagirov@edgemtech.ch) - * - ******************************************************************/ +/** + * @file lv_wayland.h + */ + #ifndef LV_WAYLAND_H #define LV_WAYLAND_H -#ifndef _WIN32 - #ifdef __cplusplus extern "C" { #endif @@ -25,12 +13,16 @@ extern "C" { * INCLUDES *********************/ -#include "../../display/lv_display.h" -#include "../../indev/lv_indev.h" -#include "../../indev/lv_indev_gesture.h" +#include "../../lv_conf_internal.h" #if LV_USE_WAYLAND +#include "lv_wl_keyboard.h" +#include "lv_wl_pointer.h" +#include "lv_wl_touch.h" +#include "lv_wl_window.h" +#include "lv_wl_pointer_axis.h" + /********************* * DEFINES *********************/ @@ -39,95 +31,22 @@ extern "C" { * TYPEDEFS **********************/ -typedef bool (*lv_wayland_display_close_f_t)(lv_display_t * disp); - /********************** * GLOBAL PROTOTYPES **********************/ -/** - * Retrieves the file descriptor of the wayland socket - */ -int lv_wayland_get_fd(void); - -/** - * Creates a window - * @param hor_res The width of the window in pixels - * @param ver_res The height of the window in pixels - * @param title The title of the window - * @param close_cb The callback that will be execute when the user closes the window - * @return The LVGL display associated to the window - */ -lv_display_t * lv_wayland_window_create(uint32_t hor_res, uint32_t ver_res, char * title, - lv_wayland_display_close_f_t close_cb); - -/** - * Closes the window programmatically - * @param disp Reference to the LVGL display associated to the window - */ -void lv_wayland_window_close(lv_display_t * disp); - -/** - * Check if the window is open - * @param disp Reference to the LVGL display associated to the window - * @return true: The window is open - */ -bool lv_wayland_window_is_open(lv_display_t * disp); - -/** - * Sets the fullscreen state of the window - * @param disp Reference to the LVGL display associated to the window - * @param fullscreen If true the window enters fullscreen - */ -void lv_wayland_window_set_fullscreen(lv_display_t * disp, bool fullscreen); - -/** - * Sets the maximized state of the window - * @param disp Reference to the LVGL display associated to the window - * @param fullscreen If true the window is maximized - */ -void lv_wayland_window_set_maximized(lv_display_t * disp, bool maximize); - -/** - * Obtains the input device of the mouse pointer - * @note It is used to create an input group on application start - * @param disp Reference to the LVGL display associated to the window - * @return The input device - */ -lv_indev_t * lv_wayland_get_pointer(lv_display_t * disp); - -/** - * Obtains the input device of the encoder - * @note It is used to create an input group on application start - * @param disp Reference to the LVGL display associated to the window - * @return The input device - */ -lv_indev_t * lv_wayland_get_pointeraxis(lv_display_t * disp); - -/** - * Obtains the input device of the keyboard - * @note It is used to create an input group on application start - * @param disp Reference to the LVGL display associated to the window - * @return The input device - */ -lv_indev_t * lv_wayland_get_keyboard(lv_display_t * disp); - -/** - * Obtains the input device of the touch screen - * @note It is used to create an input group on application start - * @param disp Reference to the LVGL display associated to the window - * @return The input device - */ -lv_indev_t * lv_wayland_get_touchscreen(lv_display_t * disp); - /** * Wrapper around lv_timer_handler * @note Must be called in the application run loop instead of the * regular lv_timer_handler provided by LVGL - * @return true: if the cycle was completed, false if the application - * went to sleep because the last frame wasn't completed + * @return time till it needs to be run next (in ms) */ -bool lv_wayland_timer_handler(void); +uint32_t lv_wayland_timer_handler(void); + +/** + * Retrieves the file descriptor of the wayland socket + */ +int lv_wayland_get_fd(void); /********************** * MACROS @@ -139,5 +58,4 @@ bool lv_wayland_timer_handler(void); } /* extern "C" */ #endif -#endif /* _WIN32 */ #endif /* WAYLAND_H */ diff --git a/src/drivers/wayland/lv_wayland_private.h b/src/drivers/wayland/lv_wayland_private.h new file mode 100644 index 0000000000..374eb0bab8 --- /dev/null +++ b/src/drivers/wayland/lv_wayland_private.h @@ -0,0 +1,374 @@ +/** + * @file lv_wayland_private.h + * + */ + +#ifndef LV_WAYLAND_PRIVATE_H +#define LV_WAYLAND_PRIVATE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "lv_wayland.h" +#if LV_USE_WAYLAND + + +#include "lv_wayland_smm.h" +#include +#include +#if !LV_WAYLAND_WL_SHELL +#include "wayland_xdg_shell.h" +#define LV_WAYLAND_XDG_SHELL 1 +#else +#define LV_WAYLAND_XDG_SHELL 0 +#endif + +/********************* + * DEFINES + *********************/ + +#define LV_WAYLAND_DEFAULT_CURSOR_NAME "left_ptr" + +#define LVGL_DRAW_BUFFER_DIV (8) +#define DMG_CACHE_CAPACITY (32) +#define TAG_LOCAL (0) +#define TAG_BUFFER_DAMAGE (1) + +#if LV_WAYLAND_WINDOW_DECORATIONS +#define TITLE_BAR_HEIGHT 24 +#define BORDER_SIZE 2 +#else +#define TITLE_BAR_HEIGHT 0 +#define BORDER_SIZE 0 +#endif + +#define BUTTON_MARGIN LV_MAX((TITLE_BAR_HEIGHT / 6), BORDER_SIZE) +#define BUTTON_PADDING LV_MAX((TITLE_BAR_HEIGHT / 8), BORDER_SIZE) +#define BUTTON_SIZE (TITLE_BAR_HEIGHT - (2 * BUTTON_MARGIN)) + +#ifndef LV_WAYLAND_CYCLE_PERIOD +#define LV_WAYLAND_CYCLE_PERIOD LV_MIN(LV_DEF_REFR_PERIOD, 1) +#endif + +/********************** + * TYPEDEFS + **********************/ + +enum object_type { + OBJECT_TITLEBAR = 0, + OBJECT_BUTTON_CLOSE, +#if LV_WAYLAND_XDG_SHELL + OBJECT_BUTTON_MAXIMIZE, + OBJECT_BUTTON_MINIMIZE, +#endif + OBJECT_BORDER_TOP, + OBJECT_BORDER_BOTTOM, + OBJECT_BORDER_LEFT, + 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; +struct input { + struct { + uint32_t x; + uint32_t y; + lv_indev_state_t left_button; + lv_indev_state_t right_button; + lv_indev_state_t wheel_button; + int16_t wheel_diff; + } pointer; + + struct { + lv_key_t key; + lv_indev_state_t state; + } keyboard; + +#if LV_USE_GESTURE_RECOGNITION + lv_indev_touch_data_t touches[10]; + uint8_t touch_event_cnt; + uint8_t primary_id; +#endif +}; + +struct seat { + struct wl_touch * wl_touch; + struct wl_pointer * wl_pointer; + struct wl_keyboard * wl_keyboard; + + struct { + struct xkb_keymap * keymap; + struct xkb_state * state; + } xkb; +}; + +struct graphic_object { + struct window * window; + struct wl_surface * surface; + struct wl_subsurface * subsurface; + smm_buffer_t * pending_buffer; + smm_group_t * buffer_group; + struct input input; + enum object_type type; + int width; + int height; +}; + +typedef struct { + struct buffer * buffers; + struct zwp_linux_dmabuf_v1 * handler; + uint32_t format; +} dmabuf_ctx_t; + +typedef struct { + lv_draw_buf_t * lv_draw_buf; + struct wl_shm * handler; + uint32_t format; +} shm_ctx_t; + +struct lv_wayland_context { + struct wl_display * display; + struct wl_registry * registry; + struct wl_compositor * compositor; + struct wl_subcompositor * subcompositor; + struct wl_seat * wl_seat; + struct wl_cursor_theme * cursor_theme; + struct wl_surface * cursor_surface; + shm_ctx_t shm_ctx; + +#if LV_WAYLAND_USE_DMABUF + dmabuf_ctx_t dmabuf_ctx; +#endif + +#if LV_WAYLAND_WL_SHELL + struct wl_shell * wl_shell; +#endif + +#if LV_WAYLAND_XDG_SHELL + struct xdg_wm_base * xdg_wm; +#endif + +#ifdef LV_WAYLAND_WINDOW_DECORATIONS + bool opt_disable_decorations; +#endif + + struct xkb_context * xkb_context; + + struct seat seat; + + struct graphic_object * touch_obj; + struct graphic_object * pointer_obj; + struct graphic_object * keyboard_obj; + + lv_ll_t window_ll; + lv_timer_t * cycle_timer; + + bool cursor_flush_pending; + struct pollfd wayland_pfd; +}; + +struct window { + lv_display_t * lv_disp; + lv_indev_t * lv_indev_pointer; + lv_indev_t * lv_indev_pointeraxis; + lv_indev_t * lv_indev_touch; + lv_indev_t * lv_indev_keyboard; + + lv_wayland_display_close_f_t close_cb; + + struct lv_wayland_context * wl_ctx; + +#if LV_WAYLAND_WL_SHELL + struct wl_shell_surface * wl_shell_surface; +#endif + +#if LV_WAYLAND_XDG_SHELL + struct xdg_surface * xdg_surface; + struct xdg_toplevel * xdg_toplevel; + uint32_t wm_capabilities; +#endif + + 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_WINDOW_DECORATIONS + struct graphic_object * decoration[NUM_DECORATIONS]; +#endif + + int width; + int height; + + bool resize_pending; + int resize_width; + int resize_height; + + bool flush_pending; + bool shall_close; + bool closed; + bool maximized; + bool fullscreen; + uint32_t frame_counter; +}; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/********************** + * MACROS + **********************/ + +extern struct lv_wayland_context lv_wl_ctx; + +/********************** + * Driver + **********************/ + +void lv_wayland_init(void); +void lv_wayland_deinit(void); +void lv_wayland_wait_flush_cb(lv_display_t * disp); +/********************** + * Window + **********************/ + +const struct wl_callback_listener * lv_wayland_window_get_wl_surface_frame_listener(void); + +void lv_wayland_window_draw(struct window * window, uint32_t width, uint32_t height); +lv_result_t lv_wayland_window_resize(struct window * window, int width, int height); +void lv_wayland_window_destroy(struct window * window); +#if LV_WAYLAND_WINDOW_DECORATIONS +uint32_t lv_wayland_window_decoration_create_all(struct window * window); +void lv_wayland_window_decoration_detach_all(struct window * window); +bool lv_wayland_window_decoration_create(struct window * window, struct graphic_object * decoration, int window_width, + int window_height); +bool lv_wayland_window_decoration_attach(struct window * window, struct graphic_object * decoration, + smm_buffer_t * decoration_buffer, struct graphic_object * parent); +void lv_wayland_window_decoration_detach(struct window * window, struct graphic_object * decoration); +#endif + +/********************** + * Window Management + **********************/ + +#if LV_WAYLAND_WL_SHELL +lv_result_t lv_wayland_wl_shell_create_window(struct lv_wayland_context * app, struct window * window, + const char * title); +const struct wl_shell_surface_listener * lv_wayland_wl_shell_get_listener(void); +void lv_wayland_wl_shell_handle_pointer_event(struct lv_wayland_context * app, uint32_t serial, uint32_t button, + uint32_t state); +lv_result_t lv_wayland_wl_shell_set_maximized(struct window * window, bool maximized); +lv_result_t lv_wayland_wl_shell_set_minimized(struct window * window); +lv_result_t lv_wayland_wl_shell_set_fullscreen(struct window * window, bool fullscreen); +lv_result_t lv_wayland_wl_shell_destroy_window(struct window * window); +void lv_wayland_wl_shell_deinit(void); +#elif LV_WAYLAND_XDG_SHELL + +const struct xdg_surface_listener * lv_wayland_xdg_shell_get_surface_listener(void); +const struct xdg_toplevel_listener * lv_wayland_xdg_shell_get_toplevel_listener(void); +const struct xdg_wm_base_listener * lv_wayland_xdg_shell_get_wm_base_listener(void); +lv_result_t lv_wayland_xdg_shell_set_maximized(struct window * window, bool maximized); +lv_result_t lv_wayland_xdg_shell_set_minimized(struct window * window); +lv_result_t lv_wayland_xdg_shell_set_fullscreen(struct window * window, bool fullscreen); +lv_result_t lv_wayland_xdg_shell_create_window(struct lv_wayland_context * app, struct window * window, + const char * title); +lv_result_t lv_wayland_xdg_shell_destroy_window_toplevel(struct window * window); +lv_result_t lv_wayland_xdg_shell_destroy_window_surface(struct window * window); +void lv_wayland_xdg_shell_handle_pointer_event(struct lv_wayland_context * app, uint32_t serial, uint32_t button, + uint32_t state); + +const char * lv_wayland_xdg_shell_get_cursor_name(const struct lv_wayland_context * app); +void lv_wayland_xdg_shell_deinit(void); +#endif + +/********************** + * SHM + **********************/ + +void lv_wayland_shm_set_interface(shm_ctx_t * context, struct wl_registry * registry, uint32_t name, + const char * interface, uint32_t version); + +struct graphic_object * lv_wayland_shm_on_graphical_object_creation(shm_ctx_t * context, struct graphic_object * obj); +void lv_wayland_shm_on_graphical_object_destruction(shm_ctx_t * context, struct graphic_object * obj); +lv_result_t lv_wayland_shm_set_draw_buffers(shm_ctx_t * context, lv_display_t * display); +lv_result_t lv_wayland_shm_create_draw_buffers(shm_ctx_t * context, struct window * window); +lv_result_t lv_wayland_shm_resize_window(shm_ctx_t * context, struct window * window, int32_t width, int32_t height); +lv_result_t lv_wayland_shm_is_ready(shm_ctx_t * context); + +void lv_wayland_shm_delete_draw_buffers(shm_ctx_t * context, struct window * window); +void lv_wayland_shm_initalize_context(shm_ctx_t * context); +void lv_wayland_shm_deinit(shm_ctx_t * context); +void lv_wayland_shm_flush_partial_mode(lv_display_t * disp, const lv_area_t * area, unsigned char * color_p); + +struct wl_cursor_theme * lv_wayland_shm_load_cursor_theme(shm_ctx_t * context); + +/********************** + * DMABUF + **********************/ + +void lv_wayland_dmabuf_set_interface(dmabuf_ctx_t * context, struct wl_registry * registry, uint32_t name, + const char * interface, uint32_t version); + +struct graphic_object * lv_wayland_dmabuf_on_graphical_object_creation(dmabuf_ctx_t * context, + struct graphic_object * obj); + +void lv_wayland_dmabuf_on_graphical_object_destruction(dmabuf_ctx_t * context, struct graphic_object * obj); +lv_result_t lv_wayland_dmabuf_set_draw_buffers(dmabuf_ctx_t * context, lv_display_t * display); +lv_result_t lv_wayland_dmabuf_create_draw_buffers(dmabuf_ctx_t * context, struct window * window); +lv_result_t lv_wayland_dmabuf_resize_window(dmabuf_ctx_t * context, struct window * window); +lv_result_t lv_wayland_dmabuf_is_ready(dmabuf_ctx_t * context); + +void lv_wayland_dmabuf_destroy_draw_buffers(dmabuf_ctx_t * context, struct window * window); +void lv_wayland_dmabuf_initalize_context(dmabuf_ctx_t * context); +void lv_wayland_dmabuf_deinit(dmabuf_ctx_t * context); +void lv_wayland_dmabuf_flush_full_mode(lv_display_t * disp, const lv_area_t * area, unsigned char * color_p); + +/********************** + * SME + **********************/ + +const struct smm_events * lv_wayland_sme_get_events(void); + +/********************** + * Seat + **********************/ + +const struct wl_seat_listener * lv_wayland_seat_get_listener(void); + +/********************** + * Input + **********************/ + +const struct wl_keyboard_listener * lv_wayland_keyboard_get_listener(void); +const struct wl_pointer_listener * lv_wayland_pointer_get_listener(void); +const struct wl_touch_listener * lv_wayland_touch_get_listener(void); + +/********************** + * Cache + **********************/ + +void lv_wayland_cache_add_area(struct window * window, smm_buffer_t * buf, const lv_area_t * area); +void lv_wayland_cache_clear(struct window * window); +void lv_wayland_cache_apply_areas(struct window * window, void * dest, void * src, smm_buffer_t * src_buf); +void lv_wayland_cache_purge(struct window * window, smm_buffer_t * buf); + +#endif /* LV_USE_WAYLAND */ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_WAYLAND_PRIVATE_H*/ diff --git a/src/drivers/wayland/lv_wayland_smm.c b/src/drivers/wayland/lv_wayland_smm.c index 945da8d040..168e45de81 100644 --- a/src/drivers/wayland/lv_wayland_smm.c +++ b/src/drivers/wayland/lv_wayland_smm.c @@ -8,7 +8,6 @@ typedef int dummy_t; /* Make GCC on windows happy, avoid empty translation u #ifndef _WIN32 #include "lv_wayland_smm.h" -#include "../../display/lv_display.h" #if LV_USE_WAYLAND @@ -144,7 +143,7 @@ static struct { } smm_instance; -void smm_init(struct smm_events * evs) +void smm_init(const struct smm_events * evs) { memcpy(&smm_instance.cbs, evs, sizeof(struct smm_events)); srand((unsigned int)clock()); diff --git a/src/drivers/wayland/lv_wayland_smm.h b/src/drivers/wayland/lv_wayland_smm.h index 617244f174..f3a16e9e24 100644 --- a/src/drivers/wayland/lv_wayland_smm.h +++ b/src/drivers/wayland/lv_wayland_smm.h @@ -69,7 +69,7 @@ struct smm_group_properties { void * tag[SMM_GROUP_TAGS]; }; -void smm_init(struct smm_events * evs); +void smm_init(const struct smm_events * evs); void smm_setctx(void * ctx); void smm_deinit(void); smm_group_t * smm_create(void); diff --git a/src/drivers/wayland/lv_wl_cache.c b/src/drivers/wayland/lv_wl_cache.c new file mode 100644 index 0000000000..5d810516f8 --- /dev/null +++ b/src/drivers/wayland/lv_wl_cache.c @@ -0,0 +1,133 @@ +/** + * @file lv_wl_cache.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_wayland.h" + +#if LV_USE_WAYLAND + +#include "lv_wayland_private.h" +#include + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/********************** + * PRIVATE FUNCTIONS + **********************/ + +void lv_wayland_cache_apply_areas(struct window * window, void * dest, void * src, smm_buffer_t * src_buf) +{ + unsigned long offset; + unsigned char start; + int32_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]; + uint8_t bpp; + + if(next_buf == NULL) { + next_dmg = (window->dmg_cache.cache + window->dmg_cache.end); + } + else { + next_dmg = SMM_BUFFER_PROPERTIES(next_buf)->tag[TAG_BUFFER_DAMAGE]; + } + + bpp = lv_color_format_get_size(LV_COLOR_FORMAT_NATIVE); + + /* 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) { + /* Copy an area from source to destination (line-by-line) */ + dmg = (window->dmg_cache.cache + start); + for(y = dmg->y1; y <= dmg->y2; y++) { + offset = (dmg->x1 + (y * obj->width)) * bpp; + + memcpy(((char *)dest) + offset, ((char *)src) + offset, ((dmg->x2 - dmg->x1 + 1) * bpp)); + } + + start++; + start %= DMG_CACHE_CAPACITY; + } +} + +void lv_wayland_cache_add_area(struct window * window, smm_buffer_t * buf, const lv_area_t * area) +{ + if(SMM_BUFFER_PROPERTIES(buf)->tag[TAG_BUFFER_DAMAGE] == NULL) { + /* Buffer damage beyond cache capacity */ + return; + } + + if((window->dmg_cache.start == window->dmg_cache.end) && (window->dmg_cache.size)) { + /* This buffer has more damage then the cache's capacity, so + * clear cache and leave buffer damage unrecorded + */ + lv_wayland_cache_clear(window); + SMM_TAG(buf, TAG_BUFFER_DAMAGE, NULL); + return; + } + + /* 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++; +} + +void lv_wayland_cache_clear(struct window * window) +{ + window->dmg_cache.start = window->dmg_cache.end; + window->dmg_cache.size = 0; +} + +void lv_wayland_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) { + lv_wayland_cache_clear(window); + } + else { + 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--; + } + } +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /* LV_USE_WAYLAND */ diff --git a/src/drivers/wayland/lv_wl_dmabuf.c b/src/drivers/wayland/lv_wl_dmabuf.c new file mode 100644 index 0000000000..ce6cb9bb8e --- /dev/null +++ b/src/drivers/wayland/lv_wl_dmabuf.c @@ -0,0 +1,360 @@ +/** + * @file lv_wl_dmabuf.c + * + */ + +#include "lv_wayland.h" + +#if LV_WAYLAND_USE_DMABUF + +#include "lv_wayland_private.h" +#include +#include +#include +#include +#include +#include "../../draw/nxp/g2d/lv_g2d_utils.h" + +/********************* + * INCLUDES + *********************/ + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +#define MAX_BUFFER_PLANES 4 + +struct buffer { + int busy; + +#if LV_WAYLAND_USE_DMABUF + struct window * window; + int plane_count; + + int dmabuf_fds[MAX_BUFFER_PLANES]; + uint32_t strides[MAX_BUFFER_PLANES]; + uint32_t offsets[MAX_BUFFER_PLANES]; + struct wl_buffer * buffer; +#endif + + void * buf_base[MAX_BUFFER_PLANES]; + lv_draw_buf_t * lv_draw_buf; +}; + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void dmabuf_modifiers(void * data, struct zwp_linux_dmabuf_v1 * zwp_linux_dmabuf, uint32_t format, + uint32_t modifier_hi, uint32_t modifier_lo); +static void dmabuf_format(void * data, struct zwp_linux_dmabuf_v1 * zwp_linux_dmabuf, uint32_t format); +static struct buffer * dmabuf_acquire_buffer(dmabuf_ctx_t * context, unsigned char * color_p); +static struct buffer * lv_wayland_dmabuf_create_draw_buffers_internal(struct window * window); +static void buffer_free(struct buffer * buf); + +/********************** + * STATIC VARIABLES + **********************/ + +static const struct zwp_linux_dmabuf_v1_listener dmabuf_listener_v3 = {.format = dmabuf_format, + .modifier = dmabuf_modifiers +}; +static const struct zwp_linux_dmabuf_v1_listener dmabuf_listener = {.format = dmabuf_format}; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/********************** + * PRIVATE FUNCTIONS + **********************/ + +void lv_wayland_dmabuf_initalize_context(dmabuf_ctx_t * context) +{ + memset(context, 0, sizeof(*context)); + context->format = DRM_FORMAT_INVALID; +} +lv_result_t lv_wayland_dmabuf_set_draw_buffers(dmabuf_ctx_t * context, lv_display_t * display) +{ + if(LV_WAYLAND_BUF_COUNT == 2) { + lv_display_set_draw_buffers(display, context->buffers[0].lv_draw_buf, context->buffers[1].lv_draw_buf); + return LV_RESULT_OK; + } + else if(LV_WAYLAND_BUF_COUNT == 1) { + lv_display_set_draw_buffers(display, context->buffers[0].lv_draw_buf, NULL); + return LV_RESULT_OK; + } + return LV_RESULT_INVALID; +} + +void lv_wayland_dmabuf_set_interface(dmabuf_ctx_t * context, struct wl_registry * registry, uint32_t name, + const char * interface, uint32_t version) +{ + LV_UNUSED(interface); + if(version > 3) { + LV_LOG_WARN("Unsupported DMABUF version %d. Using version 3 instead", version); + version = 3; + } + + context->handler = wl_registry_bind(registry, name, &zwp_linux_dmabuf_v1_interface, version); + if(version < 3) { + zwp_linux_dmabuf_v1_add_listener(context->handler, &dmabuf_listener, context); + } + else if(version == 3) { + zwp_linux_dmabuf_v1_add_listener(context->handler, &dmabuf_listener_v3, context); + } +} + +lv_result_t lv_wayland_dmabuf_is_ready(dmabuf_ctx_t * context) +{ + return (context->handler && context->format != DRM_FORMAT_INVALID) ? LV_RESULT_OK : LV_RESULT_INVALID; +} + +void lv_wayland_dmabuf_destroy_window(dmabuf_ctx_t * context, struct window * window) +{ + LV_UNUSED(context); + LV_ASSERT_NULL(window); +} + +void lv_wayland_dmabuf_deinit(dmabuf_ctx_t * context) +{ + LV_UNUSED(context); +} + +struct graphic_object * lv_wayland_dmabuf_on_graphical_object_creation(dmabuf_ctx_t * context, + struct graphic_object * obj) +{ + LV_UNUSED(context); + return obj; +} + +void lv_wayland_dmabuf_on_graphical_object_destruction(dmabuf_ctx_t * context, struct graphic_object * obj) +{ + + LV_UNUSED(context); + LV_UNUSED(obj); +} + +void lv_wayland_dmabuf_flush_full_mode(lv_display_t * disp, const lv_area_t * area, unsigned char * color_p) +{ + struct window * window = lv_display_get_user_data(disp); + struct buffer * buf = dmabuf_acquire_buffer(&window->wl_ctx->dmabuf_ctx, color_p); + if(!buf) { + LV_LOG_ERROR("Failed to acquire a wayland window body buffer"); + return; + } + + int32_t src_width = lv_area_get_width(area); + int32_t src_height = lv_area_get_height(area); + + lv_draw_buf_invalidate_cache(buf->lv_draw_buf, NULL); + + /* Mark surface damage */ + wl_surface_damage(window->body->surface, area->x1, area->y1, src_width, src_height); + + if(lv_display_flush_is_last(disp)) { + /* Finally, attach buffer and commit to surface */ + wl_surface_attach(window->body->surface, buf->buffer, 0, 0); + wl_surface_commit(window->body->surface); + + struct wl_callback * cb = wl_surface_frame(window->body->surface); + wl_callback_add_listener(cb, lv_wayland_window_get_wl_surface_frame_listener(), window->body); + + buf->busy = 1; + window->flush_pending = true; + } + + return; +} +/********************** + * STATIC FUNCTIONS + **********************/ + +static void buffer_release(void * data, struct wl_buffer * buffer) +{ + LV_UNUSED(buffer); + struct buffer * buf = data; + buf->busy = 0; +} + +static const struct wl_buffer_listener buffer_listener = {.release = buffer_release}; + +static void create_succeeded(void * data, struct zwp_linux_buffer_params_v1 * params, struct wl_buffer * new_buffer) +{ + struct buffer * buffer = data; + buffer->buffer = new_buffer; + /* When not using explicit synchronization listen to wl_buffer.release + * for release notifications, otherwise we are going to use + * zwp_linux_buffer_release_v1. */ + wl_buffer_add_listener(buffer->buffer, &buffer_listener, buffer); + + zwp_linux_buffer_params_v1_destroy(params); +} + +static void create_failed(void * data, struct zwp_linux_buffer_params_v1 * params) +{ + struct buffer * buffer = data; + + buffer->buffer = NULL; + zwp_linux_buffer_params_v1_destroy(params); + LV_LOG_ERROR("Failed to create dmabuf buffer\n"); +} + +static const struct zwp_linux_buffer_params_v1_listener params_listener = {.created = create_succeeded, + .failed = create_failed +}; + +lv_result_t lv_wayland_dmabuf_resize_window(dmabuf_ctx_t * context, struct window * window) +{ + struct buffer * buffers = lv_wayland_dmabuf_create_draw_buffers_internal(window); + if(!buffers) { + return LV_RESULT_INVALID; + } + lv_wayland_dmabuf_destroy_draw_buffers(context, window); + + context->buffers = buffers; + lv_wayland_dmabuf_set_draw_buffers(context, window->lv_disp); + return LV_RESULT_OK; +} + +lv_result_t lv_wayland_dmabuf_create_draw_buffers(dmabuf_ctx_t * context, struct window * window) +{ + struct buffer * buffers = lv_wayland_dmabuf_create_draw_buffers_internal(window); + if(!buffers) { + return LV_RESULT_INVALID; + } + + context->buffers = buffers; + return LV_RESULT_OK; +} + +void lv_wayland_dmabuf_destroy_draw_buffers(dmabuf_ctx_t * context, struct window * window) +{ + LV_UNUSED(window); + if(context->buffers == NULL) { + return; + } + for(int i = 0; i < LV_WAYLAND_BUF_COUNT; i++) { + buffer_free(&context->buffers[i]); + return; + } + free(context->buffers); + context->buffers = NULL; +} + +static struct buffer * lv_wayland_dmabuf_create_draw_buffers_internal(struct window * window) +{ + const uint32_t flags = 0; + struct zwp_linux_buffer_params_v1 * params; + const int stride = lv_draw_buf_width_to_stride(window->width, lv_display_get_color_format(window->lv_disp)); + struct buffer * buffers = (struct buffer *)calloc(LV_WAYLAND_BUF_COUNT, sizeof(struct buffer)); + LV_ASSERT_MALLOC(buffers); + + for(int i = 0; i < LV_WAYLAND_BUF_COUNT; i++) { + uint32_t drmcf = 0; + + buffers[i].window = window; + buffers[i].lv_draw_buf = + lv_draw_buf_create(window->width, window->height, lv_display_get_color_format(window->lv_disp), stride); + buffers[i].strides[0] = stride; + buffers[i].dmabuf_fds[0] = g2d_get_buf_fd(buffers[i].lv_draw_buf); + buffers[i].buf_base[0] = buffers[i].lv_draw_buf->data; + params = zwp_linux_dmabuf_v1_create_params(window->wl_ctx->dmabuf_ctx.handler); + + switch(lv_display_get_color_format(window->lv_disp)) { + case LV_COLOR_FORMAT_XRGB8888: + drmcf = DRM_FORMAT_XRGB8888; + break; + case LV_COLOR_FORMAT_ARGB8888: + drmcf = DRM_FORMAT_ARGB8888; + break; + case LV_COLOR_FORMAT_RGB565: + drmcf = DRM_FORMAT_RGB565; + break; + default: + drmcf = DRM_FORMAT_ARGB8888; + } + + zwp_linux_buffer_params_v1_add(params, buffers[i].dmabuf_fds[0], 0, buffers[i].offsets[0], buffers[i].strides[0], 0, + 0); + + zwp_linux_buffer_params_v1_add_listener(params, ¶ms_listener, &buffers[i]); + zwp_linux_buffer_params_v1_create(params, window->width, window->height, drmcf, flags); + } + + wl_display_roundtrip(lv_wl_ctx.display); + + return buffers; +} + +static void buffer_free(struct buffer * buf) +{ + if(buf->buffer) wl_buffer_destroy(buf->buffer); + + if(buf->lv_draw_buf) lv_draw_buf_destroy(buf->lv_draw_buf); +} + +static void dmabuf_modifiers(void * data, struct zwp_linux_dmabuf_v1 * zwp_linux_dmabuf, uint32_t format, + uint32_t modifier_hi, uint32_t modifier_lo) +{ + LV_UNUSED(modifier_hi); + LV_UNUSED(modifier_lo); + dmabuf_format(data, zwp_linux_dmabuf, format); +} + +static void dmabuf_format(void * data, struct zwp_linux_dmabuf_v1 * zwp_linux_dmabuf, uint32_t format) +{ + dmabuf_ctx_t * ctx = data; + + LV_UNUSED(zwp_linux_dmabuf); + + if(LV_COLOR_DEPTH == 32 && format == DRM_FORMAT_ARGB8888) { + + /* Wayland compositors MUST support ARGB8888 */ + ctx->format = format; + + } + else if(LV_COLOR_DEPTH == 32 && format == DRM_FORMAT_XRGB8888 && ctx->format != DRM_FORMAT_ARGB8888) { + /* Select XRGB only if the compositor doesn't support transprancy */ + ctx->format = format; + + } + else if(LV_COLOR_DEPTH == 16 && format == DRM_FORMAT_RGB565) { + ctx->format = format; + } +} + +static struct buffer * dmabuf_acquire_buffer(dmabuf_ctx_t * context, unsigned char * color_p) +{ + + for(int i = 0; i < LV_WAYLAND_BUF_COUNT; i++) { + struct buffer * buffer = &context->buffers[i]; + if(buffer->buf_base[0] == color_p && buffer->busy == 0) { + return buffer; + } + } + + while(1) { + wl_display_roundtrip(lv_wl_ctx.display); + + for(int i = 0; i < LV_WAYLAND_BUF_COUNT; i++) { + struct buffer * buffer = &context->buffers[i]; + if(buffer->buf_base[0] == color_p && buffer->busy == 0) { + return buffer; + } + } + } + + return NULL; +} + +#endif /* LV_WAYLAND_DMABUF */ diff --git a/src/drivers/wayland/lv_wl_keyboard.c b/src/drivers/wayland/lv_wl_keyboard.c new file mode 100644 index 0000000000..27b589271a --- /dev/null +++ b/src/drivers/wayland/lv_wl_keyboard.c @@ -0,0 +1,301 @@ +/** + * @file lv_wl_keyboard.c + * + */ + +#include "lv_wl_keyboard.h" + +#if LV_USE_WAYLAND + +#include "lv_wayland_private.h" +#include +#include +#include +#include +#include "../../misc/lv_log.h" + +/********************* + * INCLUDES + *********************/ + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void keyboard_read(lv_indev_t * drv, lv_indev_data_t * data); + +static void keyboard_handle_keymap(void * data, struct wl_keyboard * keyboard, uint32_t format, int fd, uint32_t size); +static void keyboard_handle_enter(void * data, struct wl_keyboard * keyboard, uint32_t serial, + struct wl_surface * surface, struct wl_array * keys); +static void keyboard_handle_leave(void * data, struct wl_keyboard * keyboard, uint32_t serial, + struct wl_surface * surface); + +static void keyboard_handle_modifiers(void * data, struct wl_keyboard * keyboard, uint32_t serial, + uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, + uint32_t group); + +static void keyboard_handle_key(void * data, struct wl_keyboard * keyboard, uint32_t serial, uint32_t time, + uint32_t key, uint32_t state); + +static lv_key_t keycode_xkb_to_lv(xkb_keysym_t xkb_key); + +/********************** + * STATIC VARIABLES + **********************/ + +static const struct wl_keyboard_listener keyboard_listener = { + .keymap = keyboard_handle_keymap, + .enter = keyboard_handle_enter, + .leave = keyboard_handle_leave, + .key = keyboard_handle_key, + .modifiers = keyboard_handle_modifiers, +}; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_indev_t * lv_wayland_keyboard_create(void) +{ + + lv_indev_t * keyboard = lv_indev_create(); + lv_indev_set_type(keyboard, LV_INDEV_TYPE_KEYPAD); + lv_indev_set_read_cb(keyboard, keyboard_read); + + return keyboard; +} + +lv_indev_t * lv_wayland_get_keyboard(lv_display_t * display) +{ + struct window * window = lv_display_get_user_data(display); + if(!window) { + return NULL; + } + return window->lv_indev_keyboard; +} + +/********************** + * PRIVATE FUNCTIONS + **********************/ + +const struct wl_keyboard_listener * lv_wayland_keyboard_get_listener(void) +{ + return &keyboard_listener; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void keyboard_read(lv_indev_t * drv, lv_indev_data_t * data) +{ + struct window * window = lv_display_get_user_data(lv_indev_get_display(drv)); + if(!window || window->closed) { + return; + } + + data->key = window->body->input.keyboard.key; + data->state = window->body->input.keyboard.state; +} + +static void keyboard_handle_keymap(void * data, struct wl_keyboard * keyboard, uint32_t format, int fd, uint32_t size) +{ + struct lv_wayland_context * app = data; + + struct xkb_keymap * keymap; + struct xkb_state * state; + char * map_str; + + LV_UNUSED(keyboard); + + if(format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { + close(fd); + return; + } + + map_str = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + if(map_str == MAP_FAILED) { + close(fd); + return; + } + + /* Set up XKB keymap */ + keymap = xkb_keymap_new_from_string(app->xkb_context, map_str, XKB_KEYMAP_FORMAT_TEXT_V1, 0); + munmap(map_str, size); + close(fd); + + if(!keymap) { + LV_LOG_ERROR("failed to compile keymap"); + return; + } + + /* Set up XKB state */ + state = xkb_state_new(keymap); + if(!state) { + LV_LOG_ERROR("failed to create XKB state"); + xkb_keymap_unref(keymap); + return; + } + + xkb_keymap_unref(app->seat.xkb.keymap); + xkb_state_unref(app->seat.xkb.state); + app->seat.xkb.keymap = keymap; + app->seat.xkb.state = state; +} + +static void keyboard_handle_enter(void * data, struct wl_keyboard * keyboard, uint32_t serial, + struct wl_surface * surface, struct wl_array * keys) +{ + struct lv_wayland_context * app = data; + + LV_UNUSED(keyboard); + LV_UNUSED(serial); + LV_UNUSED(keys); + + if(!surface) { + app->keyboard_obj = NULL; + } + else { + app->keyboard_obj = wl_surface_get_user_data(surface); + } +} + +static void keyboard_handle_leave(void * data, struct wl_keyboard * keyboard, uint32_t serial, + struct wl_surface * surface) +{ + struct lv_wayland_context * app = data; + + LV_UNUSED(serial); + LV_UNUSED(keyboard); + + if(!surface || (app->keyboard_obj == wl_surface_get_user_data(surface))) { + app->keyboard_obj = NULL; + } +} + +static void keyboard_handle_key(void * data, struct wl_keyboard * keyboard, uint32_t serial, uint32_t time, + uint32_t key, uint32_t state) +{ + struct lv_wayland_context * app = data; + const uint32_t code = (key + 8); + const xkb_keysym_t * syms; + xkb_keysym_t sym = XKB_KEY_NoSymbol; + + LV_UNUSED(serial); + LV_UNUSED(time); + LV_UNUSED(keyboard); + + if(!app->keyboard_obj || !app->seat.xkb.state) { + return; + } + + if(xkb_state_key_get_syms(app->seat.xkb.state, code, &syms) == 1) { + sym = syms[0]; + } + + const lv_key_t lv_key = keycode_xkb_to_lv(sym); + const lv_indev_state_t lv_state = + (state == WL_KEYBOARD_KEY_STATE_PRESSED) ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED; + + if(lv_key != 0) { + app->keyboard_obj->input.keyboard.key = lv_key; + app->keyboard_obj->input.keyboard.state = lv_state; + } +} + +static void keyboard_handle_modifiers(void * data, struct wl_keyboard * keyboard, uint32_t serial, + uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, + uint32_t group) +{ + struct lv_wayland_context * app = data; + + LV_UNUSED(serial); + LV_UNUSED(keyboard); + + /* If we're not using a keymap, then we don't handle PC-style modifiers */ + if(!app->seat.xkb.keymap) { + return; + } + + xkb_state_update_mask(app->seat.xkb.state, mods_depressed, mods_latched, mods_locked, 0, 0, group); +} +static lv_key_t keycode_xkb_to_lv(xkb_keysym_t xkb_key) +{ + lv_key_t key = 0; + + if(((xkb_key >= XKB_KEY_space) && (xkb_key <= XKB_KEY_asciitilde))) { + key = xkb_key; + } + else if(((xkb_key >= XKB_KEY_KP_0) && (xkb_key <= XKB_KEY_KP_9))) { + key = (xkb_key & 0x003f); + } + else { + switch(xkb_key) { + case XKB_KEY_BackSpace: + key = LV_KEY_BACKSPACE; + break; + case XKB_KEY_Return: + case XKB_KEY_KP_Enter: + key = LV_KEY_ENTER; + break; + case XKB_KEY_Escape: + key = LV_KEY_ESC; + break; + case XKB_KEY_Delete: + case XKB_KEY_KP_Delete: + key = LV_KEY_DEL; + break; + case XKB_KEY_Home: + case XKB_KEY_KP_Home: + key = LV_KEY_HOME; + break; + case XKB_KEY_Left: + case XKB_KEY_KP_Left: + key = LV_KEY_LEFT; + break; + case XKB_KEY_Up: + case XKB_KEY_KP_Up: + key = LV_KEY_UP; + break; + case XKB_KEY_Right: + case XKB_KEY_KP_Right: + key = LV_KEY_RIGHT; + break; + case XKB_KEY_Down: + case XKB_KEY_KP_Down: + key = LV_KEY_DOWN; + break; + case XKB_KEY_Prior: + case XKB_KEY_KP_Prior: + key = LV_KEY_PREV; + break; + case XKB_KEY_Next: + case XKB_KEY_KP_Next: + case XKB_KEY_Tab: + case XKB_KEY_KP_Tab: + key = LV_KEY_NEXT; + break; + case XKB_KEY_End: + case XKB_KEY_KP_End: + key = LV_KEY_END; + break; + default: + break; + } + } + return key; +} + +#endif /* LV_WAYLAND */ diff --git a/src/drivers/wayland/lv_wl_keyboard.h b/src/drivers/wayland/lv_wl_keyboard.h new file mode 100644 index 0000000000..86296d4484 --- /dev/null +++ b/src/drivers/wayland/lv_wl_keyboard.h @@ -0,0 +1,52 @@ +/** + * @file lv_wl_keyboard.h + * + */ + +#ifndef LV_WL_KEYBOARD_H +#define LV_WL_KEYBOARD_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../indev/lv_indev.h" +#include "../../indev/lv_indev_gesture.h" +#if LV_USE_WAYLAND + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +lv_indev_t * lv_wayland_keyboard_create(void); + +/** + * Get keyboard input device for given LVGL display + * @param display LVGL display + * @return input device connected to keyboard, or NULL on error + */ +lv_indev_t * lv_wayland_get_keyboard(lv_display_t * display); + +/********************** + * MACROS + **********************/ + +#endif /* LV_USE_WAYLAND */ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_WL_KEYBOARD_H*/ diff --git a/src/drivers/wayland/lv_wl_pointer.c b/src/drivers/wayland/lv_wl_pointer.c new file mode 100644 index 0000000000..e406d47e21 --- /dev/null +++ b/src/drivers/wayland/lv_wl_pointer.c @@ -0,0 +1,245 @@ +/** + * @file lv_wl_pointer.c + * + */ + +#include "lv_wl_pointer.h" + +#if LV_USE_WAYLAND + +#include +#include +#include +#include +#include +#include +#include "lv_wayland_private.h" + +/********************* + * INCLUDES + *********************/ + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void _lv_wayland_pointer_read(lv_indev_t * drv, lv_indev_data_t * data); + +static void pointer_handle_enter(void * data, struct wl_pointer * pointer, uint32_t serial, struct wl_surface * surface, + wl_fixed_t sx, wl_fixed_t sy); + +static void pointer_handle_leave(void * data, struct wl_pointer * pointer, uint32_t serial, + struct wl_surface * surface); + +static void pointer_handle_motion(void * data, struct wl_pointer * pointer, uint32_t time, wl_fixed_t sx, + wl_fixed_t sy); + +static void pointer_handle_button(void * data, struct wl_pointer * wl_pointer, uint32_t serial, uint32_t time, + uint32_t button, uint32_t state); + +static void pointer_handle_axis(void * data, struct wl_pointer * wl_pointer, uint32_t time, uint32_t axis, + wl_fixed_t value); + +/********************** + * STATIC VARIABLES + **********************/ + +static const struct wl_pointer_listener pointer_listener = { + .enter = pointer_handle_enter, + .leave = pointer_handle_leave, + .motion = pointer_handle_motion, + .button = pointer_handle_button, + .axis = pointer_handle_axis, +}; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_indev_t * lv_wayland_pointer_create(void) +{ + lv_indev_t * indev = lv_indev_create(); + lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER); + lv_indev_set_read_cb(indev, _lv_wayland_pointer_read); + return indev; +} + +lv_indev_t * lv_wayland_get_pointer(lv_display_t * disp) +{ + struct window * window = lv_display_get_user_data(disp); + if(!window) { + return NULL; + } + return window->lv_indev_pointer; +} + +/********************** + * PRIVATE FUNCTIONS + **********************/ + +const struct wl_pointer_listener * lv_wayland_pointer_get_listener(void) +{ + return &pointer_listener; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void _lv_wayland_pointer_read(lv_indev_t * drv, lv_indev_data_t * data) +{ + struct window * window = lv_display_get_user_data(lv_indev_get_display(drv)); + + if(!window || window->closed) { + return; + } + + data->point.x = window->body->input.pointer.x; + data->point.y = window->body->input.pointer.y; + data->state = window->body->input.pointer.left_button; +} + +static void pointer_handle_enter(void * data, struct wl_pointer * pointer, uint32_t serial, struct wl_surface * surface, + wl_fixed_t sx, wl_fixed_t sy) +{ + struct lv_wayland_context * app = data; + const char * cursor = LV_WAYLAND_DEFAULT_CURSOR_NAME; + int pos_x = wl_fixed_to_int(sx); + int pos_y = wl_fixed_to_int(sy); + + if(!surface) { + app->pointer_obj = NULL; + return; + } + + app->pointer_obj = wl_surface_get_user_data(surface); + + app->pointer_obj->input.pointer.x = pos_x; + app->pointer_obj->input.pointer.y = pos_y; + +#if LV_WAYLAND_XDG_SHELL + cursor = lv_wayland_xdg_shell_get_cursor_name(app); +#endif + + if(app->cursor_surface) { + struct wl_cursor_image * cursor_image = wl_cursor_theme_get_cursor(app->cursor_theme, cursor)->images[0]; + wl_pointer_set_cursor(pointer, serial, app->cursor_surface, cursor_image->hotspot_x, cursor_image->hotspot_y); + wl_surface_attach(app->cursor_surface, wl_cursor_image_get_buffer(cursor_image), 0, 0); + wl_surface_damage(app->cursor_surface, 0, 0, cursor_image->width, cursor_image->height); + wl_surface_commit(app->cursor_surface); + app->cursor_flush_pending = true; + } +} + +static void pointer_handle_leave(void * data, struct wl_pointer * pointer, uint32_t serial, struct wl_surface * surface) +{ + struct lv_wayland_context * app = data; + + LV_UNUSED(pointer); + LV_UNUSED(serial); + + if(!surface || (app->pointer_obj == wl_surface_get_user_data(surface))) { + app->pointer_obj = NULL; + } +} + +static void pointer_handle_motion(void * data, struct wl_pointer * pointer, uint32_t time, wl_fixed_t sx, wl_fixed_t sy) +{ + struct lv_wayland_context * app = data; + + LV_UNUSED(pointer); + LV_UNUSED(time); + + if(!app->pointer_obj) { + return; + } + + app->pointer_obj->input.pointer.x = LV_MAX(0, LV_MIN(wl_fixed_to_int(sx), app->pointer_obj->width - 1)); + app->pointer_obj->input.pointer.y = LV_MAX(0, LV_MIN(wl_fixed_to_int(sy), app->pointer_obj->height - 1)); +} + +static void pointer_handle_button(void * data, struct wl_pointer * wl_pointer, uint32_t serial, uint32_t time, + uint32_t button, uint32_t state) +{ + struct lv_wayland_context * app = data; + + LV_UNUSED(serial); + LV_UNUSED(wl_pointer); + LV_UNUSED(time); + + const lv_indev_state_t lv_state = + (state == WL_POINTER_BUTTON_STATE_PRESSED) ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED; + + if(!app->pointer_obj) { + return; + } + struct window * window = app->pointer_obj->window; + +#if LV_WAYLAND_WL_SHELL + lv_wayland_wl_shell_handle_pointer_event(app, serial, button, state); +#elif LV_WAYLAND_XDG_SHELL + lv_wayland_xdg_shell_handle_pointer_event(app, serial, button, state); +#endif + + switch(app->pointer_obj->type) { + case OBJECT_WINDOW: + switch(button) { + case BTN_LEFT: + app->pointer_obj->input.pointer.left_button = lv_state; + break; + case BTN_RIGHT: + app->pointer_obj->input.pointer.right_button = lv_state; + break; + case BTN_MIDDLE: + app->pointer_obj->input.pointer.wheel_button = lv_state; + break; + default: + break; + } + break; + case OBJECT_BUTTON_CLOSE: + if((button == BTN_LEFT) && (state == WL_POINTER_BUTTON_STATE_RELEASED)) { + window->shall_close = true; + } + break; + default: + break; + } +} + +static void pointer_handle_axis(void * data, struct wl_pointer * wl_pointer, uint32_t time, uint32_t axis, + wl_fixed_t value) +{ + struct lv_wayland_context * app = data; + const int diff = wl_fixed_to_int(value); + + LV_UNUSED(time); + LV_UNUSED(wl_pointer); + + if(!app->pointer_obj) { + return; + } + + if(axis == 0) { + if(diff > 0) { + app->pointer_obj->input.pointer.wheel_diff++; + } + else if(diff < 0) { + app->pointer_obj->input.pointer.wheel_diff--; + } + } +} + +#endif /* LV_USE_WAYLAND */ diff --git a/src/drivers/wayland/lv_wl_pointer.h b/src/drivers/wayland/lv_wl_pointer.h new file mode 100644 index 0000000000..265394c775 --- /dev/null +++ b/src/drivers/wayland/lv_wl_pointer.h @@ -0,0 +1,54 @@ + +/** + * @file lv_wl_pointer.h + * + */ + +#ifndef LV_WL_POINTER_H +#define LV_WL_POINTER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../indev/lv_indev.h" +#include "../../indev/lv_indev_gesture.h" +#if LV_USE_WAYLAND + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +lv_indev_t * lv_wayland_pointer_create(void); + +/** + * Obtains the input device of the mouse pointer + * @note It is used to create an input group on application start + * @param disp Reference to the LVGL display associated to the window + * @return The input device + */ +lv_indev_t * lv_wayland_get_pointer(lv_display_t * disp); + +/********************** + * MACROS + **********************/ + +#endif /* LV_USE_WAYLAND */ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_WL_POINTER_H*/ diff --git a/src/drivers/wayland/lv_wl_pointer_axis.c b/src/drivers/wayland/lv_wl_pointer_axis.c new file mode 100644 index 0000000000..f61bbe7942 --- /dev/null +++ b/src/drivers/wayland/lv_wl_pointer_axis.c @@ -0,0 +1,79 @@ +/** + * @file lv_wl_pointer_axis.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_wl_pointer_axis.h" + +#if LV_USE_WAYLAND + +#include "lv_wayland_private.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void pointeraxis_read(lv_indev_t * drv, lv_indev_data_t * data); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_indev_t * lv_wayland_pointer_axis_create(void) +{ + + lv_indev_t * indev = lv_indev_create(); + lv_indev_set_type(indev, LV_INDEV_TYPE_ENCODER); + lv_indev_set_read_cb(indev, pointeraxis_read); + + return indev; +} + +lv_indev_t * lv_wayland_get_pointeraxis(lv_display_t * display) +{ + struct window * window = lv_display_get_user_data(display); + if(!window) { + return NULL; + } + return window->lv_indev_pointeraxis; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void pointeraxis_read(lv_indev_t * drv, lv_indev_data_t * data) +{ + struct window * window = lv_display_get_user_data(lv_indev_get_display(drv)); + + if(!window || window->closed) { + return; + } + + data->state = window->body->input.pointer.wheel_button; + data->enc_diff = window->body->input.pointer.wheel_diff; + + window->body->input.pointer.wheel_diff = 0; +} + +#endif /* LV_USE_WAYLAND */ diff --git a/src/drivers/wayland/lv_wl_pointer_axis.h b/src/drivers/wayland/lv_wl_pointer_axis.h new file mode 100644 index 0000000000..d3564a5047 --- /dev/null +++ b/src/drivers/wayland/lv_wl_pointer_axis.h @@ -0,0 +1,54 @@ +/** + * @file lv_wl_pointer_axis.h + * + */ + +#ifndef LV_WL_POINTER_AXIS_H +#define LV_WL_POINTER_AXIS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "../../indev/lv_indev.h" +#include "../../indev/lv_indev_gesture.h" + +#if LV_USE_WAYLAND + +/********************* + * INCLUDES + *********************/ + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +lv_indev_t * lv_wayland_pointer_axis_create(void); + +/** + * Obtains the input device of the encoder + * @note It is used to create an input group on application start + * @param display Reference to the LVGL display associated to the window + * @return The input device + */ +lv_indev_t * lv_wayland_get_pointeraxis(lv_display_t * display); + +/********************** + * MACROS + **********************/ + +#endif /* LV_USE_WAYLAND */ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_WL_POINTER_AXIS_H*/ diff --git a/src/drivers/wayland/lv_wl_seat.c b/src/drivers/wayland/lv_wl_seat.c new file mode 100644 index 0000000000..e4176553c1 --- /dev/null +++ b/src/drivers/wayland/lv_wl_seat.c @@ -0,0 +1,100 @@ +/** + * @file lv_wl_seat.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_wayland.h" + +#if LV_USE_WAYLAND + +#include "lv_wayland_private.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void seat_handle_capabilities(void * data, struct wl_seat * wl_seat, enum wl_seat_capability caps); + +/********************** + * STATIC VARIABLES + **********************/ + +static const struct wl_seat_listener seat_listener = { + .capabilities = seat_handle_capabilities, +}; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/********************** + * PRIVATE FUNCTIONS + **********************/ + +const struct wl_seat_listener * lv_wayland_seat_get_listener(void) +{ + return &seat_listener; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void seat_handle_capabilities(void * data, struct wl_seat * wl_seat, enum wl_seat_capability caps) +{ + struct lv_wayland_context * app = data; + struct seat * seat = &app->seat; + + if((caps & WL_SEAT_CAPABILITY_POINTER) && !seat->wl_pointer) { + seat->wl_pointer = wl_seat_get_pointer(wl_seat); + wl_pointer_add_listener(seat->wl_pointer, lv_wayland_pointer_get_listener(), app); + app->cursor_surface = wl_compositor_create_surface(app->compositor); + if(!app->cursor_surface) { + LV_LOG_WARN("failed to create cursor surface"); + } + } + else if(!(caps & WL_SEAT_CAPABILITY_POINTER) && seat->wl_pointer) { + wl_pointer_destroy(seat->wl_pointer); + if(app->cursor_surface) { + wl_surface_destroy(app->cursor_surface); + } + seat->wl_pointer = NULL; + } + + if((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !seat->wl_keyboard) { + seat->wl_keyboard = wl_seat_get_keyboard(wl_seat); + wl_keyboard_add_listener(seat->wl_keyboard, lv_wayland_keyboard_get_listener(), app); + } + else if(!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && seat->wl_keyboard) { + wl_keyboard_destroy(seat->wl_keyboard); + seat->wl_keyboard = NULL; + } + +#if LV_USE_GESTURE_RECOGNITION + if((caps & WL_SEAT_CAPABILITY_TOUCH) && !seat->wl_touch) { + seat->wl_touch = wl_seat_get_touch(wl_seat); + wl_touch_add_listener(seat->wl_touch, lv_wayland_touch_get_listener(), app); + } +#endif + else if(!(caps & WL_SEAT_CAPABILITY_TOUCH) && seat->wl_touch) { + wl_touch_destroy(seat->wl_touch); + seat->wl_touch = NULL; + } +} + +#endif /* LV_USE_WAYLAND */ diff --git a/src/drivers/wayland/lv_wl_shell.c b/src/drivers/wayland/lv_wl_shell.c new file mode 100644 index 0000000000..5f3b24941d --- /dev/null +++ b/src/drivers/wayland/lv_wl_shell.c @@ -0,0 +1,184 @@ +/** + * @file lv_wl_shell.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_wayland.h" + +#if LV_WAYLAND_WL_SHELL + +/* WL_SHELL has been deprecated for 3 years now */ +#warning LV_WAYLAND_WL_SHELL is deprecated and will be removed in a future release + +#include "lv_wayland_private.h" +#include + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void wl_shell_handle_ping(void * data, struct wl_shell_surface * shell_surface, uint32_t serial); +static void wl_shell_handle_configure(void * data, struct wl_shell_surface * shell_surface, uint32_t edges, + int32_t width, int32_t height); + +/********************** + * STATIC VARIABLES + **********************/ + +static const struct wl_shell_surface_listener shell_surface_listener = { + .ping = wl_shell_handle_ping, + .configure = wl_shell_handle_configure, +}; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/********************** + * PRIVATE FUNCTIONS + **********************/ + +void lv_wayland_wl_shell_deinit(void) +{ + if(lv_wl_ctx.wl_shell) { + wl_shell_destroy(lv_wl_ctx.wl_shell); + } +} + +const struct wl_shell_surface_listener * lv_wayland_wl_shell_get_listener(void) +{ + return &shell_surface_listener; +} + +lv_result_t lv_wayland_wl_shell_create_window(struct lv_wayland_context * ctx, struct window * window, + const char * title) +{ + if(!ctx->wl_shell) { + return LV_RESULT_INVALID; + } + + window->wl_shell_surface = wl_shell_get_shell_surface(ctx->wl_shell, window->body->surface); + if(!window->wl_shell_surface) { + LV_LOG_ERROR("cannot create WL shell surface"); + return LV_RESULT_INVALID; + } + + wl_shell_surface_add_listener(window->wl_shell_surface, lv_wayland_wl_shell_get_listener(), window); + wl_shell_surface_set_toplevel(window->wl_shell_surface); + wl_shell_surface_set_title(window->wl_shell_surface, title); + + /* For wl_shell, just draw the window, weston doesn't send it */ + lv_wayland_window_draw(window, window->width, window->height); + return LV_RESULT_OK; +} + +lv_result_t lv_wayland_wl_shell_set_maximized(struct window * window, bool maximized) +{ + + if(!window->wl_shell_surface) { + return LV_RESULT_INVALID; + } + if(maximized) { + LV_LOG_ERROR("WL_SHELL - Unsupported operation - Maximization"); + return LV_RESULT_INVALID; + } + else { + wl_shell_surface_set_toplevel(window->wl_shell_surface); + } + return LV_RESULT_OK; +} + +lv_result_t lv_wayland_wl_shell_set_minimized(struct window * window) +{ + LV_LOG_ERROR("WL_SHELL - Unsupported operation - Minization"); + return LV_RESULT_INVALID; +} +lv_result_t lv_wayland_wl_shell_set_fullscreen(struct window * window, bool fullscreen) +{ + if(!window->wl_shell_surface) { + return LV_RESULT_INVALID; + } + if(fullscreen) { + wl_shell_surface_set_fullscreen(window->wl_shell_surface, WL_SHELL_SURFACE_FULLSCREEN_METHOD_SCALE, 0, NULL); + } + else { + wl_shell_surface_set_toplevel(window->wl_shell_surface); + } + return LV_RESULT_OK; +} + +lv_result_t lv_wayland_wl_shell_destroy_window(struct window * window) +{ + if(!window->wl_shell_surface) { + return LV_RESULT_INVALID; + } + wl_shell_surface_destroy(window->wl_shell_surface); + return LV_RESULT_OK; +} + +void lv_wayland_wl_shell_handle_pointer_event(struct lv_wayland_context * ctx, uint32_t serial, uint32_t button, + uint32_t state) +{ + struct window * window = ctx->pointer_obj->window; + switch(ctx->pointer_obj->type) { + case OBJECT_TITLEBAR: + if((button == BTN_LEFT) && (state == WL_POINTER_BUTTON_STATE_PRESSED)) { + if(window->wl_shell_surface) { + wl_shell_surface_move(window->wl_shell_surface, ctx->wl_seat, serial); + window->flush_pending = true; + } + } + break; + case OBJECT_BUTTON_CLOSE: + case OBJECT_BORDER_TOP: + case OBJECT_BORDER_BOTTOM: + case OBJECT_BORDER_LEFT: + case OBJECT_BORDER_RIGHT: + case OBJECT_WINDOW: + break; + } +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void wl_shell_handle_ping(void * data, struct wl_shell_surface * shell_surface, uint32_t serial) +{ + return wl_shell_surface_pong(shell_surface, serial); +} + +static void wl_shell_handle_configure(void * data, struct wl_shell_surface * shell_surface, uint32_t edges, + int32_t width, int32_t height) +{ + struct window * window = (struct window *)data; + + LV_UNUSED(edges); + + if((width <= 0) || (height <= 0)) { + return; + } + else if((width != window->width) || (height != window->height)) { + window->resize_width = width; + window->resize_height = height; + window->resize_pending = true; + } +} + +#endif /* LV_WAYLAND_WL_SHELL */ diff --git a/src/drivers/wayland/lv_wl_shm.c b/src/drivers/wayland/lv_wl_shm.c new file mode 100644 index 0000000000..87eb547c6e --- /dev/null +++ b/src/drivers/wayland/lv_wl_shm.c @@ -0,0 +1,468 @@ +/** + * @file lv_wl_shm.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_wayland.h" +#if LV_USE_WAYLAND + +#include "lv_wayland_private.h" +#include +#include +#include +#include +#include + +/********************* + * DEFINES + *********************/ + +#define SHM_FORMAT_UNKNOWN 0xFFFFFF + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +/* + * shm_format + * @description called by the compositor to advertise the supported + * color formats for SHM buffers, there is a call per supported format + */ +static void shm_format(void * data, struct wl_shm * wl_shm, uint32_t format); +static void handle_wl_buffer_release(void * data, struct wl_buffer * wl_buffer); +static bool sme_new_pool(void * ctx, smm_pool_t * pool); +static void sme_expand_pool(void * ctx, smm_pool_t * pool); +static void sme_free_pool(void * ctx, smm_pool_t * pool); +static bool sme_new_buffer(void * ctx, smm_buffer_t * buf); +static bool sme_init_buffer(void * ctx, smm_buffer_t * buf); +static void sme_free_buffer(void * ctx, smm_buffer_t * buf); + +/********************** + * STATIC VARIABLES + **********************/ + +static const struct smm_events sme_events = {NULL, sme_new_pool, sme_expand_pool, sme_free_pool, + sme_new_buffer, sme_init_buffer, sme_free_buffer +}; + +static const struct wl_shm_listener shm_listener = {.format = shm_format}; + +static const struct wl_buffer_listener wl_buffer_listener = { + .release = handle_wl_buffer_release, +}; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/********************** + * PRIVATE FUNCTIONS + **********************/ + +void lv_wayland_shm_initalize_context(shm_ctx_t * context) +{ + memset(context, 0, sizeof(*context)); + context->format = SHM_FORMAT_UNKNOWN; + smm_init(&sme_events); + smm_setctx(context); +} + +void lv_wayland_shm_set_interface(shm_ctx_t * context, struct wl_registry * registry, uint32_t name, + const char * interface, uint32_t version) +{ + LV_UNUSED(version); + LV_UNUSED(interface); + context->handler = wl_registry_bind(registry, name, &wl_shm_interface, 1); + wl_shm_add_listener(context->handler, &shm_listener, context); +} + +lv_result_t lv_wayland_shm_is_ready(shm_ctx_t * context) +{ + return (context->handler && context->format != SHM_FORMAT_UNKNOWN) ? LV_RESULT_OK : LV_RESULT_INVALID; +} + +/* TODO: Move all cursor functions to a lv_wl_cursor file*/ +struct wl_cursor_theme * lv_wayland_shm_load_cursor_theme(shm_ctx_t * context) +{ + return wl_cursor_theme_load(NULL, 32, context->handler); +} + +void lv_wayland_shm_deinit(shm_ctx_t * context) +{ + smm_deinit(); + wl_shm_destroy(context->handler); +} + +struct graphic_object * lv_wayland_shm_on_graphical_object_creation(shm_ctx_t * context, struct graphic_object * obj) +{ + LV_UNUSED(context); + obj->pending_buffer = NULL; + obj->buffer_group = smm_create(); + if(obj->buffer_group == NULL) { + LV_LOG_ERROR("Failed to create buffer group for graphic object"); + lv_free(obj); + return NULL; + } + SMM_TAG(obj->buffer_group, TAG_LOCAL, obj); + return obj; +} +void lv_wayland_shm_on_graphical_object_destruction(shm_ctx_t * context, struct graphic_object * obj) +{ + LV_UNUSED(context); + smm_destroy(obj->buffer_group); +} + +lv_result_t lv_wayland_shm_resize_window(shm_ctx_t * context, struct window * window, int32_t width, int32_t height) +{ + const uint8_t bpp = lv_color_format_get_size(LV_COLOR_FORMAT_NATIVE); + + /* Update size for newly allocated buffers */ + smm_resize(window->body->buffer_group, ((width * bpp) * height) * 2); + + window->body->width = width; + window->body->height = height; + + /* Pre-allocate two buffers for the window body here */ + struct smm_buffer_t * body_buf1 = smm_acquire(window->body->buffer_group); + struct smm_buffer_t * body_buf2 = smm_acquire(window->body->buffer_group); + + if(smm_map(body_buf2) == NULL) { + LV_LOG_ERROR("Cannot pre-allocate backing buffers for window body"); + wl_surface_destroy(window->body->surface); + return LV_RESULT_INVALID; + } + + /* Moves the buffers to the the unused list of the group */ + smm_release(body_buf1); + smm_release(body_buf2); + + LV_LOG_TRACE("resize window:%dx%d body:%dx%d frame: %d", window->width, window->height, window->body->width, + window->body->height, window->frame_counter); + + width = window->body->width; + height = window->body->height; + + if(window->lv_disp != NULL) { + /* Resize draw buffer */ + const uint32_t stride = lv_draw_buf_width_to_stride(width, lv_display_get_color_format(window->lv_disp)); + context->lv_draw_buf = lv_draw_buf_reshape(context->lv_draw_buf, lv_display_get_color_format(window->lv_disp), + width, height / LVGL_DRAW_BUFFER_DIV, stride); + } + + return LV_RESULT_OK; +} +lv_result_t lv_wayland_shm_create_draw_buffers(shm_ctx_t * context, struct window * window) +{ + + const uint32_t stride = lv_draw_buf_width_to_stride(window->width, lv_display_get_color_format(window->lv_disp)); + + context->lv_draw_buf = lv_draw_buf_create(window->width, window->height / LVGL_DRAW_BUFFER_DIV, + lv_display_get_color_format(window->lv_disp), stride); + return LV_RESULT_OK; +} + +lv_result_t lv_wayland_shm_set_draw_buffers(shm_ctx_t * context, lv_display_t * display) +{ + if(LV_WAYLAND_BUF_COUNT != 1) { + LV_LOG_ERROR("Wayland without dmabuf only supports 1 drawbuffer for now."); + return LV_RESULT_INVALID; + } + lv_display_set_draw_buffers(display, context->lv_draw_buf, NULL); + return LV_RESULT_OK; +} + +void lv_wayland_shm_delete_draw_buffers(shm_ctx_t * context, struct window * window) +{ + LV_UNUSED(window); + lv_draw_buf_destroy(context->lv_draw_buf); +} +void lv_wayland_shm_flush_partial_mode(lv_display_t * disp, const lv_area_t * area, unsigned char * color_p) +{ + struct window * window = lv_display_get_user_data(disp); + uint32_t format = window->wl_ctx->shm_ctx.format; + smm_buffer_t * buf = window->body->pending_buffer; + int32_t src_width = lv_area_get_width(area); + int32_t src_height = lv_area_get_height(area); + uint8_t bpp = lv_color_format_get_size(LV_COLOR_FORMAT_NATIVE); + lv_display_rotation_t rot = lv_display_get_rotation(disp); + int32_t w = lv_display_get_horizontal_resolution(disp); + int32_t h = lv_display_get_vertical_resolution(disp); + + /* TODO actually test what happens if the rotation is 90 or 270 or 180 ? */ + int32_t hres = (rot == LV_DISPLAY_ROTATION_0) ? w : h; + int32_t vres = (rot == LV_DISPLAY_ROTATION_0) ? h : w; + + /* If window has been / is being closed, or is not visible, skip flush */ + if(window->closed || window->shall_close) { + goto skip; + } + /* Skip if the area is out the screen */ + else if((area->x2 < 0) || (area->y2 < 0) || (area->x1 > hres - 1) || (area->y1 > vres - 1)) { + goto skip; + } + + /* Acquire and map a buffer to attach/commit to surface */ + if(buf == NULL) { + 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); + } + + void * buf_base = smm_map(buf); + if(buf_base == NULL) { + LV_LOG_ERROR("cannot map in window body buffer"); + goto skip; + } + + /* Modify specified area in buffer */ + for(int32_t y = 0; y < src_height; ++y) { + if(format == WL_SHM_FORMAT_ARGB8888) { + for(int32_t x = 0; x < src_width; ++x) { + lv_color_premultiply((lv_color32_t *)color_p + x); + } + } + memcpy(((char *)buf_base) + ((((area->y1 + y) * hres) + area->x1) * bpp), color_p, src_width * bpp); + color_p += src_width * bpp; + } + + /* Mark surface damage */ + wl_surface_damage(window->body->surface, area->x1, area->y1, src_width, src_height); + + lv_wayland_cache_add_area(window, buf, area); + + if(lv_display_flush_is_last(disp)) { + /* Finally, attach buffer and commit to surface */ + struct wl_buffer * 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; + + struct wl_callback * cb = wl_surface_frame(window->body->surface); + wl_callback_add_listener(cb, lv_wayland_window_get_wl_surface_frame_listener(), window->body); + LV_LOG_TRACE("last flush frame: %d", window->frame_counter); + + window->flush_pending = true; + /* Return early here, the lv_display_flush_ready will get called in the frame_listener callback */ + return; + } + lv_display_flush_ready(disp); + return; +skip: + if(buf != NULL) { + /* Cleanup any intermediate state (in the event that this flush being + * skipped is in the middle of a flush sequence) + */ + lv_wayland_cache_clear(window); + SMM_TAG(buf, TAG_BUFFER_DAMAGE, NULL); + smm_release(buf); + window->body->pending_buffer = NULL; + } +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void shm_format(void * data, struct wl_shm * wl_shm, uint32_t format) +{ + shm_ctx_t * shm_ctx = (shm_ctx_t *)data; + + LV_UNUSED(wl_shm); + + LV_LOG_TRACE("Supported color space fourcc.h code: %08X", format); + + if(LV_COLOR_DEPTH == 32 && format == WL_SHM_FORMAT_ARGB8888) { + LV_LOG_TRACE("Found WL_SHM_FORMAT_ARGB8888"); + + /* Wayland compositors MUST support ARGB8888 */ + shm_ctx->format = format; + + } + else if(LV_COLOR_DEPTH == 32 && format == WL_SHM_FORMAT_XRGB8888 && shm_ctx->format != WL_SHM_FORMAT_ARGB8888) { + + LV_LOG_TRACE("Found WL_SHM_FORMAT_XRGB8888"); + /* Select XRGB only if the compositor doesn't support transprancy */ + shm_ctx->format = format; + + } + else if(LV_COLOR_DEPTH == 16 && format == WL_SHM_FORMAT_RGB565) { + + shm_ctx->format = format; + } +} + +static void handle_wl_buffer_release(void * data, struct wl_buffer * wl_buffer) +{ + const struct smm_buffer_properties * props; + struct graphic_object * obj; + struct window * window; + smm_buffer_t * buf; + /* window is unused when LV_LOG level is not set to trace */ + LV_UNUSED(window); + LV_UNUSED(wl_buffer); + + buf = (smm_buffer_t *)data; + props = SMM_BUFFER_PROPERTIES(buf); + obj = SMM_GROUP_PROPERTIES(props->group)->tag[TAG_LOCAL]; + window = obj->window; + + LV_LOG_TRACE("releasing buffer %p wl_buffer %p w:%d h:%d frame: %d", (smm_buffer_t *)data, (void *)wl_buffer, + obj->width, obj->height, window->frame_counter); + smm_release((smm_buffer_t *)data); +} + +static bool sme_new_pool(void * ctx, smm_pool_t * pool) +{ + struct wl_shm_pool * wl_pool; + shm_ctx_t * shm_ctx = ctx; + const struct smm_pool_properties * props = SMM_POOL_PROPERTIES(pool); + + LV_UNUSED(ctx); + + wl_pool = wl_shm_create_pool(shm_ctx->handler, props->fd, props->size); + + 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); + + LV_UNUSED(ctx); + + wl_shm_pool_resize(props->tag[TAG_LOCAL], props->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]; + + LV_UNUSED(ctx); + + wl_shm_pool_destroy(wl_pool); +} + +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]; + shm_ctx_t * shm_ctx = (shm_ctx_t *)ctx; + struct graphic_object * obj = SMM_GROUP_PROPERTIES(props->group)->tag[TAG_LOCAL]; + uint8_t bpp; + + LV_LOG_TRACE("create new buffer of width %d height %d", obj->width, obj->height); + + bpp = lv_color_format_get_size(LV_COLOR_FORMAT_NATIVE); + wl_buf = + wl_shm_pool_create_buffer(wl_pool, props->offset, obj->width, obj->height, obj->width * bpp, shm_ctx->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 sme_init_buffer(void * ctx, smm_buffer_t * buf) +{ + 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]; + uint8_t bpp; + + LV_UNUSED(ctx); + + if(buf_base == NULL) { + LV_LOG_ERROR("cannot map in buffer to initialize"); + goto done; + } + + /* Determine if all subsequent buffers damage is recorded */ + for(src = smm_next(buf); src != NULL; src = smm_next(src)) { + if(SMM_BUFFER_PROPERTIES(src)->tag[TAG_BUFFER_DAMAGE] == NULL) { + dmg_missing = true; + break; + } + } + + bpp = lv_color_format_get_size(LV_COLOR_FORMAT_NATIVE); + + 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 * bpp) * 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; + } + + lv_wayland_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) { + lv_wayland_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]; + + LV_UNUSED(ctx); + + wl_buffer_destroy(wl_buf); +} + +#endif /* LV_USE_WAYLAND */ diff --git a/src/drivers/wayland/lv_wl_touch.c b/src/drivers/wayland/lv_wl_touch.c new file mode 100644 index 0000000000..ee18ec3ddc --- /dev/null +++ b/src/drivers/wayland/lv_wl_touch.c @@ -0,0 +1,275 @@ +/** + * @file lv_wl_touch.c + * + */ + +#include "lv_wl_touch.h" + +#if LV_USE_WAYLAND && LV_USE_GESTURE_RECOGNITION + +#include "lv_wayland_private.h" + +#include +#include + +/********************* + * INCLUDES + *********************/ + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void _lv_wayland_touch_read(lv_indev_t * drv, lv_indev_data_t * data); + +static void touch_handle_down(void * data, struct wl_touch * wl_touch, uint32_t serial, uint32_t time, + struct wl_surface * surface, int32_t id, wl_fixed_t x_w, wl_fixed_t y_w); + +static void touch_handle_up(void * data, struct wl_touch * wl_touch, uint32_t serial, uint32_t time, int32_t id); + +static void touch_handle_motion(void * data, struct wl_touch * wl_touch, uint32_t time, int32_t id, wl_fixed_t x_w, + wl_fixed_t y_w); + +static void touch_handle_frame(void * data, struct wl_touch * wl_touch); + +static void touch_handle_cancel(void * data, struct wl_touch * wl_touch); + +/********************** + * STATIC VARIABLES + **********************/ + +static const struct wl_touch_listener touch_listener = { + .down = touch_handle_down, + .up = touch_handle_up, + .motion = touch_handle_motion, + .frame = touch_handle_frame, + .cancel = touch_handle_cancel, +}; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_indev_t * lv_wayland_touch_create(void) +{ + + lv_indev_t * indev = lv_indev_create(); + lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER); + lv_indev_set_read_cb(indev, _lv_wayland_touch_read); + + return indev; +} + +lv_indev_t * lv_wayland_get_touchscreen(lv_display_t * display) +{ + struct window * window = lv_display_get_user_data(display); + if(!window) { + return NULL; + } + return window->lv_indev_touch; +} + +/********************** + * PRIVATE FUNCTIONS + **********************/ + +const struct wl_touch_listener * lv_wayland_touch_get_listener(void) +{ + return &touch_listener; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void _lv_wayland_touch_read(lv_indev_t * drv, lv_indev_data_t * data) +{ + + struct window * window = lv_display_get_user_data(lv_indev_get_display(drv)); + + if(!window || window->closed) { + return; + } + + /* Collect touches if there are any - send them to the gesture recognizer */ + lv_indev_gesture_recognizers_update(drv, &window->body->input.touches[0], window->body->input.touch_event_cnt); + + LV_LOG_TRACE("collected touch events: %d", window->body->input.touch_event_cnt); + + window->body->input.touch_event_cnt = 0; + + /* Set the gesture information, before returning to LVGL */ + lv_indev_gesture_recognizers_set_data(drv, data); +} + +static void touch_handle_down(void * data, struct wl_touch * wl_touch, uint32_t serial, uint32_t time, + struct wl_surface * surface, int32_t id, wl_fixed_t x_w, wl_fixed_t y_w) +{ + struct lv_wayland_context * app = data; + uint8_t i; + + LV_UNUSED(id); + LV_UNUSED(time); + LV_UNUSED(serial); + LV_UNUSED(wl_touch); + + if(!surface) { + app->touch_obj = NULL; + return; + } + + /* Create the touch down event */ + app->touch_obj = wl_surface_get_user_data(surface); + i = app->touch_obj->input.touch_event_cnt; + + app->touch_obj->input.touches[i].point.x = wl_fixed_to_int(x_w); + app->touch_obj->input.touches[i].point.y = wl_fixed_to_int(y_w); + app->touch_obj->input.touches[i].id = id; + app->touch_obj->input.touches[i].timestamp = time; + app->touch_obj->input.touches[i].state = LV_INDEV_STATE_PRESSED; + app->touch_obj->input.touch_event_cnt++; + +#if LV_WAYLAND_WINDOW_DECORATIONS + struct window * window = app->touch_obj->window; + switch(app->touch_obj->type) { + case OBJECT_TITLEBAR: +#if LV_WAYLAND_XDG_SHELL + if(window->xdg_toplevel) { + xdg_toplevel_move(window->xdg_toplevel, app->wl_seat, serial); + window->flush_pending = true; + } +#endif +#if LV_WAYLAND_WL_SHELL + if(window->wl_shell_surface) { + wl_shell_surface_move(window->wl_shell_surface, app->wl_seat, serial); + window->flush_pending = true; + } +#endif + break; + default: + break; + } +#endif +} + +static void touch_handle_up(void * data, struct wl_touch * wl_touch, uint32_t serial, uint32_t time, int32_t id) +{ + struct lv_wayland_context * app = data; + uint8_t i; + + LV_UNUSED(serial); + LV_UNUSED(time); + LV_UNUSED(id); + LV_UNUSED(wl_touch); + +#if LV_USE_GESTURE_RECOGNITION + /* Create a released event */ + i = app->touch_obj->input.touch_event_cnt; + + app->touch_obj->input.touches[i].point.x = 0; + app->touch_obj->input.touches[i].point.y = 0; + app->touch_obj->input.touches[i].id = id; + app->touch_obj->input.touches[i].timestamp = time; + app->touch_obj->input.touches[i].state = LV_INDEV_STATE_RELEASED; + + app->touch_obj->input.touch_event_cnt++; +#endif + +#if LV_WAYLAND_WINDOW_DECORATIONS + struct window * window = app->touch_obj->window; + switch(app->touch_obj->type) { + case OBJECT_BUTTON_CLOSE: + window->shall_close = true; + break; +#if LV_WAYLAND_XDG_SHELL + case OBJECT_BUTTON_MAXIMIZE: + if(window->xdg_toplevel) { + if(window->maximized) { + xdg_toplevel_unset_maximized(window->xdg_toplevel); + } + else { + xdg_toplevel_set_maximized(window->xdg_toplevel); + } + window->maximized ^= true; + } + break; + case OBJECT_BUTTON_MINIMIZE: + if(window->xdg_toplevel) { + xdg_toplevel_set_minimized(window->xdg_toplevel); + window->flush_pending = true; + } +#endif /* LV_WAYLAND_XDG_SHELL */ + default: + break; + } +#endif /* LV_WAYLAND_WINDOW_DECORATIONS */ +} + +static void touch_handle_motion(void * data, struct wl_touch * wl_touch, uint32_t time, int32_t id, wl_fixed_t x_w, + wl_fixed_t y_w) +{ + struct lv_wayland_context * app = data; + lv_indev_touch_data_t * touch; + lv_indev_touch_data_t * cur; + uint8_t i; + + LV_UNUSED(time); + LV_UNUSED(id); + LV_UNUSED(wl_touch); + + /* Update the contact point of the corresponding id with the latest coordinate */ + touch = &app->touch_obj->input.touches[0]; + cur = NULL; + + for(i = 0; i < app->touch_obj->input.touch_event_cnt; i++) { + if(touch->id == id) { + cur = touch; + } + touch++; + } + + if(cur == NULL) { + + i = app->touch_obj->input.touch_event_cnt; + app->touch_obj->input.touches[i].point.x = wl_fixed_to_int(x_w); + app->touch_obj->input.touches[i].point.y = wl_fixed_to_int(y_w); + app->touch_obj->input.touches[i].id = id; + app->touch_obj->input.touches[i].timestamp = time; + app->touch_obj->input.touches[i].state = LV_INDEV_STATE_PRESSED; + app->touch_obj->input.touch_event_cnt++; + + } + else { + + cur->point.x = wl_fixed_to_int(x_w); + cur->point.y = wl_fixed_to_int(y_w); + cur->id = id; + cur->timestamp = time; + } +} + +static void touch_handle_frame(void * data, struct wl_touch * wl_touch) +{ + LV_UNUSED(wl_touch); + LV_UNUSED(data); +} + +static void touch_handle_cancel(void * data, struct wl_touch * wl_touch) +{ + LV_UNUSED(wl_touch); + LV_UNUSED(data); +} + +#endif /* LV_USE_WAYLAND && LV_USE_GESTURE_RECOGNITION */ diff --git a/src/drivers/wayland/lv_wl_touch.h b/src/drivers/wayland/lv_wl_touch.h new file mode 100644 index 0000000000..eab4443469 --- /dev/null +++ b/src/drivers/wayland/lv_wl_touch.h @@ -0,0 +1,54 @@ + + +/** + * @file lv_wl_touch.h + * + */ + +#ifndef LV_WL_TOUCH_H +#define LV_WL_TOUCH_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../indev/lv_indev.h" +#include "../../indev/lv_indev_gesture.h" +#if LV_USE_WAYLAND && LV_USE_GESTURE_RECOGNITION + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +lv_indev_t * lv_wayland_touch_create(void); + +/** + * Get touchscreen input device for given LVGL display + * @param display LVGL display + * @return input device connected to touchscreen, or NULL on error + */ +lv_indev_t * lv_wayland_get_touchscreen(lv_display_t * display); + +/********************** + * MACROS + **********************/ + +#endif /* LV_USE_WAYLAND */ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_WL_TOUCH_H*/ diff --git a/src/drivers/wayland/lv_wl_window.c b/src/drivers/wayland/lv_wl_window.c new file mode 100644 index 0000000000..1e9df64061 --- /dev/null +++ b/src/drivers/wayland/lv_wl_window.c @@ -0,0 +1,489 @@ +/** + * @file lv_wl_window.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_wl_window.h" + +#if LV_USE_WAYLAND + +#include +#include "lv_wayland_private.h" +#include "lv_wayland_private.h" +#include "lv_wl_pointer.h" +#include "lv_wl_pointer_axis.h" +#include "lv_wl_touch.h" +#include "lv_wl_keyboard.h" + +#include "../../core/lv_refr.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +static struct graphic_object * create_graphic_obj(struct window * window, enum object_type type, + struct graphic_object * parent); +static void destroy_graphic_obj(struct window * window, struct graphic_object * obj); + +/* Create a window + * @description Creates the graphical context for the window body, and then create a toplevel + * wayland surface and commit it to obtain an XDG configuration event + * @param width the height of the window w/decorations + * @param height the width of the window w/decorations + */ +static struct window * create_window(struct lv_wayland_context * app, int width, int height, const char * title); + +/** + * The frame callback called when the compositor has finished rendering + * a frame.It increments the frame counter and sets up the callback + * for the next frame the frame counter is used to avoid needlessly + * committing frames too fast on a slow system + * + * NOTE: this function is invoked by the wayland-server library within the compositor + * the event is added to the queue, and then upon the next timer call it's + * called indirectly from _lv_wayland_handle_input (via wl_display_dispatch_queue) + * @param void data the user object defined that was tied to this event during + * the configuration of the callback + * @param struct wl_callback The callback that needs to be destroyed and re-created + * @param time Timestamp of the event (unused) + */ +static void lv_window_graphic_obj_flush_done(void * data, struct wl_callback * cb, uint32_t time); + +/********************** + * STATIC VARIABLES + **********************/ + +static const struct wl_callback_listener wl_surface_frame_listener = { + .done = lv_window_graphic_obj_flush_done, +}; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_display_t * lv_wayland_window_create(uint32_t hor_res, uint32_t ver_res, char * title, + lv_wayland_display_close_f_t close_cb) +{ + struct window * window; + int32_t window_width; + int32_t window_height; + + lv_wayland_init(); + + window_width = hor_res; + window_height = ver_res; + +#if LV_WAYLAND_WINDOW_DECORATIONS + if(!lv_wl_ctx.opt_disable_decorations) { + window_width = hor_res + (2 * BORDER_SIZE); + window_height = ver_res + (TITLE_BAR_HEIGHT + (2 * BORDER_SIZE)); + } +#endif + + window = create_window(&lv_wl_ctx, window_width, window_height, title); + if(!window) { + LV_LOG_ERROR("failed to create wayland window"); + return NULL; + } + + window->close_cb = close_cb; + + /* Initialize display driver */ + window->lv_disp = lv_display_create(hor_res, ver_res); + if(window->lv_disp == NULL) { + LV_LOG_ERROR("failed to create lvgl display"); + return NULL; + } + +#if LV_WAYLAND_USE_DMABUF + if(lv_wayland_dmabuf_create_draw_buffers(&lv_wl_ctx.dmabuf_ctx, window) != LV_RESULT_OK) { + LV_LOG_ERROR("Failed to create draw buffers"); + return NULL; + } +#else + if(lv_wayland_shm_create_draw_buffers(&lv_wl_ctx.shm_ctx, window) != LV_RESULT_OK) { + LV_LOG_ERROR("Failed to create window buffers"); + return NULL; + } +#endif + + lv_display_set_user_data(window->lv_disp, window); + + lv_display_set_render_mode(window->lv_disp, LV_WAYLAND_RENDER_MODE); + lv_display_set_flush_wait_cb(window->lv_disp, lv_wayland_wait_flush_cb); + +#if LV_WAYLAND_USE_DMABUF + lv_wayland_dmabuf_set_draw_buffers(&lv_wl_ctx.dmabuf_ctx, window->lv_disp); + lv_display_set_flush_cb(window->lv_disp, lv_wayland_dmabuf_flush_full_mode); +#else + lv_wayland_shm_set_draw_buffers(&lv_wl_ctx.shm_ctx, window->lv_disp); + lv_display_set_flush_cb(window->lv_disp, lv_wayland_shm_flush_partial_mode); +#endif + + /* Register input */ + window->lv_indev_pointer = lv_wayland_pointer_create(); + + lv_indev_set_display(window->lv_indev_pointer, window->lv_disp); + + if(!window->lv_indev_pointer) { + LV_LOG_ERROR("failed to register pointer indev"); + } + + window->lv_indev_pointeraxis = lv_wayland_pointer_axis_create(); + lv_indev_set_display(window->lv_indev_pointeraxis, window->lv_disp); + + if(!window->lv_indev_pointeraxis) { + LV_LOG_ERROR("failed to register pointeraxis indev"); + } + +#if LV_USE_GESTURE_RECOGNITION + + window->lv_indev_touch = lv_wayland_touch_create(); + lv_indev_set_display(window->lv_indev_touch, window->lv_disp); + + if(!window->lv_indev_touch) { + LV_LOG_ERROR("failed to register touch indev"); + } + +#endif /* END LV_USE_GESTURE_RECOGNITION */ + + window->lv_indev_keyboard = lv_wayland_keyboard_create(); + lv_indev_set_display(window->lv_indev_keyboard, window->lv_disp); + + if(!window->lv_indev_keyboard) { + LV_LOG_ERROR("failed to register keyboard indev"); + } + return window->lv_disp; +} + +void lv_wayland_window_close(lv_display_t * disp) +{ + struct window * window = lv_display_get_user_data(disp); + if(!window || window->closed) { + return; + } + window->shall_close = true; + window->close_cb = NULL; + lv_wayland_deinit(); +} + +bool lv_wayland_window_is_open(lv_display_t * disp) +{ + struct window * window; + bool open = false; + + if(disp == NULL) { + LV_LL_READ(&lv_wl_ctx.window_ll, window) { + if(!window->closed) { + open = true; + break; + } + } + } + else { + window = lv_display_get_user_data(disp); + open = (!window->closed); + } + + return open; +} + +void lv_wayland_window_set_maximized(lv_display_t * disp, bool maximized) +{ + struct window * window = lv_display_get_user_data(disp); + lv_result_t err = LV_RESULT_INVALID; + if(!window || window->closed) { + return; + } + + if(window->maximized != maximized) { +#if LV_WAYLAND_WL_SHELL + err = lv_wayland_wl_shell_set_maximized(window, maximized); +#elif LV_WAYLAND_XDG_SHELL + err = lv_wayland_xdg_shell_set_maximized(window, maximized); +#endif + } + + if(err == LV_RESULT_INVALID) { + LV_LOG_WARN("Failed to maximize wayland window"); + return; + } + + window->maximized = maximized; + window->flush_pending = true; +} + +void lv_wayland_window_set_fullscreen(lv_display_t * disp, bool fullscreen) +{ + struct window * window = lv_display_get_user_data(disp); + lv_result_t err = LV_RESULT_INVALID; + if(!window || window->closed) { + return; + } + + if(window->fullscreen == fullscreen) { + return; + } +#if LV_WAYLAND_WL_SHELL + err = lv_wayland_wl_shell_set_fullscreen(window, fullscreen); +#elif LV_WAYLAND_XDG_SHELL + err = lv_wayland_xdg_shell_set_fullscreen(window, fullscreen); +#endif + + if(err == LV_RESULT_INVALID) { + LV_LOG_WARN("Failed to set wayland window to fullscreen"); + return; + } + + window->fullscreen = fullscreen; + window->flush_pending = true; +} + +/********************** + * PRIVATE FUNCTIONS + **********************/ + +void lv_wayland_window_draw(struct window * window, uint32_t width, uint32_t height) +{ + +#if LV_WAYLAND_WINDOW_DECORATIONS + if(lv_wl_ctx.opt_disable_decorations == false) { + for(size_t i = 0; i < NUM_DECORATIONS; i++) { + window->decoration[i] = create_graphic_obj(window, (FIRST_DECORATION + i), window->body); + if(!window->decoration[i]) { + LV_LOG_ERROR("Failed to create decoration %zu", i); + } + } + } +#endif + + LV_LOG_TRACE("Resizing to %d %d", width, height); + /* First resize */ + if(lv_wayland_window_resize(window, width, height) != LV_RESULT_OK) { + LV_LOG_ERROR("Failed to resize window"); +#if LV_WAYLAND_XDG_SHELL + lv_wayland_xdg_shell_destroy_window_toplevel(window); +#endif + } + + lv_refr_now(window->lv_disp); +} + +lv_result_t lv_wayland_window_resize(struct window * window, int width, int height) +{ + + +#if LV_WAYLAND_WINDOW_DECORATIONS + if(!window->wl_ctx->opt_disable_decorations && !window->fullscreen) { + width -= (2 * BORDER_SIZE); + height -= (TITLE_BAR_HEIGHT + (2 * BORDER_SIZE)); + } +#endif + +#if LV_WAYLAND_USE_DMABUF + { + lv_result_t err = lv_wayland_dmabuf_resize_window(&window->wl_ctx->dmabuf_ctx, window); + if(err != LV_RESULT_OK) { + return err; + } + } +#else + { + lv_result_t err = lv_wayland_shm_resize_window(&window->wl_ctx->shm_ctx, window, width, height); + if(err != LV_RESULT_OK) { + return err; + } + } +#endif + +#if LV_WAYLAND_WINDOW_DECORATIONS + if(!window->wl_ctx->opt_disable_decorations && !window->fullscreen) { + lv_wayland_window_decoration_create_all(window); + } + else if(!window->wl_ctx->opt_disable_decorations) { + /* Entering fullscreen, detach decorations to prevent xdg_wm_base error 4 */ + /* requested geometry larger than the configured fullscreen state */ + lv_wayland_window_decoration_detach_all(window); + } +#endif + + if(window->lv_disp) { + lv_display_set_resolution(window->lv_disp, width, height); + window->body->input.pointer.x = LV_MIN((int32_t)window->body->input.pointer.x, (width - 1)); + window->body->input.pointer.y = LV_MIN((int32_t)window->body->input.pointer.y, (height - 1)); + } + window->width = width; + window->height = height; + return LV_RESULT_OK; +} + +void lv_wayland_window_destroy(struct window * window) +{ + if(!window) { + return; + } + +#if LV_WAYLAND_WL_SHELL + lv_wayland_wl_shell_destroy_window(window); +#elif LV_WAYLAND_XDG_SHELL + lv_wayland_xdg_shell_destroy_window_toplevel(window); + lv_wayland_xdg_shell_destroy_window_surface(window); +#endif + +#if LV_WAYLAND_WINDOW_DECORATIONS + for(size_t i = 0; i < NUM_DECORATIONS; i++) { + if(window->decoration[i]) { + destroy_graphic_obj(window, window->decoration[i]); + window->decoration[i] = NULL; + } + } +#endif + + destroy_graphic_obj(window, window->body); +} + +const struct wl_callback_listener * lv_wayland_window_get_wl_surface_frame_listener(void) +{ + return &wl_surface_frame_listener; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static struct window * create_window(struct lv_wayland_context * app, int width, int height, const char * title) +{ + struct window * window; + + window = lv_ll_ins_tail(&app->window_ll); + LV_ASSERT_MALLOC(window); + if(!window) { + return NULL; + } + + lv_memset(window, 0x00, sizeof(struct window)); + + window->wl_ctx = app; + + // Create wayland buffer and surface + window->body = create_graphic_obj(window, OBJECT_WINDOW, NULL); + window->width = width; + window->height = height; + + if(!window->body) { + LV_LOG_ERROR("cannot create window body"); + goto err_free_window; + } + +#if LV_WAYLAND_WL_SHELL + if(lv_wayland_wl_shell_create_window(app, window, title) != LV_RESULT_OK) { + LV_LOG_ERROR("Failed to create wl shell window"); + goto err_destroy_surface; + } +#elif LV_WAYLAND_XDG_SHELL + if(lv_wayland_xdg_shell_create_window(app, window, title) != LV_RESULT_OK) { + goto err_destroy_surface; + } +#endif + + return window; + +err_destroy_surface: + wl_surface_destroy(window->body->surface); + +err_free_window: + lv_ll_remove(&app->window_ll, window); + lv_free(window); + return NULL; +} + +static struct graphic_object * create_graphic_obj(struct window * window, enum object_type type, + struct graphic_object * parent) +{ + + /* For now creating a graphical object is similar enough for both shm and dmabuf + * so the heavylifting is done in this function directly but we still + * call the shm and dmabuf specific functions at the end to make sure they can add additional + * attributes if needed + */ + struct graphic_object * obj = NULL; + + LV_UNUSED(parent); + + obj = lv_malloc(sizeof(*obj)); + LV_ASSERT_MALLOC(obj); + if(!obj) { + LV_LOG_ERROR("Failed to create graphic object"); + return NULL; + } + + lv_memset(obj, 0, sizeof(*obj)); + + obj->surface = wl_compositor_create_surface(window->wl_ctx->compositor); + if(!obj->surface) { + LV_LOG_ERROR("Failed to create surface for graphic object"); + lv_free(obj); + return NULL; + } + wl_surface_set_user_data(obj->surface, obj); + + obj->window = window; + obj->type = type; + +#if LV_WAYLAND_USE_DMABUF + return lv_wayland_dmabuf_on_graphical_object_creation(&window->wl_ctx->dmabuf_ctx, obj); +#else + return lv_wayland_shm_on_graphical_object_creation(&window->wl_ctx->shm_ctx, obj); +#endif +} + +static void destroy_graphic_obj(struct window * window, struct graphic_object * obj) +{ + if(obj->subsurface) { + wl_subsurface_destroy(obj->subsurface); + } + + wl_surface_destroy(obj->surface); + +#if LV_WAYLAND_USE_DMABUF + lv_wayland_dmabuf_on_graphical_object_destruction(&window->wl_ctx->dmabuf_ctx, obj); +#else + lv_wayland_shm_on_graphical_object_destruction(&window->wl_ctx->shm_ctx, obj); +#endif + lv_free(obj); +} + +static void lv_window_graphic_obj_flush_done(void * data, struct wl_callback * cb, uint32_t time) +{ + struct graphic_object * obj; + struct window * window; + + LV_UNUSED(time); + + wl_callback_destroy(cb); + + obj = (struct graphic_object *)data; + window = obj->window; + window->frame_counter++; + + LV_LOG_TRACE("frame: %d done, new frame: %d", window->frame_counter - 1, window->frame_counter); + + lv_display_flush_ready(window->lv_disp); +} + +#endif /* LV_USE_WAYLAND */ diff --git a/src/drivers/wayland/lv_wl_window.h b/src/drivers/wayland/lv_wl_window.h new file mode 100644 index 0000000000..7333985655 --- /dev/null +++ b/src/drivers/wayland/lv_wl_window.h @@ -0,0 +1,84 @@ + +/** + * @file lv_wl_display.h + * + */ + +#ifndef LV_WL_WINDOW_H +#define LV_WL_WINDOW_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../display/lv_display.h" + +#if LV_USE_WAYLAND + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef bool (*lv_wayland_display_close_f_t)(lv_display_t * disp); + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Creates a window + * @param hor_res The width of the window in pixels + * @param ver_res The height of the window in pixels + * @param title The title of the window + * @param close_cb The callback that will be execute when the user closes the window + * @return The LVGL display associated to the window + */ +lv_display_t * lv_wayland_window_create(uint32_t hor_res, uint32_t ver_res, char * title, + lv_wayland_display_close_f_t close_cb); + +/** + * Closes the window programmatically + * @param disp Reference to the LVGL display associated to the window + */ +void lv_wayland_window_close(lv_display_t * disp); + +/** + * Check if the window is open + * @param disp Reference to the LVGL display associated to the window + * @return true: The window is open + */ +bool lv_wayland_window_is_open(lv_display_t * disp); + +/** + * Sets the fullscreen state of the window + * @param disp Reference to the LVGL display associated to the window + * @param fullscreen If true the window enters fullscreen + */ +void lv_wayland_window_set_fullscreen(lv_display_t * disp, bool fullscreen); + +/** + * Sets the maximized state of the window + * @param disp Reference to the LVGL display associated to the window + * @param fullscreen If true the window is maximized + */ +void lv_wayland_window_set_maximized(lv_display_t * disp, bool maximize); + +/********************** + * MACROS + **********************/ + +#endif /* LV_USE_WAYLAND */ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_WL_WINDOW_H*/ diff --git a/src/drivers/wayland/lv_wl_window_decorations.c b/src/drivers/wayland/lv_wl_window_decorations.c new file mode 100644 index 0000000000..314043643f --- /dev/null +++ b/src/drivers/wayland/lv_wl_window_decorations.c @@ -0,0 +1,361 @@ +/** + * @file lv_wl_window_decorations.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_wayland.h" + +#if LV_WAYLAND_WINDOW_DECORATIONS + +#include "lv_wayland_private.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/* + * Fills a buffer with a color + * @description Used to draw the decorations, by writing directly to the SHM buffer, + * most wayland compositors support the ARGB8888, XRGB8888, RGB565 formats + * + * For color depths usually not natively supported by wayland i.e RGB332, Grayscale + * A conversion is performed to match the format of the SHM buffer read by the compositor. + * + * This function can also be used as a visual debugging aid to see how damage is applied + * + * @param pixels pointer to the buffer to fill + * @param lv_color_t color the color that will be used for the fill + * @param width width of the filled area + * @param height height of the filled area + * + */ +static void color_fill(void * pixels, lv_color_t color, uint32_t width, uint32_t height); +static void color_fill_XRGB8888(void * pixels, lv_color_t color, uint32_t width, uint32_t height); +static void color_fill_RGB565(void * pixels, lv_color_t color, uint32_t width, uint32_t height); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +bool lv_wayland_window_decoration_attach(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; + + 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; + } + + /* Enable this, to make it function on weston 10.0.2 */ + /* It's not elegant but it forces weston to size the surfaces before */ + /* the conversion to a subsurface takes place */ + + /* Likely related to this issue, some patches were merged into 10.0.0 */ + /* https://gitlab.freedesktop.org/wayland/weston/-/issues/446 */ + /* Moreover, it crashes on GNOME */ + +#if 0 + wl_surface_attach(decoration->surface, wl_buf, 0, 0); + wl_surface_commit(decoration->surface); +#endif + + if(decoration->subsurface == NULL) { + /* Create the subsurface only once */ + + decoration->subsurface = + wl_subcompositor_get_subsurface(window->wl_ctx->subcompositor, decoration->surface, parent->surface); + if(!decoration->subsurface) { + LV_LOG_ERROR("cannot get subsurface for decoration"); + goto err_destroy_surface; + } + } + + 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; +} + +uint32_t lv_wayland_window_decoration_create_all(struct window * window) +{ + uint32_t created = 0; + for(size_t i = 0; i < NUM_DECORATIONS; i++) { + if(lv_wayland_window_decoration_create(window, window->decoration[i], window->body->width, + window->body->height)) { + created++; + continue; + } + LV_LOG_ERROR("failed to create decoration %zu", i); + } + return created; +} + +void lv_wayland_window_decoration_detach_all(struct window * window) +{ + for(int i = 0; i < NUM_DECORATIONS; i++) { + lv_wayland_window_decoration_detach(window, window->decoration[i]); + } +} + +bool lv_wayland_window_decoration_create(struct window * window, struct graphic_object * decoration, int window_width, + int window_height) +{ + smm_buffer_t * buf; + void * buf_base; + int x, y; + lv_color_t * pixel; + uint8_t bpp; + + switch(decoration->type) { + case OBJECT_TITLEBAR: + decoration->width = window_width; + decoration->height = TITLE_BAR_HEIGHT; + break; + case OBJECT_BUTTON_CLOSE: + decoration->width = BUTTON_SIZE; + decoration->height = BUTTON_SIZE; + break; +#if LV_WAYLAND_XDG_SHELL + case OBJECT_BUTTON_MAXIMIZE: + decoration->width = BUTTON_SIZE; + decoration->height = BUTTON_SIZE; + break; + case OBJECT_BUTTON_MINIMIZE: + decoration->width = BUTTON_SIZE; + decoration->height = BUTTON_SIZE; + break; +#endif + case OBJECT_BORDER_TOP: + decoration->width = window_width + 2 * (BORDER_SIZE); + decoration->height = BORDER_SIZE; + break; + case OBJECT_BORDER_BOTTOM: + decoration->width = window_width + 2 * (BORDER_SIZE); + decoration->height = BORDER_SIZE; + break; + case OBJECT_BORDER_LEFT: + decoration->width = BORDER_SIZE; + decoration->height = window_height + TITLE_BAR_HEIGHT; + break; + case OBJECT_BORDER_RIGHT: + decoration->width = BORDER_SIZE; + decoration->height = window_height + TITLE_BAR_HEIGHT; + break; + default: + LV_ASSERT_MSG(0, "Invalid object type"); + return false; + } + + bpp = lv_color_format_get_size(LV_COLOR_FORMAT_NATIVE); + + LV_LOG_TRACE("decoration window %dx%d", decoration->width, decoration->height); + + smm_resize(decoration->buffer_group, (decoration->width * bpp) * 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 map in allocated decoration buffer"); + smm_release(buf); + return false; + } + + switch(decoration->type) { + case OBJECT_TITLEBAR: + color_fill(buf_base, lv_color_make(0x66, 0x66, 0x66), decoration->width, decoration->height); + break; + case OBJECT_BUTTON_CLOSE: + color_fill(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++) { + pixel = (lv_color_t *)((unsigned char *)buf_base + (y * (decoration->width * bpp)) + x * bpp); + if((x >= BUTTON_PADDING) && (x < decoration->width - BUTTON_PADDING)) { + if((x == y) || (x == decoration->width - 1 - y)) { + color_fill(pixel, lv_color_make(0x33, 0x33, 0x33), 1, 1); + } + else if((x == y - 1) || (x == decoration->width - y)) { + color_fill(pixel, lv_color_make(0x66, 0x66, 0x66), 1, 1); + } + } + } + } + break; +#if LV_WAYLAND_XDG_SHELL + case OBJECT_BUTTON_MAXIMIZE: + color_fill(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++) { + pixel = (lv_color_t *)((unsigned char *)buf_base + (y * (decoration->width * bpp)) + x * bpp); + 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)) || + ((y == (BUTTON_PADDING + 1)) && (x >= BUTTON_PADDING) && + (x < decoration->width - BUTTON_PADDING)) || + ((y == (decoration->height - BUTTON_PADDING)) && (x >= BUTTON_PADDING) && + (x < decoration->width - BUTTON_PADDING))) { + color_fill(pixel, lv_color_make(0x33, 0x33, 0x33), 1, 1); + } + } + } + break; + case OBJECT_BUTTON_MINIMIZE: + color_fill(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++) { + pixel = (lv_color_t *)((unsigned char *)buf_base + (y * (decoration->width * bpp)) + x * bpp); + if((x >= BUTTON_PADDING) && (x < decoration->width - BUTTON_PADDING) && + (y > decoration->height - (2 * BUTTON_PADDING)) && (y < decoration->height - BUTTON_PADDING)) { + color_fill(pixel, lv_color_make(0x33, 0x33, 0x33), 1, 1); + } + } + } + break; +#endif + case OBJECT_BORDER_TOP: + /* fallthrough */ + case OBJECT_BORDER_BOTTOM: + /* fallthrough */ + case OBJECT_BORDER_LEFT: + /* fallthrough */ + case OBJECT_BORDER_RIGHT: + color_fill(buf_base, lv_color_make(0x66, 0x66, 0x66), decoration->width, decoration->height); + break; + default: + LV_ASSERT_MSG(0, "Invalid object type"); + return false; + } + + return lv_wayland_window_decoration_attach(window, decoration, buf, window->body); +} + +void lv_wayland_window_decoration_detach(struct window * window, struct graphic_object * decoration) +{ + + LV_UNUSED(window); + + if(decoration->subsurface) { + wl_subsurface_destroy(decoration->subsurface); + decoration->subsurface = NULL; + } +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void color_fill(void * pixels, lv_color_t color, uint32_t width, uint32_t height) +{ + + switch(lv_wl_ctx.shm_ctx.format) { + case WL_SHM_FORMAT_ARGB8888: + color_fill_XRGB8888(pixels, color, width, height); + break; + case WL_SHM_FORMAT_RGB565: + color_fill_RGB565(pixels, color, width, height); + break; + default: + LV_ASSERT_MSG(0, "Unsupported WL_SHM_FORMAT"); + break; + } +} + +static void color_fill_XRGB8888(void * pixels, lv_color_t color, uint32_t width, uint32_t height) +{ + unsigned char * buf = pixels; + unsigned char * buf_end; + + buf_end = (unsigned char *)((uint32_t *)buf + width * height); + + while(buf < buf_end) { + *(buf++) = color.blue; + *(buf++) = color.green; + *(buf++) = color.red; + *(buf++) = 0xFF; + } +} + +static void color_fill_RGB565(void * pixels, lv_color_t color, uint32_t width, uint32_t height) +{ + uint16_t * buf = pixels; + uint16_t * buf_end; + + buf_end = (uint16_t *)buf + width * height; + + while(buf < buf_end) { + *(buf++) = lv_color_to_u16(color); + } +} + +#endif /* LV_WAYLAND_WINDOW_DECORATIONS */ diff --git a/src/drivers/wayland/lv_wl_xdg_shell.c b/src/drivers/wayland/lv_wl_xdg_shell.c new file mode 100644 index 0000000000..43848544e8 --- /dev/null +++ b/src/drivers/wayland/lv_wl_xdg_shell.c @@ -0,0 +1,467 @@ +/** + * @file lv_wl_xdg_shell.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_wayland.h" + +#if LV_USE_WAYLAND +/* + * LV_WAYLAND_XDG_SHELL is automatically defined if LV_WAYLAND_WL_SHELL is not set + * inside lv_wayland_private.h so we need include this header file before checking + * for LV_WAYLAND_XDG_SHELL + */ +#include "lv_wayland_private.h" + +#if LV_WAYLAND_XDG_SHELL + +#include +#include "wayland_xdg_shell.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void xdg_surface_handle_configure(void * data, struct xdg_surface * xdg_surface, uint32_t serial); +static void xdg_toplevel_handle_configure(void * data, struct xdg_toplevel * xdg_toplevel, int32_t width, + int32_t height, struct wl_array * states); +static void xdg_toplevel_handle_close(void * data, struct xdg_toplevel * xdg_toplevel); +static void xdg_wm_base_ping(void * data, struct xdg_wm_base * xdg_wm_base, uint32_t serial); + +/********************** + * STATIC VARIABLES + **********************/ + +static const struct xdg_surface_listener xdg_surface_listener = { + .configure = xdg_surface_handle_configure, +}; + +static const struct xdg_toplevel_listener xdg_toplevel_listener = { + .configure = xdg_toplevel_handle_configure, + .close = xdg_toplevel_handle_close, +}; + +static const struct xdg_wm_base_listener xdg_wm_base_listener = {.ping = xdg_wm_base_ping}; +static bool is_window_configured = false; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/********************** + * PRIVATE FUNCTIONS + **********************/ + +/********************** + * Shell + **********************/ + +void lv_wayland_xdg_shell_deinit(void) +{ + + if(lv_wl_ctx.xdg_wm) { + xdg_wm_base_destroy(lv_wl_ctx.xdg_wm); + } +} + +/********************** + * Listeners + **********************/ + +const struct xdg_wm_base_listener * lv_wayland_xdg_shell_get_wm_base_listener(void) +{ + return &xdg_wm_base_listener; +} + +const struct xdg_surface_listener * lv_wayland_xdg_shell_get_surface_listener(void) +{ + return &xdg_surface_listener; +} + +const struct xdg_toplevel_listener * lv_wayland_xdg_shell_get_toplevel_listener(void) +{ + return &xdg_toplevel_listener; +} + +/********************** + * Shell Window + **********************/ + +lv_result_t lv_wayland_xdg_shell_set_fullscreen(struct window * window, bool fullscreen) +{ + + if(!window->xdg_toplevel) { + return LV_RESULT_INVALID; + } + if(fullscreen) { + xdg_toplevel_set_fullscreen(window->xdg_toplevel, NULL); + } + else { + xdg_toplevel_unset_fullscreen(window->xdg_toplevel); + } + return LV_RESULT_OK; +} + +lv_result_t lv_wayland_xdg_shell_set_maximized(struct window * window, bool maximized) +{ + if(!window->xdg_toplevel) { + return LV_RESULT_INVALID; + } + + if(maximized) { + xdg_toplevel_set_maximized(window->xdg_toplevel); + } + else { + xdg_toplevel_unset_maximized(window->xdg_toplevel); + } + + return LV_RESULT_OK; +} + +lv_result_t lv_wayland_xdg_shell_set_minimized(struct window * window) +{ + if(!window->xdg_toplevel) { + return LV_RESULT_INVALID; + } + + xdg_toplevel_set_minimized(window->xdg_toplevel); + return LV_RESULT_OK; +} + +lv_result_t lv_wayland_xdg_shell_create_window(struct lv_wayland_context * app, struct window * window, + const char * title) +{ + if(!app->xdg_wm) { + return LV_RESULT_INVALID; + } + + window->xdg_surface = xdg_wm_base_get_xdg_surface(app->xdg_wm, window->body->surface); + if(!window->xdg_surface) { + LV_LOG_ERROR("cannot create XDG surface"); + return LV_RESULT_INVALID; + } + + xdg_surface_add_listener(window->xdg_surface, lv_wayland_xdg_shell_get_surface_listener(), window); + + window->xdg_toplevel = xdg_surface_get_toplevel(window->xdg_surface); + if(!window->xdg_toplevel) { + xdg_surface_destroy(window->xdg_surface); + LV_LOG_ERROR("cannot get XDG toplevel surface"); + return LV_RESULT_INVALID; + } + + xdg_toplevel_add_listener(window->xdg_toplevel, lv_wayland_xdg_shell_get_toplevel_listener(), window); + xdg_toplevel_set_title(window->xdg_toplevel, title); + xdg_toplevel_set_app_id(window->xdg_toplevel, title); + + // XDG surfaces need to be configured before a buffer can be attached. + // An (XDG) surface commit (without an attached buffer) triggers this + // configure event + is_window_configured = false; + wl_surface_commit(window->body->surface); + wl_display_roundtrip(lv_wl_ctx.display); + LV_ASSERT_MSG(is_window_configured, "Failed to receive the xdg_surface configuration event"); + return LV_RESULT_OK; +} + +lv_result_t lv_wayland_xdg_shell_destroy_window_surface(struct window * window) +{ + + if(!window->xdg_surface) { + return LV_RESULT_INVALID; + } + xdg_surface_destroy(window->xdg_surface); + return LV_RESULT_OK; +} + +lv_result_t lv_wayland_xdg_shell_destroy_window_toplevel(struct window * window) +{ + + if(!window->xdg_toplevel) { + return LV_RESULT_INVALID; + } + xdg_toplevel_destroy(window->xdg_toplevel); + return LV_RESULT_OK; +} + +/********************** + * Shell Input + **********************/ + +void lv_wayland_xdg_shell_handle_pointer_event(struct lv_wayland_context * app, uint32_t serial, uint32_t button, + uint32_t state) +{ + struct window * window = app->pointer_obj->window; + int pos_x = (int)app->pointer_obj->input.pointer.x; + int pos_y = (int)app->pointer_obj->input.pointer.y; + + switch(app->pointer_obj->type) { + case OBJECT_TITLEBAR: + if((button == BTN_LEFT) && (state == WL_POINTER_BUTTON_STATE_PRESSED)) { + if(window->xdg_toplevel) { + xdg_toplevel_move(window->xdg_toplevel, app->wl_seat, serial); + window->flush_pending = true; + } + } + break; + case OBJECT_BUTTON_MAXIMIZE: + if((button == BTN_LEFT) && (state == WL_POINTER_BUTTON_STATE_RELEASED)) { + if(lv_wayland_xdg_shell_set_maximized(window, !window->maximized) == LV_RESULT_OK) { + window->maximized = !window->maximized; + window->flush_pending = true; + } + } + break; + case OBJECT_BUTTON_MINIMIZE: + if((button == BTN_LEFT) && (state == WL_POINTER_BUTTON_STATE_RELEASED)) { + if(lv_wayland_xdg_shell_set_minimized(window) == LV_RESULT_OK) { + window->flush_pending = true; + } + } + break; + case OBJECT_BORDER_TOP: + if((button == BTN_LEFT) && (state == WL_POINTER_BUTTON_STATE_PRESSED)) { + if(window->xdg_toplevel && !window->maximized) { + uint32_t edge; + if(pos_x < (BORDER_SIZE * 5)) { + edge = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT; + } + else if(pos_x >= (window->width + BORDER_SIZE - (BORDER_SIZE * 5))) { + edge = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT; + } + else { + edge = XDG_TOPLEVEL_RESIZE_EDGE_TOP; + } + xdg_toplevel_resize(window->xdg_toplevel, window->wl_ctx->wl_seat, serial, edge); + window->flush_pending = true; + } + } + break; + case OBJECT_BORDER_BOTTOM: + if((button == BTN_LEFT) && (state == WL_POINTER_BUTTON_STATE_PRESSED)) { + if(window->xdg_toplevel && !window->maximized) { + uint32_t edge; + if(pos_x < (BORDER_SIZE * 5)) { + edge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT; + } + else if(pos_x >= (window->width + BORDER_SIZE - (BORDER_SIZE * 5))) { + edge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT; + } + else { + edge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM; + } + xdg_toplevel_resize(window->xdg_toplevel, window->wl_ctx->wl_seat, serial, edge); + window->flush_pending = true; + } + } + break; + case OBJECT_BORDER_LEFT: + if((button == BTN_LEFT) && (state == WL_POINTER_BUTTON_STATE_PRESSED)) { + if(window->xdg_toplevel && !window->maximized) { + uint32_t edge; + if(pos_y < (BORDER_SIZE * 5)) { + edge = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT; + } + else if(pos_y >= (window->height + BORDER_SIZE - (BORDER_SIZE * 5))) { + edge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT; + } + else { + edge = XDG_TOPLEVEL_RESIZE_EDGE_LEFT; + } + xdg_toplevel_resize(window->xdg_toplevel, window->wl_ctx->wl_seat, serial, edge); + window->flush_pending = true; + } + } + break; + case OBJECT_BORDER_RIGHT: + if((button == BTN_LEFT) && (state == WL_POINTER_BUTTON_STATE_PRESSED)) { + if(window->xdg_toplevel && !window->maximized) { + uint32_t edge; + if(pos_y < (BORDER_SIZE * 5)) { + edge = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT; + } + else if(pos_y >= (window->height + BORDER_SIZE - (BORDER_SIZE * 5))) { + edge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT; + } + else { + edge = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT; + } + xdg_toplevel_resize(window->xdg_toplevel, window->wl_ctx->wl_seat, serial, edge); + window->flush_pending = true; + } + } + break; + case OBJECT_BUTTON_CLOSE: + case OBJECT_WINDOW: + /* These events are handled in the main pointer callback */ + break; + } +} + +const char * lv_wayland_xdg_shell_get_cursor_name(const struct lv_wayland_context * app) +{ + + if(!app->pointer_obj->window->xdg_toplevel || app->opt_disable_decorations) { + return LV_WAYLAND_DEFAULT_CURSOR_NAME; + } + int pos_x = (int)app->pointer_obj->input.pointer.x; + int pos_y = (int)app->pointer_obj->input.pointer.y; + + struct window * window = app->pointer_obj->window; + + switch(app->pointer_obj->type) { + case OBJECT_BORDER_TOP: + if(window->maximized) { + // do nothing + } + else if(pos_x < (BORDER_SIZE * 5)) { + return "top_left_corner"; + } + else if(pos_x >= (window->width + BORDER_SIZE - (BORDER_SIZE * 5))) { + return "top_right_corner"; + } + else { + return "top_side"; + } + break; + case OBJECT_BORDER_BOTTOM: + if(window->maximized) { + // do nothing + } + else if(pos_x < (BORDER_SIZE * 5)) { + return "bottom_left_corner"; + } + else if(pos_x >= (window->width + BORDER_SIZE - (BORDER_SIZE * 5))) { + return "bottom_right_corner"; + } + else { + return "bottom_side"; + } + break; + case OBJECT_BORDER_LEFT: + if(window->maximized) { + // do nothing + } + else if(pos_y < (BORDER_SIZE * 5)) { + return "top_left_corner"; + } + else if(pos_y >= (window->height + BORDER_SIZE - (BORDER_SIZE * 5))) { + return "bottom_left_corner"; + } + else { + return "left_side"; + } + break; + case OBJECT_BORDER_RIGHT: + if(window->maximized) { + // do nothing + } + else if(pos_y < (BORDER_SIZE * 5)) { + return "top_right_corner"; + } + else if(pos_y >= (window->height + BORDER_SIZE - (BORDER_SIZE * 5))) { + return "bottom_right_corner"; + } + else { + return "right_side"; + } + break; + default: + break; + } + + return LV_WAYLAND_DEFAULT_CURSOR_NAME; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void xdg_surface_handle_configure(void * data, struct xdg_surface * xdg_surface, uint32_t serial) +{ + struct window * window = (struct window *)data; + + xdg_surface_ack_configure(xdg_surface, serial); + + if(!is_window_configured) { + /* This branch is executed at launch */ + if(!window->resize_pending) { + /* Use the size passed to the create_window function */ + lv_wayland_window_draw(window, window->width, window->height); + } + else { + + /* Handle early maximization or fullscreen, */ + /* by using the size communicated by the compositor */ + /* when the initial xdg configure event arrives */ + lv_wayland_window_draw(window, window->resize_width, window->resize_height); + window->width = window->resize_width; + window->height = window->resize_height; + window->resize_pending = false; + } + } + is_window_configured = true; +} + +static void xdg_toplevel_handle_configure(void * data, struct xdg_toplevel * xdg_toplevel, int32_t width, + int32_t height, struct wl_array * states) +{ + struct window * window = (struct window *)data; + + LV_UNUSED(xdg_toplevel); + LV_UNUSED(states); + + LV_LOG_TRACE("w:%d h:%d", width, height); + LV_LOG_TRACE("current body w:%d h:%d", window->body->width, window->body->height); + LV_LOG_TRACE("window w:%d h:%d", window->width, window->height); + + if((width <= 0) || (height <= 0)) { + LV_LOG_TRACE("will not resize to w:%d h:%d", width, height); + return; + } + + if((width != window->width) || (height != window->height)) { + window->resize_width = width; + window->resize_height = height; + window->resize_pending = true; + LV_LOG_TRACE("resize_pending is set, will resize to w:%d h:%d", width, height); + } + else { + LV_LOG_TRACE("resize_pending not set w:%d h:%d", width, height); + } +} + +static void xdg_toplevel_handle_close(void * data, struct xdg_toplevel * xdg_toplevel) +{ + struct window * window = (struct window *)data; + window->shall_close = true; + + LV_UNUSED(xdg_toplevel); +} + +static void xdg_wm_base_ping(void * data, struct xdg_wm_base * xdg_wm_base, uint32_t serial) +{ + LV_UNUSED(data); + + xdg_wm_base_pong(xdg_wm_base, serial); + + return; +} + +#endif /* LV_WAYLAND_XDG_SHELL */ +#endif /* LV_USE_WAYLAND */ diff --git a/src/lv_conf_internal.h b/src/lv_conf_internal.h index 80d989edaa..d5b30d873a 100644 --- a/src/lv_conf_internal.h +++ b/src/lv_conf_internal.h @@ -3825,18 +3825,44 @@ #endif #endif #if LV_USE_WAYLAND + #ifndef LV_WAYLAND_BUF_COUNT + #ifdef LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_WAYLAND_BUF_COUNT + #define LV_WAYLAND_BUF_COUNT CONFIG_LV_WAYLAND_BUF_COUNT + #else + #define LV_WAYLAND_BUF_COUNT 0 + #endif + #else + #define LV_WAYLAND_BUF_COUNT 1 /**< Use 1 for single buffer with partial render mode or 2 for double buffer with full render mode*/ + #endif + #endif + #ifndef LV_WAYLAND_USE_DMABUF + #ifdef CONFIG_LV_WAYLAND_USE_DMABUF + #define LV_WAYLAND_USE_DMABUF CONFIG_LV_WAYLAND_USE_DMABUF + #else + #define LV_WAYLAND_USE_DMABUF 0 /**< Use DMA buffers for frame buffers. Requires LV_DRAW_USE_G2D */ + #endif + #endif + #ifndef LV_WAYLAND_RENDER_MODE + #ifdef CONFIG_LV_WAYLAND_RENDER_MODE + #define LV_WAYLAND_RENDER_MODE CONFIG_LV_WAYLAND_RENDER_MODE + #else + #define LV_WAYLAND_RENDER_MODE LV_DISPLAY_RENDER_MODE_PARTIAL /**< DMABUF supports LV_DISPLAY_RENDER_MODE_FULL and LV_DISPLAY_RENDER_MODE_DIRECT*/ + #endif + #endif + /**< When LV_WAYLAND_USE_DMABUF is disabled, only LV_DISPLAY_RENDER_MODE_PARTIAL is supported*/ #ifndef LV_WAYLAND_WINDOW_DECORATIONS #ifdef CONFIG_LV_WAYLAND_WINDOW_DECORATIONS #define LV_WAYLAND_WINDOW_DECORATIONS CONFIG_LV_WAYLAND_WINDOW_DECORATIONS #else - #define LV_WAYLAND_WINDOW_DECORATIONS 0 /**< Draw client side window decorations only necessary on Mutter/GNOME */ + #define LV_WAYLAND_WINDOW_DECORATIONS 0 /**< Draw client side window decorations only necessary on Mutter/GNOME. Not supported using DMABUF*/ #endif #endif #ifndef LV_WAYLAND_WL_SHELL #ifdef CONFIG_LV_WAYLAND_WL_SHELL #define LV_WAYLAND_WL_SHELL CONFIG_LV_WAYLAND_WL_SHELL #else - #define LV_WAYLAND_WL_SHELL 0 /**< Use the legacy wl_shell protocol instead of the default XDG shell */ + #define LV_WAYLAND_WL_SHELL 0 /**< Use the legacy wl_shell protocol instead of the default XDG shell*/ #endif #endif #endif @@ -4478,6 +4504,12 @@ LV_EXPORT_CONST_INT(LV_DRAW_BUF_ALIGN); #define LV_LOG_TRACE_ANIM 0 #endif /*LV_USE_LOG*/ +#if LV_USE_WAYLAND == 0 + #define LV_WAYLAND_USE_DMABUF 0 + #define LV_WAYLAND_WINDOW_DECORATIONS 0 + #define LV_WAYLAND_WL_SHELL 0 +#endif /* LV_USE_WAYLAND */ + #if LV_USE_SYSMON == 0 #define LV_USE_PERF_MONITOR 0 #define LV_USE_MEM_MONITOR 0 diff --git a/src/lv_conf_kconfig.h b/src/lv_conf_kconfig.h index ff5681a694..78f9596878 100644 --- a/src/lv_conf_kconfig.h +++ b/src/lv_conf_kconfig.h @@ -271,6 +271,18 @@ extern "C" { # define CONFIG_LV_SDL_RENDER_MODE LV_DISPLAY_RENDER_MODE_FULL #endif +/*------------------ + * WAYLAND + *-----------------*/ + +#ifdef CONFIG_LV_WAYLAND_RENDER_MODE_PARTIAL +# define CONFIG_LV_WAYLAND_RENDER_MODE LV_DISPLAY_RENDER_MODE_PARTIAL +#elif defined(CONFIG_LV_WAYLAND_RENDER_MODE_DIRECT) +# define CONFIG_LV_WAYLAND_RENDER_MODE LV_DISPLAY_RENDER_MODE_DIRECT +#elif defined(CONFIG_LV_WAYLAND_RENDER_MODE_FULL) +# define CONFIG_LV_WAYLAND_RENDER_MODE_FULL LV_DISPLAY_RENDER_MODE_FULL +#endif + /*------------------ * LINUX FBDEV *-----------------*/ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5c8ec48a23..5161ded263 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -454,6 +454,7 @@ endif() if(WIN32) add_definitions(-DLV_USE_LINUX_FBDEV=0) add_definitions(-DLV_USE_WINDOWS=1) + add_definitions(-DLV_USE_WAYLAND=0) add_definitions(-DLV_USE_OS=LV_OS_WINDOWS) endif()