feat(wayland): add display rotation support for the shm backend (#9386)

This commit is contained in:
André Costa
2026-01-19 13:05:34 +01:00
committed by GitHub
parent e23b7bebf4
commit ca2db90dc1
2 changed files with 89 additions and 34 deletions
@@ -54,10 +54,7 @@ It uses Wayland's shared memory protocol (``wl_shm``) for rendering, which is su
* Double-buffered direct rendering * Double-buffered direct rendering
* Universal compatibility with all Wayland compositors * Universal compatibility with all Wayland compositors
* No special hardware requirements * No special hardware requirements
* Rotation support
**Limitations:**
* Rotation is not currently supported
**Usage:** **Usage:**
@@ -296,7 +293,6 @@ Current state and objectives
**************************** ****************************
* Server-side window decorations * Server-side window decorations
* Rotation support for the SHM backend
+88 -29
View File
@@ -11,6 +11,7 @@
#if LV_WAYLAND_USE_SHM #if LV_WAYLAND_USE_SHM
#include "../../draw/sw/lv_draw_sw_utils.h"
#include "../../display/lv_display_private.h" #include "../../display/lv_display_private.h"
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
@@ -41,6 +42,7 @@ typedef struct {
typedef struct { typedef struct {
void * mmap_ptr; void * mmap_ptr;
size_t mmap_size; size_t mmap_size;
uint8_t * rotated_buf;
struct wl_shm_pool * pool; struct wl_shm_pool * pool;
lv_wl_buffer_t buffers[LV_WL_SHM_BUF_COUNT]; lv_wl_buffer_t buffers[LV_WL_SHM_BUF_COUNT];
size_t curr_wl_buffer_idx; size_t curr_wl_buffer_idx;
@@ -65,8 +67,8 @@ static int create_shm_file(size_t size);
static void shm_flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map); static void shm_flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map);
static lv_wl_shm_display_data_t * shm_create_display_data(lv_wl_shm_ctx_t * ctx, lv_display_t * display, int32_t width, static lv_wl_shm_display_data_t * shm_create_display_data(lv_wl_shm_ctx_t * ctx, lv_display_t * display, int32_t width,
int32_t height, size_t buf_count); int32_t height);
static void shm_destroy_display_data(lv_wl_shm_display_data_t * ddata); static void shm_delete_display_data(lv_wl_shm_display_data_t * ddata);
static void frame_done(void * data, struct wl_callback * callback, uint32_t time); static void frame_done(void * data, struct wl_callback * callback, uint32_t time);
static void buffer_release(void * data, struct wl_buffer * wl_buffer); static void buffer_release(void * data, struct wl_buffer * wl_buffer);
@@ -117,7 +119,7 @@ static void buffer_release(void * data, struct wl_buffer * wl_buffer)
} }
if(ddata->delete_on_release) { if(ddata->delete_on_release) {
shm_destroy_display_data(ddata); shm_delete_display_data(ddata);
} }
} }
@@ -158,8 +160,10 @@ static void shm_deinit(void * backend_ctx)
} }
} }
static lv_wl_shm_display_data_t * shm_create_display_data(lv_wl_shm_ctx_t * ctx, lv_display_t * display, int32_t width, static lv_wl_shm_display_data_t * shm_create_display_data(lv_wl_shm_ctx_t * ctx,
int32_t height, size_t buf_count) lv_display_t * display,
int32_t width,
int32_t height)
{ {
lv_wl_shm_display_data_t * ddata = lv_zalloc(sizeof(*ddata)); lv_wl_shm_display_data_t * ddata = lv_zalloc(sizeof(*ddata));
if(!ddata) { if(!ddata) {
@@ -167,6 +171,7 @@ static lv_wl_shm_display_data_t * shm_create_display_data(lv_wl_shm_ctx_t * ctx,
return NULL; return NULL;
} }
const lv_display_rotation_t rotation = lv_display_get_rotation(display);
lv_color_format_t cf = lv_display_get_color_format(display); lv_color_format_t cf = lv_display_get_color_format(display);
ddata->shm_cf = lv_cf_to_shm_cf(cf); ddata->shm_cf = lv_cf_to_shm_cf(cf);
@@ -177,30 +182,37 @@ static lv_wl_shm_display_data_t * shm_create_display_data(lv_wl_shm_ctx_t * ctx,
ddata->shm_cf = WL_SHM_FORMAT_XRGB8888; ddata->shm_cf = WL_SHM_FORMAT_XRGB8888;
} }
const uint32_t stride = lv_draw_buf_width_to_stride(width, cf); const bool needs_rotation = rotation != LV_DISPLAY_ROTATION_0;
const size_t buf_size = stride * height; const int32_t phy_width = lv_display_get_original_horizontal_resolution(display);
const int32_t phy_height = lv_display_get_original_vertical_resolution(display);
const uint32_t phy_stride = lv_draw_buf_width_to_stride(phy_width, cf);
const size_t phy_buf_size = phy_stride * phy_height;
ddata->mmap_size = buf_size * buf_count; ddata->mmap_size = phy_buf_size * LV_WL_SHM_BUF_COUNT;
ddata->fd = create_shm_file(ddata->mmap_size); ddata->fd = create_shm_file(ddata->mmap_size);
if(ddata->fd < 0) { if(ddata->fd < 0) {
LV_LOG_ERROR("Failed to create shm file"); LV_LOG_ERROR("Failed to create shm file");
goto shm_file_err; goto shm_file_err;
} }
ddata->mmap_ptr = mmap(NULL, ddata->mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, ddata->fd, 0); ddata->mmap_ptr = mmap(NULL, ddata->mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, ddata->fd, 0);
if(ddata->mmap_ptr == MAP_FAILED) { if(ddata->mmap_ptr == MAP_FAILED) {
LV_LOG_ERROR("Failed to map shm file: %s", strerror(errno)); LV_LOG_ERROR("Failed to map shm file: %s", strerror(errno));
goto mmap_err; goto mmap_err;
} }
ddata->pool = wl_shm_create_pool(ctx->shm, ddata->fd, ddata->mmap_size); ddata->pool = wl_shm_create_pool(ctx->shm, ddata->fd, ddata->mmap_size);
if(!ddata->pool) { if(!ddata->pool) {
LV_LOG_ERROR("Failed to create wl_shm_pool"); LV_LOG_ERROR("Failed to create wl_shm_pool");
goto shm_pool_err; goto shm_pool_err;
} }
for(size_t i = 0; i < buf_count; ++i) { for(size_t i = 0; i < LV_WL_SHM_BUF_COUNT; ++i) {
size_t offset = i * buf_size; size_t offset = i * phy_buf_size;
ddata->buffers[i].wl_buffer = wl_shm_pool_create_buffer(ddata->pool, offset, width, height, stride, ddata->shm_cf); ddata->buffers[i].wl_buffer =
wl_shm_pool_create_buffer(ddata->pool, offset, phy_width, phy_height, phy_stride, ddata->shm_cf);
if(!ddata->buffers[i].wl_buffer) { if(!ddata->buffers[i].wl_buffer) {
LV_LOG_ERROR("Failed to create wl_buffer %zu", i); LV_LOG_ERROR("Failed to create wl_buffer %zu", i);
goto pool_buffer_err; goto pool_buffer_err;
@@ -209,11 +221,30 @@ static lv_wl_shm_display_data_t * shm_create_display_data(lv_wl_shm_ctx_t * ctx,
ddata->buffers[i].busy = false; ddata->buffers[i].busy = false;
} }
lv_display_set_buffers(display, ddata->mmap_ptr, (uint8_t *)ddata->mmap_ptr + buf_size, if(needs_rotation) {
buf_size, LV_DISPLAY_RENDER_MODE_DIRECT); const uint32_t stride = lv_draw_buf_width_to_stride(width, cf);
const size_t buf_size = stride * height;
ddata->rotated_buf = lv_malloc(buf_size);
LV_ASSERT_MALLOC(ddata->rotated_buf);
if(!ddata->rotated_buf) {
LV_LOG_ERROR("Failed to allocate LVGL render buffer");
goto rotated_buf_err;
}
lv_display_set_buffers(display, ddata->rotated_buf, NULL,
buf_size, LV_DISPLAY_RENDER_MODE_DIRECT);
}
else {
lv_display_set_buffers(display, ddata->mmap_ptr,
(uint8_t *)ddata->mmap_ptr + phy_buf_size,
phy_buf_size, LV_DISPLAY_RENDER_MODE_DIRECT);
}
return ddata; return ddata;
lv_free(ddata->rotated_buf);
rotated_buf_err:
pool_buffer_err: pool_buffer_err:
wl_shm_pool_destroy(ddata->pool); wl_shm_pool_destroy(ddata->pool);
shm_pool_err: shm_pool_err:
@@ -225,7 +256,7 @@ shm_file_err:
return NULL; return NULL;
} }
static void shm_destroy_display_data(lv_wl_shm_display_data_t * ddata) static void shm_delete_display_data(lv_wl_shm_display_data_t * ddata)
{ {
for(size_t i = 0; i < LV_WL_SHM_BUF_COUNT; ++i) { for(size_t i = 0; i < LV_WL_SHM_BUF_COUNT; ++i) {
lv_wl_buffer_t * buffer = &ddata->buffers[i]; lv_wl_buffer_t * buffer = &ddata->buffers[i];
@@ -257,6 +288,10 @@ static void shm_destroy_display_data(lv_wl_shm_display_data_t * ddata)
close(ddata->fd); close(ddata->fd);
ddata->fd = -1; ddata->fd = -1;
} }
if(ddata->rotated_buf) {
lv_free(ddata->rotated_buf);
ddata->rotated_buf = NULL;
}
LV_LOG_INFO("Deleted buffers and display data"); LV_LOG_INFO("Deleted buffers and display data");
lv_free(ddata); lv_free(ddata);
@@ -277,7 +312,7 @@ static void * shm_init_display(void * backend_ctx, lv_display_t * display, int32
return NULL; return NULL;
} }
lv_wl_shm_display_data_t * ddata = shm_create_display_data(ctx, display, width, height, LV_WL_SHM_BUF_COUNT); lv_wl_shm_display_data_t * ddata = shm_create_display_data(ctx, display, width, height);
if(!ddata) { if(!ddata) {
LV_LOG_ERROR("Failed to allocate data for display"); LV_LOG_ERROR("Failed to allocate data for display");
return NULL; return NULL;
@@ -296,7 +331,7 @@ static void * shm_resize_display(void * backend_ctx, lv_display_t * display)
const int32_t new_width = lv_display_get_horizontal_resolution(display); const int32_t new_width = lv_display_get_horizontal_resolution(display);
const int32_t new_height = lv_display_get_vertical_resolution(display); const int32_t new_height = lv_display_get_vertical_resolution(display);
lv_wl_shm_display_data_t * ddata = shm_create_display_data(ctx, display, new_width, new_height, LV_WL_SHM_BUF_COUNT); lv_wl_shm_display_data_t * ddata = shm_create_display_data(ctx, display, new_width, new_height);
if(!ddata) { if(!ddata) {
LV_LOG_ERROR("Failed to allocate data for new display resolution"); LV_LOG_ERROR("Failed to allocate data for new display resolution");
@@ -304,7 +339,7 @@ static void * shm_resize_display(void * backend_ctx, lv_display_t * display)
} }
lv_wl_shm_display_data_t * curr_ddata = lv_wayland_get_backend_display_data(display); lv_wl_shm_display_data_t * curr_ddata = lv_wayland_get_backend_display_data(display);
shm_destroy_display_data(curr_ddata); shm_delete_display_data(curr_ddata);
return ddata; return ddata;
} }
@@ -316,7 +351,7 @@ static void shm_deinit_display(void * backend_ctx, lv_display_t * display)
if(!ddata) { if(!ddata) {
return; return;
} }
shm_destroy_display_data(ddata); shm_delete_display_data(ddata);
} }
static int create_shm_file(size_t size) static int create_shm_file(size_t size)
@@ -351,10 +386,8 @@ static void shm_global_handler(void * backend_ctx, struct wl_registry * registry
ctx->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); ctx->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
} }
} }
static void shm_flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map) static void shm_flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map)
{ {
LV_UNUSED(px_map);
lv_wl_shm_display_data_t * ddata = lv_wayland_get_backend_display_data(disp); lv_wl_shm_display_data_t * ddata = lv_wayland_get_backend_display_data(disp);
struct wl_surface * surface = lv_wayland_get_window_surface(disp); struct wl_surface * surface = lv_wayland_get_window_surface(disp);
if(!surface) { if(!surface) {
@@ -362,32 +395,58 @@ static void shm_flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t *
return; return;
} }
int32_t w = lv_area_get_width(area); const lv_display_rotation_t rotation = lv_display_get_rotation(disp);
int32_t h = lv_area_get_height(area); const lv_color_format_t cf = lv_display_get_color_format(disp);
lv_color_format_t cf = lv_display_get_color_format(disp);
/* When using ARGB8888, the compositor expects premultiplied ARGB8888 so premultiply it here*/ /* When using ARGB8888, the compositor expects premultiplied ARGB8888 so premultiply it here*/
if(ddata->shm_cf == WL_SHM_FORMAT_ARGB8888 && cf != LV_COLOR_FORMAT_ARGB8888_PREMULTIPLIED) { if(ddata->shm_cf == WL_SHM_FORMAT_ARGB8888 && cf != LV_COLOR_FORMAT_ARGB8888_PREMULTIPLIED) {
const int32_t w = lv_area_get_width(area);
const int32_t h = lv_area_get_height(area);
size_t index = 0; size_t index = 0;
for(int32_t y = area->y1; y <= area->y2; ++y) { for(int32_t y = 0; y < h; ++y) {
for(int32_t x = area->x1; x <= area->x2; ++x) { for(int32_t x = 0; x < w; ++x) {
lv_color_premultiply((lv_color32_t *) px_map + (index++)); lv_color_premultiply((lv_color32_t *) px_map + (index++));
} }
} }
} }
wl_surface_damage(surface, area->x1, area->y1, w, h);
if(!lv_display_flush_is_last(disp)) { if(!lv_display_flush_is_last(disp)) {
lv_display_flush_ready(disp); lv_display_flush_ready(disp);
return; return;
} }
struct wl_callback * callback = wl_surface_frame(surface);
wl_callback_add_listener(callback, &frame_listener, disp);
lv_wl_buffer_t * buffer = &ddata->buffers[ddata->curr_wl_buffer_idx]; lv_wl_buffer_t * buffer = &ddata->buffers[ddata->curr_wl_buffer_idx];
if(buffer->busy) { if(buffer->busy) {
LV_LOG_WARN("Failed to acquire a non-busy buffer"); LV_LOG_WARN("Failed to acquire a non-busy buffer");
} }
/* If we have rotation, copy from rotated_buf to Wayland buffer */
if(rotation != LV_DISPLAY_ROTATION_0) {
const int32_t hor_res = lv_display_get_horizontal_resolution(disp);
const int32_t ver_res = lv_display_get_vertical_resolution(disp);
const uint32_t src_stride = lv_draw_buf_width_to_stride(hor_res, cf);
const int32_t phy_width = lv_display_get_original_horizontal_resolution(disp);
const int32_t phy_height = lv_display_get_original_vertical_resolution(disp);
const uint32_t dest_stride = lv_draw_buf_width_to_stride(phy_width, cf);
size_t buf_size = dest_stride * phy_height;
uint8_t * wl_buf = (uint8_t *)ddata->mmap_ptr + (ddata->curr_wl_buffer_idx * buf_size);
lv_draw_sw_rotate(ddata->rotated_buf, wl_buf, hor_res, ver_res,
src_stride, dest_stride, rotation, cf);
wl_surface_damage(surface, 0, 0, phy_width, phy_height);
}
else {
const int32_t w = lv_area_get_width(area);
const int32_t h = lv_area_get_height(area);
wl_surface_damage(surface, area->x1, area->y1, w, h);
}
struct wl_callback * callback = wl_surface_frame(surface);
wl_callback_add_listener(callback, &frame_listener, disp);
wl_surface_attach(surface, buffer->wl_buffer, 0, 0); wl_surface_attach(surface, buffer->wl_buffer, 0, 0);
wl_surface_commit(surface); wl_surface_commit(surface);