diff --git a/src/video/wayland/SDL_waylandevents_c.h b/src/video/wayland/SDL_waylandevents_c.h index 03c44375f6..077c2659f4 100644 --- a/src/video/wayland/SDL_waylandevents_c.h +++ b/src/video/wayland/SDL_waylandevents_c.h @@ -182,10 +182,11 @@ typedef struct SDL_WaylandSeat struct wl_surface *surface; struct wp_viewport *viewport; - // Animation state for legacy animated cursors + // Animation state for cursors + void *cursor_handle; struct wl_callback *frame_callback; - Uint64 last_frame_callback_time_ns; - Uint64 current_frame_time_ns; + Uint64 last_frame_callback_time_ms; + Uint32 current_frame_time_ms; int current_frame; } cursor_state; } pointer; diff --git a/src/video/wayland/SDL_waylandmouse.c b/src/video/wayland/SDL_waylandmouse.c index e92adedd71..3ff173c70e 100644 --- a/src/video/wayland/SDL_waylandmouse.c +++ b/src/video/wayland/SDL_waylandmouse.c @@ -48,42 +48,45 @@ static bool Wayland_SetRelativeMouseMode(bool enabled); typedef struct { - struct Wayland_SHMBuffer shmBuffer; + Wayland_SHMPool *shmPool; double scale; struct wl_list node; } Wayland_ScaledCustomCursor; typedef struct { - SDL_Surface *sdl_cursor_surface; int hot_x; int hot_y; struct wl_list scaled_cursor_cache; + SDL_Surface *sdl_cursor_surfaces[]; } Wayland_CustomCursor; typedef struct { - struct wl_buffer *wl_buffer; - Uint64 duration_ns; -} Wayland_SystemCursorFrame; + int size; + struct wl_list node; + struct wl_buffer *buffers[]; +} Wayland_CachedSystemCursor; typedef struct { - Wayland_SystemCursorFrame *frames; - Uint64 total_duration_ns; - int num_frames; SDL_SystemCursor id; + struct wl_list cursor_buffer_cache; } Wayland_SystemCursor; struct SDL_CursorData { + // Cursor animation data. + Uint32 *frame_durations_ms; + Uint32 total_duration_ms; + int num_frames; + bool is_system_cursor; + union { Wayland_CustomCursor custom; Wayland_SystemCursor system; } cursor_data; - - bool is_system_cursor; }; static int dbus_cursor_size; @@ -285,6 +288,21 @@ static void Wayland_DBusFinishCursorProperties(void) #endif +static struct wl_buffer *Wayland_SeatGetCursorFrame(SDL_WaylandSeat *seat, int frame_index) +{ + SDL_CursorData *data = seat->pointer.current_cursor; + + if (data) { + if (!data->is_system_cursor) { + return ((Wayland_ScaledCustomCursor *)(seat->pointer.cursor_state.cursor_handle))->shmPool->buffers[frame_index].wl_buffer; + } else { + return ((Wayland_CachedSystemCursor *)(seat->pointer.cursor_state.cursor_handle))->buffers[frame_index]; + } + } + + return NULL; +} + static void cursor_frame_done(void *data, struct wl_callback *cb, uint32_t time); struct wl_callback_listener cursor_frame_listener = { cursor_frame_done @@ -293,34 +311,42 @@ struct wl_callback_listener cursor_frame_listener = { static void cursor_frame_done(void *data, struct wl_callback *cb, uint32_t time) { SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; - SDL_CursorData *c = (struct SDL_CursorData *)seat->pointer.current_cursor; + if (!seat->pointer.current_cursor) { + return; + } - const Uint64 now = SDL_GetTicksNS(); - const Uint64 elapsed = (now - seat->pointer.cursor_state.last_frame_callback_time_ns) % c->cursor_data.system.total_duration_ns; - Uint64 advance = 0; + Uint32 *frames = seat->pointer.current_cursor->frame_durations_ms; + SDL_CursorData *c = seat->pointer.current_cursor; + + const Uint64 now = SDL_GetTicks(); + const Uint32 elapsed = (now - seat->pointer.cursor_state.last_frame_callback_time_ms) % c->total_duration_ms; + Uint32 advance = 0; int next = seat->pointer.cursor_state.current_frame; wl_callback_destroy(cb); seat->pointer.cursor_state.frame_callback = wl_surface_frame(seat->pointer.cursor_state.surface); wl_callback_add_listener(seat->pointer.cursor_state.frame_callback, &cursor_frame_listener, data); - seat->pointer.cursor_state.current_frame_time_ns += elapsed; + seat->pointer.cursor_state.current_frame_time_ms += elapsed; // Calculate the next frame based on the elapsed duration. - for (Uint64 t = c->cursor_data.system.frames[next].duration_ns; t <= seat->pointer.cursor_state.current_frame_time_ns; t += c->cursor_data.system.frames[next].duration_ns) { - next = (next + 1) % c->cursor_data.system.num_frames; + for (Uint32 t = frames[next]; t <= seat->pointer.cursor_state.current_frame_time_ms; t += frames[next]) { + next = (next + 1) % c->num_frames; advance = t; // Make sure we don't end up in an infinite loop if a cursor has frame durations of 0. - if (!c->cursor_data.system.frames[next].duration_ns) { + if (!frames[next]) { break; } } - seat->pointer.cursor_state.current_frame_time_ns -= advance; - seat->pointer.cursor_state.last_frame_callback_time_ns = now; + seat->pointer.cursor_state.current_frame_time_ms -= advance; + seat->pointer.cursor_state.last_frame_callback_time_ms = now; seat->pointer.cursor_state.current_frame = next; - wl_surface_attach(seat->pointer.cursor_state.surface, c->cursor_data.system.frames[next].wl_buffer, 0, 0); + + struct wl_buffer *buffer = Wayland_SeatGetCursorFrame(seat, next); + wl_surface_attach(seat->pointer.cursor_state.surface, buffer, 0, 0); + if (wl_surface_get_version(seat->pointer.cursor_state.surface) >= WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION) { wl_surface_damage_buffer(seat->pointer.cursor_state.surface, 0, 0, SDL_MAX_SINT32, SDL_MAX_SINT32); } else { @@ -329,10 +355,39 @@ static void cursor_frame_done(void *data, struct wl_callback *cb, uint32_t time) wl_surface_commit(seat->pointer.cursor_state.surface); } -static bool Wayland_GetSystemCursor(SDL_VideoData *vdata, SDL_CursorData *cdata, int *scale, int *dst_size, int *hot_x, int *hot_y) +static Wayland_CachedSystemCursor *Wayland_CacheSystemCursor(SDL_CursorData *cdata, struct wl_cursor *cursor, int size) { + Wayland_CachedSystemCursor *cache = NULL; + + // Is this cursor already cached at the target scale? + if (!WAYLAND_wl_list_empty(&cdata->cursor_data.system.cursor_buffer_cache)) { + Wayland_CachedSystemCursor *c = NULL; + wl_list_for_each (c, &cdata->cursor_data.system.cursor_buffer_cache, node) { + if (c->size == size) { + cache = c; + break; + } + } + } + + if (!cache) { + cache = SDL_calloc(1, sizeof(Wayland_CachedSystemCursor) + (sizeof(struct wl_buffer *) * cdata->num_frames)); + + cache->size = size; + for (int i = 0; i < cdata->num_frames; ++i) { + cache->buffers[i] = WAYLAND_wl_cursor_image_get_buffer(cursor->images[i]); + } + + WAYLAND_wl_list_insert(&cdata->cursor_data.system.cursor_buffer_cache, &cache->node); + } + + return cache; +} + +static bool Wayland_GetSystemCursor(SDL_CursorData *cdata, SDL_WaylandSeat *seat, int *scale, int *dst_size, int *hot_x, int *hot_y) +{ + SDL_VideoData *vdata = seat->display; struct wl_cursor_theme *theme = NULL; - struct wl_cursor *cursor; const char *css_name = "default"; const char *fallback_name = NULL; double scale_factor = 1.0; @@ -383,7 +438,7 @@ static bool Wayland_GetSystemCursor(SDL_VideoData *vdata, SDL_CursorData *cdata, } css_name = SDL_GetCSSCursorName(cdata->cursor_data.system.id, &fallback_name); - cursor = WAYLAND_wl_cursor_theme_get_cursor(theme, css_name); + struct wl_cursor *cursor = WAYLAND_wl_cursor_theme_get_cursor(theme, css_name); if (!cursor && fallback_name) { cursor = WAYLAND_wl_cursor_theme_get_cursor(theme, fallback_name); } @@ -400,21 +455,19 @@ static bool Wayland_GetSystemCursor(SDL_VideoData *vdata, SDL_CursorData *cdata, return false; } - if (cdata->cursor_data.system.num_frames != cursor->image_count) { - SDL_free(cdata->cursor_data.system.frames); - cdata->cursor_data.system.frames = SDL_calloc(cursor->image_count, sizeof(Wayland_SystemCursorFrame)); - if (!cdata->cursor_data.system.frames) { - return false; - } - } - // ... Set the cursor data, finally. - cdata->cursor_data.system.num_frames = cursor->image_count; - cdata->cursor_data.system.total_duration_ns = 0; - for (int i = 0; i < cursor->image_count; ++i) { - cdata->cursor_data.system.frames[i].wl_buffer = WAYLAND_wl_cursor_image_get_buffer(cursor->images[i]); - cdata->cursor_data.system.frames[i].duration_ns = SDL_MS_TO_NS((Uint64)cursor->images[i]->delay); - cdata->cursor_data.system.total_duration_ns += cdata->cursor_data.system.frames[i].duration_ns; + cdata->num_frames = cursor->image_count; + Wayland_CachedSystemCursor *c = Wayland_CacheSystemCursor(cdata, cursor, theme_size); + seat->pointer.cursor_state.cursor_handle = c; + + if (cursor->image_count > 1 && !cdata->frame_durations_ms) { + cdata->total_duration_ms = 0; + cdata->frame_durations_ms = SDL_calloc(cursor->image_count, sizeof(Uint32)); + + for (int i = 0; i < cursor->image_count; ++i) { + cdata->frame_durations_ms[i] = cursor->images[i]->delay; + cdata->total_duration_ms += cursor->images[i]->delay; + } } *scale = SDL_ceil(scale_factor) == scale_factor ? (int)scale_factor : 0; @@ -447,14 +500,14 @@ static bool Wayland_GetSystemCursor(SDL_VideoData *vdata, SDL_CursorData *cdata, return true; } -static Wayland_ScaledCustomCursor *Wayland_CacheScaledCustomCursor(SDL_CursorData *cdata, double scale) +static Wayland_ScaledCustomCursor *Wayland_CacheScaledCustomCursor(SDL_CursorData *cursor, double scale) { Wayland_ScaledCustomCursor *cache = NULL; // Is this cursor already cached at the target scale? - if (!WAYLAND_wl_list_empty(&cdata->cursor_data.custom.scaled_cursor_cache)) { + if (!WAYLAND_wl_list_empty(&cursor->cursor_data.custom.scaled_cursor_cache)) { Wayland_ScaledCustomCursor *c = NULL; - wl_list_for_each (c, &cdata->cursor_data.custom.scaled_cursor_cache, node) { + wl_list_for_each (c, &cursor->cursor_data.custom.scaled_cursor_cache, node) { if (c->scale == scale) { cache = c; break; @@ -468,41 +521,50 @@ static Wayland_ScaledCustomCursor *Wayland_CacheScaledCustomCursor(SDL_CursorDat return NULL; } - SDL_Surface *surface = SDL_GetSurfaceImage(cdata->cursor_data.custom.sdl_cursor_surface, (float)scale); + SDL_Surface *surface = SDL_GetSurfaceImage(cursor->cursor_data.custom.sdl_cursor_surfaces[0], (float)scale); if (!surface) { SDL_free(cache); return NULL; } // Allocate the shared memory buffer for this cursor. - if (!Wayland_AllocSHMBuffer(surface->w, surface->h, &cache->shmBuffer)) { + cache->shmPool = Wayland_AllocSHMPool(surface->w, surface->h, cursor->num_frames); + if (!cache->shmPool) { SDL_free(cache); SDL_DestroySurface(surface); return NULL; } - // Wayland requires premultiplied alpha for its surfaces. - SDL_PremultiplyAlpha(surface->w, surface->h, - surface->format, surface->pixels, surface->pitch, - SDL_PIXELFORMAT_ARGB8888, cache->shmBuffer.shm_data, surface->w * 4, true); + for (int i = 0; i < cursor->num_frames; ++i) { + if (!surface) { + surface = SDL_GetSurfaceImage(cursor->cursor_data.custom.sdl_cursor_surfaces[i], (float)scale); + } + + // Wayland requires premultiplied alpha for its surfaces. + SDL_PremultiplyAlpha(surface->w, surface->h, + surface->format, surface->pixels, surface->pitch, + SDL_PIXELFORMAT_ARGB8888, cache->shmPool->buffers[i].shm_data, surface->w * 4, true); + + SDL_DestroySurface(surface); + surface = NULL; + } cache->scale = scale; - WAYLAND_wl_list_insert(&cdata->cursor_data.custom.scaled_cursor_cache, &cache->node); - SDL_DestroySurface(surface); + WAYLAND_wl_list_insert(&cursor->cursor_data.custom.scaled_cursor_cache, &cache->node); } return cache; } -static bool Wayland_GetCustomCursor(SDL_Cursor *cursor, struct wl_buffer **buffer, int *scale, int *dst_width, int *dst_height, int *hot_x, int *hot_y) +static bool Wayland_GetCustomCursor(SDL_CursorData *cursor, SDL_WaylandSeat *seat, int *scale, int *dst_width, int *dst_height, int *hot_x, int *hot_y) { SDL_VideoDevice *vd = SDL_GetVideoDevice(); SDL_VideoData *wd = vd->internal; - SDL_CursorData *data = cursor->internal; + Wayland_CustomCursor *custom_cursor = &cursor->cursor_data.custom; SDL_Window *focus = SDL_GetMouseFocus(); double scale_factor = 1.0; - if (focus && SDL_SurfaceHasAlternateImages(data->cursor_data.custom.sdl_cursor_surface)) { + if (focus && SDL_SurfaceHasAlternateImages(custom_cursor->sdl_cursor_surfaces[0])) { scale_factor = focus->internal->scale_factor; } @@ -511,17 +573,17 @@ static bool Wayland_GetCustomCursor(SDL_Cursor *cursor, struct wl_buffer **buffe scale_factor = SDL_ceil(scale_factor); } - Wayland_ScaledCustomCursor *c = Wayland_CacheScaledCustomCursor(data, scale_factor); + Wayland_ScaledCustomCursor *c = Wayland_CacheScaledCustomCursor(cursor, scale_factor); if (!c) { return false; } - *buffer = c->shmBuffer.wl_buffer; + seat->pointer.cursor_state.cursor_handle = c; *scale = SDL_ceil(scale_factor) == scale_factor ? (int)scale_factor : 0; - *dst_width = data->cursor_data.custom.sdl_cursor_surface->w; - *dst_height = data->cursor_data.custom.sdl_cursor_surface->h; - *hot_x = data->cursor_data.custom.hot_x; - *hot_y = data->cursor_data.custom.hot_y; + *dst_width = custom_cursor->sdl_cursor_surfaces[0]->w; + *dst_height = custom_cursor->sdl_cursor_surfaces[0]->h; + *hot_x = custom_cursor->hot_x; + *hot_y = custom_cursor->hot_y; return true; } @@ -531,17 +593,18 @@ static SDL_Cursor *Wayland_CreateCursor(SDL_Surface *surface, int hot_x, int hot SDL_Cursor *cursor = SDL_calloc(1, sizeof(*cursor)); if (cursor) { - SDL_CursorData *data = SDL_calloc(1, sizeof(*data)); + SDL_CursorData *data = SDL_calloc(1, sizeof(*data) + sizeof(SDL_Surface *)); if (!data) { SDL_free(cursor); return NULL; } cursor->internal = data; WAYLAND_wl_list_init(&data->cursor_data.custom.scaled_cursor_cache); + data->num_frames = 1; data->cursor_data.custom.hot_x = hot_x; data->cursor_data.custom.hot_y = hot_y; - data->cursor_data.custom.sdl_cursor_surface = surface; + data->cursor_data.custom.sdl_cursor_surfaces[0] = surface; ++surface->refcount; // If the cursor has only one size, just prepare it now. @@ -565,6 +628,7 @@ static SDL_Cursor *Wayland_CreateSystemCursor(SDL_SystemCursor id) } cursor->internal = cdata; + WAYLAND_wl_list_init(&cdata->cursor_data.system.cursor_buffer_cache); cdata->cursor_data.system.id = id; cdata->is_system_cursor = true; } @@ -596,22 +660,29 @@ static void Wayland_FreeCursorData(SDL_CursorData *d) wl_surface_attach(seat->pointer.cursor_state.surface, NULL, 0, 0); } + seat->pointer.current_cursor = NULL; } } - // Buffers for system cursors must not be destroyed. if (d->is_system_cursor) { - SDL_free(d->cursor_data.system.frames); + Wayland_CachedSystemCursor *c, *temp; + wl_list_for_each_safe(c, temp, &d->cursor_data.system.cursor_buffer_cache, node) { + SDL_free(c); + } } else { Wayland_ScaledCustomCursor *c, *temp; wl_list_for_each_safe(c, temp, &d->cursor_data.custom.scaled_cursor_cache, node) { - Wayland_ReleaseSHMBuffer(&c->shmBuffer); + Wayland_ReleaseSHMPool(c->shmPool); SDL_free(c); } - SDL_DestroySurface(d->cursor_data.custom.sdl_cursor_surface); + for (int i = 0; i < d->num_frames; ++i) { + SDL_DestroySurface(d->cursor_data.custom.sdl_cursor_surfaces[i]); + } } + + SDL_free(d->frame_durations_ms); } static void Wayland_FreeCursor(SDL_Cursor *cursor) @@ -707,13 +778,12 @@ static void Wayland_SetSystemCursorShape(SDL_WaylandSeat *seat, SDL_SystemCursor static void Wayland_SeatSetCursor(SDL_WaylandSeat *seat, SDL_Cursor *cursor) { if (seat->pointer.wl_pointer) { - struct wl_buffer *buffer = NULL; + SDL_CursorData *cursor_data = cursor ? cursor->internal : NULL; int scale = 1; int dst_width = 0; int dst_height = 0; int hot_x; int hot_y; - SDL_CursorData *cursor_data = cursor ? cursor->internal : NULL; // Stop the frame callback for old animated cursors. if (seat->pointer.cursor_state.frame_callback && cursor_data != seat->pointer.current_cursor) { @@ -746,34 +816,33 @@ static void Wayland_SeatSetCursor(SDL_WaylandSeat *seat, SDL_Cursor *cursor) return; } - if (!Wayland_GetSystemCursor(seat->display, cursor_data, &scale, &dst_width, &hot_x, &hot_y)) { + if (!Wayland_GetSystemCursor(cursor_data, seat, &scale, &dst_width, &hot_x, &hot_y)) { return; } dst_height = dst_width; - - if (!seat->pointer.cursor_state.surface) { - seat->pointer.cursor_state.surface = wl_compositor_create_surface(seat->display->compositor); - } - wl_surface_attach(seat->pointer.cursor_state.surface, cursor_data->cursor_data.system.frames[0].wl_buffer, 0, 0); - - // If more than one frame is available, create a frame callback to run the animation. - if (cursor_data->cursor_data.system.num_frames > 1) { - seat->pointer.cursor_state.last_frame_callback_time_ns = SDL_GetTicks(); - seat->pointer.cursor_state.current_frame_time_ns = 0; - seat->pointer.cursor_state.current_frame = 0; - seat->pointer.cursor_state.frame_callback = wl_surface_frame(seat->pointer.cursor_state.surface); - wl_callback_add_listener(seat->pointer.cursor_state.frame_callback, &cursor_frame_listener, seat); - } } else { - if (!Wayland_GetCustomCursor(cursor, &buffer, &scale, &dst_width, &dst_height, &hot_x, &hot_y)) { + if (!Wayland_GetCustomCursor(cursor_data, seat, &scale, &dst_width, &dst_height, &hot_x, &hot_y)) { return; } + } - if (!seat->pointer.cursor_state.surface) { - seat->pointer.cursor_state.surface = wl_compositor_create_surface(seat->display->compositor); - } - wl_surface_attach(seat->pointer.cursor_state.surface, buffer, 0, 0); + seat->pointer.current_cursor = cursor_data; + + if (!seat->pointer.cursor_state.surface) { + seat->pointer.cursor_state.surface = wl_compositor_create_surface(seat->display->compositor); + } + + struct wl_buffer *buffer = Wayland_SeatGetCursorFrame(seat, 0); + wl_surface_attach(seat->pointer.cursor_state.surface, buffer, 0, 0); + + // If more than one frame is available, create a frame callback to run the animation. + if (cursor_data->num_frames > 1) { + seat->pointer.cursor_state.last_frame_callback_time_ms = SDL_GetTicks(); + seat->pointer.cursor_state.current_frame_time_ms = 0; + seat->pointer.cursor_state.current_frame = 0; + seat->pointer.cursor_state.frame_callback = wl_surface_frame(seat->pointer.cursor_state.surface); + wl_callback_add_listener(seat->pointer.cursor_state.frame_callback, &cursor_frame_listener, seat); } // A scale value of 0 indicates that a viewport with the returned destination size should be used. @@ -800,7 +869,6 @@ static void Wayland_SeatSetCursor(SDL_WaylandSeat *seat, SDL_Cursor *cursor) wl_surface_damage(seat->pointer.cursor_state.surface, 0, 0, SDL_MAX_SINT32, SDL_MAX_SINT32); } - seat->pointer.current_cursor = cursor_data; wl_surface_commit(seat->pointer.cursor_state.surface); } else { seat->pointer.current_cursor = NULL; diff --git a/src/video/wayland/SDL_waylandshmbuffer.c b/src/video/wayland/SDL_waylandshmbuffer.c index 8fc6d9e5b5..77b3c84291 100644 --- a/src/video/wayland/SDL_waylandshmbuffer.c +++ b/src/video/wayland/SDL_waylandshmbuffer.c @@ -114,11 +114,10 @@ static struct wl_buffer_listener buffer_listener = { buffer_handle_release }; -bool Wayland_AllocSHMBuffer(int width, int height, struct Wayland_SHMBuffer *shmBuffer) +bool Wayland_AllocSHMBuffer(int width, int height, Wayland_SHMBuffer *shmBuffer) { SDL_VideoDevice *vd = SDL_GetVideoDevice(); SDL_VideoData *data = vd->internal; - struct wl_shm_pool *shm_pool; const Uint32 SHM_FMT = WL_SHM_FORMAT_ARGB8888; if (!shmBuffer) { @@ -142,7 +141,7 @@ bool Wayland_AllocSHMBuffer(int width, int height, struct Wayland_SHMBuffer *shm SDL_assert(shmBuffer->shm_data != NULL); - shm_pool = wl_shm_create_pool(data->shm, shm_fd, shmBuffer->shm_data_size); + struct wl_shm_pool *shm_pool = wl_shm_create_pool(data->shm, shm_fd, shmBuffer->shm_data_size); shmBuffer->wl_buffer = wl_shm_pool_create_buffer(shm_pool, 0, width, height, stride, SHM_FMT); wl_buffer_add_listener(shmBuffer->wl_buffer, &buffer_listener, shmBuffer); @@ -152,7 +151,7 @@ bool Wayland_AllocSHMBuffer(int width, int height, struct Wayland_SHMBuffer *shm return true; } -void Wayland_ReleaseSHMBuffer(struct Wayland_SHMBuffer *shmBuffer) +void Wayland_ReleaseSHMBuffer(Wayland_SHMBuffer *shmBuffer) { if (shmBuffer) { if (shmBuffer->wl_buffer) { @@ -167,4 +166,75 @@ void Wayland_ReleaseSHMBuffer(struct Wayland_SHMBuffer *shmBuffer) } } +Wayland_SHMPool *Wayland_AllocSHMPool(int width, int height, int buffer_count) +{ + SDL_VideoDevice *vd = SDL_GetVideoDevice(); + SDL_VideoData *data = vd->internal; + const Uint32 SHM_FMT = WL_SHM_FORMAT_ARGB8888; + + if (buffer_count <= 0) { + SDL_InvalidParamError("count"); + return NULL; + } + + Wayland_SHMPool *shmPool = SDL_calloc(buffer_count, sizeof(Wayland_SHMPool) + (sizeof(Wayland_SHMBuffer) * buffer_count)); + if (!shmPool) { + return NULL; + } + + const int stride = width * 4; + const int element_size = stride * height; + const int element_offset = (element_size + 15) & (~15); + shmPool->internal.shm_pool_size = element_offset * buffer_count; + shmPool->buffer_count = buffer_count; + + const int shm_fd = CreateTempFD(shmPool->internal.shm_pool_size); + if (shm_fd < 0) { + SDL_free(shmPool); + SDL_SetError("Creating SHM buffer failed."); + return NULL; + } + + shmPool->internal.shm_pool_handle = mmap(NULL, shmPool->internal.shm_pool_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); + if (shmPool->internal.shm_pool_handle == MAP_FAILED) { + shmPool->internal.shm_pool_handle = NULL; + close(shm_fd); + SDL_free(shmPool); + SDL_SetError("mmap() failed."); + return NULL; + } + + SDL_assert(shmPool->internal.shm_pool_handle != NULL); + + struct wl_shm_pool *shm_pool = wl_shm_create_pool(data->shm, shm_fd, shmPool->internal.shm_pool_size); + + for (size_t i = 0; i < buffer_count; i++) { + shmPool->buffers[i].shm_data = (Uint8 *)shmPool->internal.shm_pool_handle + (element_offset * i); + shmPool->buffers[i].wl_buffer = wl_shm_pool_create_buffer(shm_pool, element_offset * i, width, height, stride, SHM_FMT); + wl_buffer_add_listener(shmPool->buffers[i].wl_buffer, &buffer_listener, shmPool); + } + + wl_shm_pool_destroy(shm_pool); + close(shm_fd); + + return shmPool; +} + +void Wayland_ReleaseSHMPool(Wayland_SHMPool *shmPool) +{ + if (shmPool) { + for (int i = 0; i < shmPool->buffer_count; ++i) { + if (shmPool->buffers[i].wl_buffer) { + wl_buffer_destroy(shmPool->buffers[i].wl_buffer); + } + } + + if (shmPool->internal.shm_pool_handle) { + munmap(shmPool->internal.shm_pool_handle, shmPool->internal.shm_pool_size); + } + + SDL_free(shmPool); + } +} + #endif diff --git a/src/video/wayland/SDL_waylandshmbuffer.h b/src/video/wayland/SDL_waylandshmbuffer.h index f6f49a4e3b..4410fd6e56 100644 --- a/src/video/wayland/SDL_waylandshmbuffer.h +++ b/src/video/wayland/SDL_waylandshmbuffer.h @@ -24,15 +24,30 @@ #ifndef SDL_waylandshmbuffer_h_ #define SDL_waylandshmbuffer_h_ -struct Wayland_SHMBuffer +typedef struct { struct wl_buffer *wl_buffer; void *shm_data; int shm_data_size; -}; +} Wayland_SHMBuffer; + +typedef struct +{ + struct + { + void *shm_pool_handle; + int shm_pool_size; + } internal; + + int buffer_count; + Wayland_SHMBuffer buffers[]; +} Wayland_SHMPool; // Allocates an SHM buffer with the format WL_SHM_FORMAT_ARGB8888 -extern bool Wayland_AllocSHMBuffer(int width, int height, struct Wayland_SHMBuffer *shmBuffer); -extern void Wayland_ReleaseSHMBuffer(struct Wayland_SHMBuffer *shmBuffer); +extern bool Wayland_AllocSHMBuffer(int width, int height, Wayland_SHMBuffer *shmBuffer); +extern void Wayland_ReleaseSHMBuffer(Wayland_SHMBuffer *shmBuffer); + +extern Wayland_SHMPool *Wayland_AllocSHMPool(int width, int height, int buffer_count); +extern void Wayland_ReleaseSHMPool(Wayland_SHMPool *shmPool); #endif diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c index 5917f2552c..46337c89e1 100644 --- a/src/video/wayland/SDL_waylandwindow.c +++ b/src/video/wayland/SDL_waylandwindow.c @@ -2988,14 +2988,14 @@ bool Wayland_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surfa wind->icon_buffer_count = 0; wind->xdg_toplevel_icon_v1 = xdg_toplevel_icon_manager_v1_create_icon(_this->internal->xdg_toplevel_icon_manager_v1); - wind->icon_buffers = SDL_calloc(image_count, sizeof(struct Wayland_SHMBuffer)); + wind->icon_buffers = SDL_calloc(image_count, sizeof(Wayland_SHMBuffer)); if (!wind->icon_buffers) { goto failure_cleanup; } for (int i = 0; i < image_count; ++i) { if (images[i]->w == images[i]->h) { - struct Wayland_SHMBuffer *buffer = &wind->icon_buffers[wind->icon_buffer_count]; + Wayland_SHMBuffer *buffer = &wind->icon_buffers[wind->icon_buffer_count]; if (!Wayland_AllocSHMBuffer(images[i]->w, images[i]->h, buffer)) { SDL_SetError("wayland: failed to allocate SHM buffer for the icon"); diff --git a/src/video/wayland/SDL_waylandwindow.h b/src/video/wayland/SDL_waylandwindow.h index e16f2d9d1a..50f8c528d0 100644 --- a/src/video/wayland/SDL_waylandwindow.h +++ b/src/video/wayland/SDL_waylandwindow.h @@ -127,7 +127,7 @@ struct SDL_WindowData char *app_id; double scale_factor; - struct Wayland_SHMBuffer *icon_buffers; + Wayland_SHMBuffer *icon_buffers; int icon_buffer_count; // Keyboard, pointer, and touch focus refcount.