From 4fcb92e0df40a7b999635d8c1f244a6dbd59a224 Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Mon, 29 Dec 2025 12:42:53 -0500 Subject: [PATCH] wayland: Scale non-square icon images Wayland icons must be square, so scale non-square images to fit a square region instead of failing. This matches X11 behavior. A warning that the image will be scaled will be logged. --- src/video/wayland/SDL_waylandwindow.c | 84 ++++++++++++++++----------- 1 file changed, 50 insertions(+), 34 deletions(-) diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c index 9f008be93f..c6ea8ea17c 100644 --- a/src/video/wayland/SDL_waylandwindow.c +++ b/src/video/wayland/SDL_waylandwindow.c @@ -3003,10 +3003,6 @@ bool Wayland_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surfa return SDL_SetError("wayland: cannot set icon; required xdg_toplevel_icon_v1 protocol not supported"); } - if (icon->w != icon->h) { - return SDL_SetError("wayland: icon width and height must be equal, got %ix%i", icon->w, icon->h); - } - int image_count = 0; SDL_Surface **images = SDL_GetSurfaceImages(icon, &image_count); if (!images || !image_count) { @@ -3034,11 +3030,12 @@ bool Wayland_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surfa size_t pool_size = 0; for (int i = 0; i < image_count; ++i) { // Ignore non-square images; if we got here, we know that at least the base image is square. - if (images[i]->w == images[i]->h) { - pool_size += images[i]->w * images[i]->h * 4; - } else { - SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "wayland: icon width and height must be equal, got %ix%i for image level %i; skipping", images[i]->w, images[i]->h, i); + if (images[i]->w != images[i]->h) { + SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "wayland: icon width and height must be equal, got %ix%i for image level %i; icon will be scaled", images[i]->w, images[i]->h, i); } + + const int size = SDL_max(images[i]->w, images[i]->h); + pool_size += size * size * 4; } // Sort the images in ascending order by size. @@ -3046,46 +3043,65 @@ bool Wayland_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surfa shm_pool = Wayland_AllocSHMPool(pool_size); if (!shm_pool) { - SDL_SetError("wayland: failed to allocate SHM pool for the icon"); + SDL_SetError("wayland: failed to allocate an SHM pool for the icon"); goto failure_cleanup; } + const double base_size = (double)SDL_max(icon->w, icon->h); for (int i = 0; i < image_count; ++i) { - if (images[i]->w == images[i]->h) { - SDL_Surface *surface = images[i]; + SDL_Surface *surface = images[i]; - // Choose the largest image for each integer scale, ignoring any below the base size. - const int scale = (int)SDL_floor((double)surface->w / (double)icon->w); - if (!scale) { - continue; - } + // Choose the largest image for each integer scale, ignoring any below the base size. + const int level_size = SDL_max(surface->w, surface->h); + const int scale = (int)SDL_floor((double)level_size / base_size); + if (!scale) { + continue; + } - void *buffer_mem; - struct wl_buffer *buffer = Wayland_AllocBufferFromPool(shm_pool, surface->w, surface->h, &buffer_mem); - if (!buffer) { - SDL_SetError("wayland: failed to allocate SHM buffer for the icon"); + if (surface->format != SDL_PIXELFORMAT_ARGB8888) { + surface = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ARGB8888); + if (!surface) { + SDL_SetError("wayland: failed to convert the icon image to ARGB8888 format"); goto failure_cleanup; } + } - wind->icon_buffers[wind->icon_buffer_count++] = buffer; - - if (surface->format != SDL_PIXELFORMAT_ARGB8888) { - surface = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ARGB8888); - if (!surface) { - SDL_SetError("wayland: unable to convert surface to ARGB8888 format"); - goto failure_cleanup; - } - } - - SDL_PremultiplyAlpha(surface->w, surface->h, surface->format, surface->pixels, surface->pitch, - SDL_PIXELFORMAT_ARGB8888, buffer_mem, surface->w * 4, true); - - xdg_toplevel_icon_v1_add_buffer(wind->xdg_toplevel_icon_v1, buffer, scale); + if (surface->w != surface->h) { + SDL_Surface *scaled_surface = SDL_ScaleSurface(surface, level_size, level_size, SDL_SCALEMODE_LINEAR); // Clean up the temporary conversion surface. if (surface != images[i]) { SDL_DestroySurface(surface); } + surface = scaled_surface; + + if (!surface) { + SDL_SetError("wayland: failed to scale the non-square icon image to the required size"); + goto failure_cleanup; + } + } + + void *buffer_mem; + struct wl_buffer *buffer = Wayland_AllocBufferFromPool(shm_pool, surface->w, surface->h, &buffer_mem); + if (!buffer) { + // Clean up the temporary scaled or conversion surface. + if (surface != images[i]) { + SDL_DestroySurface(surface); + } + SDL_SetError("wayland: failed to allocate a wl_buffer for the icon"); + goto failure_cleanup; + } + + wind->icon_buffers[wind->icon_buffer_count++] = buffer; + + SDL_PremultiplyAlpha(surface->w, surface->h, surface->format, surface->pixels, surface->pitch, + SDL_PIXELFORMAT_ARGB8888, buffer_mem, surface->w * 4, true); + + xdg_toplevel_icon_v1_add_buffer(wind->xdg_toplevel_icon_v1, buffer, scale); + + // Clean up the temporary scaled or conversion surface. + if (surface != images[i]) { + SDL_DestroySurface(surface); } }