wayland: Refactor animated cursor handling

Generalizes the animated cursor info, so it can be used for custom cursors as well.
This commit is contained in:
Frank Praznik
2025-10-10 13:41:50 -04:00
parent 2ffabb5150
commit 69692de8b8
6 changed files with 255 additions and 101 deletions

View File

@@ -182,10 +182,11 @@ typedef struct SDL_WaylandSeat
struct wl_surface *surface; struct wl_surface *surface;
struct wp_viewport *viewport; struct wp_viewport *viewport;
// Animation state for legacy animated cursors // Animation state for cursors
void *cursor_handle;
struct wl_callback *frame_callback; struct wl_callback *frame_callback;
Uint64 last_frame_callback_time_ns; Uint64 last_frame_callback_time_ms;
Uint64 current_frame_time_ns; Uint32 current_frame_time_ms;
int current_frame; int current_frame;
} cursor_state; } cursor_state;
} pointer; } pointer;

View File

@@ -48,42 +48,45 @@ static bool Wayland_SetRelativeMouseMode(bool enabled);
typedef struct typedef struct
{ {
struct Wayland_SHMBuffer shmBuffer; Wayland_SHMPool *shmPool;
double scale; double scale;
struct wl_list node; struct wl_list node;
} Wayland_ScaledCustomCursor; } Wayland_ScaledCustomCursor;
typedef struct typedef struct
{ {
SDL_Surface *sdl_cursor_surface;
int hot_x; int hot_x;
int hot_y; int hot_y;
struct wl_list scaled_cursor_cache; struct wl_list scaled_cursor_cache;
SDL_Surface *sdl_cursor_surfaces[];
} Wayland_CustomCursor; } Wayland_CustomCursor;
typedef struct typedef struct
{ {
struct wl_buffer *wl_buffer; int size;
Uint64 duration_ns; struct wl_list node;
} Wayland_SystemCursorFrame; struct wl_buffer *buffers[];
} Wayland_CachedSystemCursor;
typedef struct typedef struct
{ {
Wayland_SystemCursorFrame *frames;
Uint64 total_duration_ns;
int num_frames;
SDL_SystemCursor id; SDL_SystemCursor id;
struct wl_list cursor_buffer_cache;
} Wayland_SystemCursor; } Wayland_SystemCursor;
struct SDL_CursorData struct SDL_CursorData
{ {
// Cursor animation data.
Uint32 *frame_durations_ms;
Uint32 total_duration_ms;
int num_frames;
bool is_system_cursor;
union union
{ {
Wayland_CustomCursor custom; Wayland_CustomCursor custom;
Wayland_SystemCursor system; Wayland_SystemCursor system;
} cursor_data; } cursor_data;
bool is_system_cursor;
}; };
static int dbus_cursor_size; static int dbus_cursor_size;
@@ -285,6 +288,21 @@ static void Wayland_DBusFinishCursorProperties(void)
#endif #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); static void cursor_frame_done(void *data, struct wl_callback *cb, uint32_t time);
struct wl_callback_listener cursor_frame_listener = { struct wl_callback_listener cursor_frame_listener = {
cursor_frame_done 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) static void cursor_frame_done(void *data, struct wl_callback *cb, uint32_t time)
{ {
SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data; 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(); Uint32 *frames = seat->pointer.current_cursor->frame_durations_ms;
const Uint64 elapsed = (now - seat->pointer.cursor_state.last_frame_callback_time_ns) % c->cursor_data.system.total_duration_ns; SDL_CursorData *c = seat->pointer.current_cursor;
Uint64 advance = 0;
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; int next = seat->pointer.cursor_state.current_frame;
wl_callback_destroy(cb); wl_callback_destroy(cb);
seat->pointer.cursor_state.frame_callback = wl_surface_frame(seat->pointer.cursor_state.surface); 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); 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. // 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) { for (Uint32 t = frames[next]; t <= seat->pointer.cursor_state.current_frame_time_ms; t += frames[next]) {
next = (next + 1) % c->cursor_data.system.num_frames; next = (next + 1) % c->num_frames;
advance = t; advance = t;
// Make sure we don't end up in an infinite loop if a cursor has frame durations of 0. // 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; break;
} }
} }
seat->pointer.cursor_state.current_frame_time_ns -= advance; seat->pointer.cursor_state.current_frame_time_ms -= advance;
seat->pointer.cursor_state.last_frame_callback_time_ns = now; seat->pointer.cursor_state.last_frame_callback_time_ms = now;
seat->pointer.cursor_state.current_frame = next; 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) { 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); wl_surface_damage_buffer(seat->pointer.cursor_state.surface, 0, 0, SDL_MAX_SINT32, SDL_MAX_SINT32);
} else { } 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); 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_theme *theme = NULL;
struct wl_cursor *cursor;
const char *css_name = "default"; const char *css_name = "default";
const char *fallback_name = NULL; const char *fallback_name = NULL;
double scale_factor = 1.0; 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); 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) { if (!cursor && fallback_name) {
cursor = WAYLAND_wl_cursor_theme_get_cursor(theme, 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; 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. // ... Set the cursor data, finally.
cdata->cursor_data.system.num_frames = cursor->image_count; cdata->num_frames = cursor->image_count;
cdata->cursor_data.system.total_duration_ns = 0; Wayland_CachedSystemCursor *c = Wayland_CacheSystemCursor(cdata, cursor, theme_size);
for (int i = 0; i < cursor->image_count; ++i) { seat->pointer.cursor_state.cursor_handle = c;
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); if (cursor->image_count > 1 && !cdata->frame_durations_ms) {
cdata->cursor_data.system.total_duration_ns += cdata->cursor_data.system.frames[i].duration_ns; 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; *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; 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; Wayland_ScaledCustomCursor *cache = NULL;
// Is this cursor already cached at the target scale? // 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; 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) { if (c->scale == scale) {
cache = c; cache = c;
break; break;
@@ -468,41 +521,50 @@ static Wayland_ScaledCustomCursor *Wayland_CacheScaledCustomCursor(SDL_CursorDat
return NULL; 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) { if (!surface) {
SDL_free(cache); SDL_free(cache);
return NULL; return NULL;
} }
// Allocate the shared memory buffer for this cursor. // 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_free(cache);
SDL_DestroySurface(surface); SDL_DestroySurface(surface);
return NULL; return NULL;
} }
// Wayland requires premultiplied alpha for its surfaces. for (int i = 0; i < cursor->num_frames; ++i) {
SDL_PremultiplyAlpha(surface->w, surface->h, if (!surface) {
surface->format, surface->pixels, surface->pitch, surface = SDL_GetSurfaceImage(cursor->cursor_data.custom.sdl_cursor_surfaces[i], (float)scale);
SDL_PIXELFORMAT_ARGB8888, cache->shmBuffer.shm_data, surface->w * 4, true); }
// 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; cache->scale = scale;
WAYLAND_wl_list_insert(&cdata->cursor_data.custom.scaled_cursor_cache, &cache->node); WAYLAND_wl_list_insert(&cursor->cursor_data.custom.scaled_cursor_cache, &cache->node);
SDL_DestroySurface(surface);
} }
return cache; 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_VideoDevice *vd = SDL_GetVideoDevice();
SDL_VideoData *wd = vd->internal; SDL_VideoData *wd = vd->internal;
SDL_CursorData *data = cursor->internal; Wayland_CustomCursor *custom_cursor = &cursor->cursor_data.custom;
SDL_Window *focus = SDL_GetMouseFocus(); SDL_Window *focus = SDL_GetMouseFocus();
double scale_factor = 1.0; 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; 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); scale_factor = SDL_ceil(scale_factor);
} }
Wayland_ScaledCustomCursor *c = Wayland_CacheScaledCustomCursor(data, scale_factor); Wayland_ScaledCustomCursor *c = Wayland_CacheScaledCustomCursor(cursor, scale_factor);
if (!c) { if (!c) {
return false; 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; *scale = SDL_ceil(scale_factor) == scale_factor ? (int)scale_factor : 0;
*dst_width = data->cursor_data.custom.sdl_cursor_surface->w; *dst_width = custom_cursor->sdl_cursor_surfaces[0]->w;
*dst_height = data->cursor_data.custom.sdl_cursor_surface->h; *dst_height = custom_cursor->sdl_cursor_surfaces[0]->h;
*hot_x = data->cursor_data.custom.hot_x; *hot_x = custom_cursor->hot_x;
*hot_y = data->cursor_data.custom.hot_y; *hot_y = custom_cursor->hot_y;
return true; 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)); SDL_Cursor *cursor = SDL_calloc(1, sizeof(*cursor));
if (cursor) { if (cursor) {
SDL_CursorData *data = SDL_calloc(1, sizeof(*data)); SDL_CursorData *data = SDL_calloc(1, sizeof(*data) + sizeof(SDL_Surface *));
if (!data) { if (!data) {
SDL_free(cursor); SDL_free(cursor);
return NULL; return NULL;
} }
cursor->internal = data; cursor->internal = data;
WAYLAND_wl_list_init(&data->cursor_data.custom.scaled_cursor_cache); 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_x = hot_x;
data->cursor_data.custom.hot_y = hot_y; 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; ++surface->refcount;
// If the cursor has only one size, just prepare it now. // 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; cursor->internal = cdata;
WAYLAND_wl_list_init(&cdata->cursor_data.system.cursor_buffer_cache);
cdata->cursor_data.system.id = id; cdata->cursor_data.system.id = id;
cdata->is_system_cursor = true; 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); wl_surface_attach(seat->pointer.cursor_state.surface, NULL, 0, 0);
} }
seat->pointer.current_cursor = NULL; seat->pointer.current_cursor = NULL;
} }
} }
// Buffers for system cursors must not be destroyed.
if (d->is_system_cursor) { 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 { } else {
Wayland_ScaledCustomCursor *c, *temp; Wayland_ScaledCustomCursor *c, *temp;
wl_list_for_each_safe(c, temp, &d->cursor_data.custom.scaled_cursor_cache, node) { 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_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) 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) static void Wayland_SeatSetCursor(SDL_WaylandSeat *seat, SDL_Cursor *cursor)
{ {
if (seat->pointer.wl_pointer) { if (seat->pointer.wl_pointer) {
struct wl_buffer *buffer = NULL; SDL_CursorData *cursor_data = cursor ? cursor->internal : NULL;
int scale = 1; int scale = 1;
int dst_width = 0; int dst_width = 0;
int dst_height = 0; int dst_height = 0;
int hot_x; int hot_x;
int hot_y; int hot_y;
SDL_CursorData *cursor_data = cursor ? cursor->internal : NULL;
// Stop the frame callback for old animated cursors. // Stop the frame callback for old animated cursors.
if (seat->pointer.cursor_state.frame_callback && cursor_data != seat->pointer.current_cursor) { 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; 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; return;
} }
dst_height = dst_width; 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 { } 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; return;
} }
}
if (!seat->pointer.cursor_state.surface) { seat->pointer.current_cursor = cursor_data;
seat->pointer.cursor_state.surface = wl_compositor_create_surface(seat->display->compositor);
} if (!seat->pointer.cursor_state.surface) {
wl_surface_attach(seat->pointer.cursor_state.surface, buffer, 0, 0); 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. // 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); 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); wl_surface_commit(seat->pointer.cursor_state.surface);
} else { } else {
seat->pointer.current_cursor = NULL; seat->pointer.current_cursor = NULL;

View File

@@ -114,11 +114,10 @@ static struct wl_buffer_listener buffer_listener = {
buffer_handle_release 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_VideoDevice *vd = SDL_GetVideoDevice();
SDL_VideoData *data = vd->internal; SDL_VideoData *data = vd->internal;
struct wl_shm_pool *shm_pool;
const Uint32 SHM_FMT = WL_SHM_FORMAT_ARGB8888; const Uint32 SHM_FMT = WL_SHM_FORMAT_ARGB8888;
if (!shmBuffer) { if (!shmBuffer) {
@@ -142,7 +141,7 @@ bool Wayland_AllocSHMBuffer(int width, int height, struct Wayland_SHMBuffer *shm
SDL_assert(shmBuffer->shm_data != NULL); 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); 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); 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; return true;
} }
void Wayland_ReleaseSHMBuffer(struct Wayland_SHMBuffer *shmBuffer) void Wayland_ReleaseSHMBuffer(Wayland_SHMBuffer *shmBuffer)
{ {
if (shmBuffer) { if (shmBuffer) {
if (shmBuffer->wl_buffer) { 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 #endif

View File

@@ -24,15 +24,30 @@
#ifndef SDL_waylandshmbuffer_h_ #ifndef SDL_waylandshmbuffer_h_
#define SDL_waylandshmbuffer_h_ #define SDL_waylandshmbuffer_h_
struct Wayland_SHMBuffer typedef struct
{ {
struct wl_buffer *wl_buffer; struct wl_buffer *wl_buffer;
void *shm_data; void *shm_data;
int shm_data_size; 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 // Allocates an SHM buffer with the format WL_SHM_FORMAT_ARGB8888
extern bool Wayland_AllocSHMBuffer(int width, int height, struct Wayland_SHMBuffer *shmBuffer); extern bool Wayland_AllocSHMBuffer(int width, int height, Wayland_SHMBuffer *shmBuffer);
extern void Wayland_ReleaseSHMBuffer(struct 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 #endif

View File

@@ -2988,14 +2988,14 @@ bool Wayland_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surfa
wind->icon_buffer_count = 0; 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->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) { if (!wind->icon_buffers) {
goto failure_cleanup; goto failure_cleanup;
} }
for (int i = 0; i < image_count; ++i) { for (int i = 0; i < image_count; ++i) {
if (images[i]->w == images[i]->h) { 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)) { if (!Wayland_AllocSHMBuffer(images[i]->w, images[i]->h, buffer)) {
SDL_SetError("wayland: failed to allocate SHM buffer for the icon"); SDL_SetError("wayland: failed to allocate SHM buffer for the icon");

View File

@@ -127,7 +127,7 @@ struct SDL_WindowData
char *app_id; char *app_id;
double scale_factor; double scale_factor;
struct Wayland_SHMBuffer *icon_buffers; Wayland_SHMBuffer *icon_buffers;
int icon_buffer_count; int icon_buffer_count;
// Keyboard, pointer, and touch focus refcount. // Keyboard, pointer, and touch focus refcount.