Simple Directmedia Layer
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

wayland: Add support for setting window icons via the xdg-toplevel-icon-v1 protocol

+288 -6
+6 -6
docs/README-wayland.md
··· 46 46 47 47 ### The application icon can't be set via ```SDL_SetWindowIcon()``` 48 48 49 - - Wayland doesn't support programmatically setting the application icon. To provide a custom icon for your application, 50 - you must create an associated desktop entry file, aka a `.desktop` file, that points to the icon image. Please see the 51 - [Desktop Entry Specification](https://specifications.freedesktop.org/desktop-entry-spec/latest/) for more information 52 - on the format of this file. Note that if your application manually sets the application ID via the `SDL_APP_ID` hint 53 - string, the desktop entry file name should match the application ID. For example, if your application ID is set 54 - to `org.my_org.sdl_app`, the desktop entry file should be named `org.my_org.sdl_app.desktop`. 49 + - Wayland requires compositor support for the `xdg-toplevel-icon-v1` protocol to set window icons programmatically. 50 + Otherwise, the launcher icon from the associated desktop entry file, aka a `.desktop` file, will typically be used. 51 + Please see the [Desktop Entry Specification](https://specifications.freedesktop.org/desktop-entry-spec/latest/) for 52 + more information on the format of this file. Note that if your application manually sets the application ID via the 53 + `SDL_APP_ID` hint string, the desktop entry file name should match the application ID. For example, if your 54 + application ID is set to `org.my_org.sdl_app`, the desktop entry file should be named `org.my_org.sdl_app.desktop`. 55 55 56 56 ## Using custom Wayland windowing protocols with SDL windows 57 57
+9
src/video/wayland/SDL_waylandvideo.c
··· 63 63 #include "xdg-foreign-unstable-v2-client-protocol.h" 64 64 #include "xdg-output-unstable-v1-client-protocol.h" 65 65 #include "xdg-shell-client-protocol.h" 66 + #include "xdg-toplevel-icon-v1-client-protocol.h" 66 67 67 68 #ifdef HAVE_LIBDECOR_H 68 69 #include <libdecor.h> ··· 549 550 device->SetWindowModalFor = Wayland_SetWindowModalFor; 550 551 device->SetWindowOpacity = Wayland_SetWindowOpacity; 551 552 device->SetWindowTitle = Wayland_SetWindowTitle; 553 + device->SetWindowIcon = Wayland_SetWindowIcon; 552 554 device->GetWindowSizeInPixels = Wayland_GetWindowSizeInPixels; 553 555 device->GetDisplayForWindow = Wayland_GetDisplayForWindow; 554 556 device->DestroyWindow = Wayland_DestroyWindow; ··· 1191 1193 d->xdg_wm_dialog_v1 = wl_registry_bind(d->registry, id, &xdg_wm_dialog_v1_interface, 1); 1192 1194 } else if (SDL_strcmp(interface, "wp_alpha_modifier_v1") == 0) { 1193 1195 d->wp_alpha_modifier_v1 = wl_registry_bind(d->registry, id, &wp_alpha_modifier_v1_interface, 1); 1196 + } else if (SDL_strcmp(interface, "xdg_toplevel_icon_manager_v1") == 0) { 1197 + d->xdg_toplevel_icon_manager_v1 = wl_registry_bind(d->registry, id, &xdg_toplevel_icon_manager_v1_interface, 1); 1194 1198 } else if (SDL_strcmp(interface, "kde_output_order_v1") == 0) { 1195 1199 d->kde_output_order = wl_registry_bind(d->registry, id, &kde_output_order_v1_interface, 1); 1196 1200 kde_output_order_v1_add_listener(d->kde_output_order, &kde_output_order_listener, d); ··· 1458 1462 if (data->wp_alpha_modifier_v1) { 1459 1463 wp_alpha_modifier_v1_destroy(data->wp_alpha_modifier_v1); 1460 1464 data->wp_alpha_modifier_v1 = NULL; 1465 + } 1466 + 1467 + if (data->xdg_toplevel_icon_manager_v1) { 1468 + xdg_toplevel_icon_manager_v1_destroy(data->xdg_toplevel_icon_manager_v1); 1469 + data->xdg_toplevel_icon_manager_v1 = NULL; 1461 1470 } 1462 1471 1463 1472 if (data->kde_output_order) {
+1
src/video/wayland/SDL_waylandvideo.h
··· 81 81 struct zxdg_exporter_v2 *zxdg_exporter_v2; 82 82 struct xdg_wm_dialog_v1 *xdg_wm_dialog_v1; 83 83 struct wp_alpha_modifier_v1 *wp_alpha_modifier_v1; 84 + struct xdg_toplevel_icon_manager_v1 *xdg_toplevel_icon_manager_v1; 84 85 struct kde_output_order_v1 *kde_output_order; 85 86 struct frog_color_management_factory_v1 *frog_color_management_factory_v1; 86 87 struct zwp_tablet_manager_v2 *tablet_manager;
+64
src/video/wayland/SDL_waylandwindow.c
··· 42 42 #include "xdg-foreign-unstable-v2-client-protocol.h" 43 43 #include "xdg-dialog-v1-client-protocol.h" 44 44 #include "frog-color-management-v1-client-protocol.h" 45 + #include "xdg-toplevel-icon-v1-client-protocol.h" 45 46 46 47 #ifdef HAVE_LIBDECOR_H 47 48 #include <libdecor.h> ··· 1722 1723 zxdg_exported_v2_add_listener(data->exported, &exported_v2_listener, data); 1723 1724 } 1724 1725 1726 + if (c->xdg_toplevel_icon_manager_v1 && data->xdg_toplevel_icon_v1) { 1727 + xdg_toplevel_icon_manager_v1_set_icon(_this->internal->xdg_toplevel_icon_manager_v1, 1728 + libdecor_frame_get_xdg_toplevel(data->shell_surface.libdecor.frame), 1729 + data->xdg_toplevel_icon_v1); 1730 + } 1731 + 1725 1732 SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_SURFACE_POINTER, libdecor_frame_get_xdg_surface(data->shell_surface.libdecor.frame)); 1726 1733 SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_POINTER, libdecor_frame_get_xdg_toplevel(data->shell_surface.libdecor.frame)); 1727 1734 } ··· 1800 1807 if (c->zxdg_exporter_v2) { 1801 1808 data->exported = zxdg_exporter_v2_export_toplevel(c->zxdg_exporter_v2, data->surface); 1802 1809 zxdg_exported_v2_add_listener(data->exported, &exported_v2_listener, data); 1810 + } 1811 + 1812 + if (c->xdg_toplevel_icon_manager_v1 && data->xdg_toplevel_icon_v1) { 1813 + xdg_toplevel_icon_manager_v1_set_icon(_this->internal->xdg_toplevel_icon_manager_v1, 1814 + data->shell_surface.xdg.roleobj.toplevel, 1815 + data->xdg_toplevel_icon_v1); 1803 1816 } 1804 1817 1805 1818 SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_POINTER, data->shell_surface.xdg.roleobj.toplevel); ··· 2666 2679 } 2667 2680 } 2668 2681 2682 + bool Wayland_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *icon) 2683 + { 2684 + SDL_WindowData *wind = window->internal; 2685 + struct xdg_toplevel *toplevel = NULL; 2686 + 2687 + if (!_this->internal->xdg_toplevel_icon_manager_v1) { 2688 + return SDL_SetError("wayland: cannot set icon; xdg_toplevel_icon_v1 protocol not supported"); 2689 + } 2690 + 2691 + if (icon->w != icon->h) { 2692 + return SDL_SetError("wayland: icon width and height must be equal, got %ix%i", icon->w, icon->h); 2693 + } 2694 + 2695 + if (wind->xdg_toplevel_icon_v1) { 2696 + xdg_toplevel_icon_v1_destroy(wind->xdg_toplevel_icon_v1); 2697 + wind->xdg_toplevel_icon_v1 = NULL; 2698 + } 2699 + 2700 + // TODO: Add high-DPI icon support 2701 + Wayland_ReleaseSHMBuffer(&wind->icon); 2702 + if (Wayland_AllocSHMBuffer(icon->w, icon->h, &wind->icon) != 0) { 2703 + return SDL_SetError("wayland: failed to allocate SHM buffer for the icon"); 2704 + } 2705 + 2706 + SDL_PremultiplyAlpha(icon->w, icon->h, icon->format, icon->pixels, icon->pitch, SDL_PIXELFORMAT_ARGB8888, wind->icon.shm_data, icon->w * 4, SDL_TRUE); 2707 + 2708 + wind->xdg_toplevel_icon_v1 = xdg_toplevel_icon_manager_v1_create_icon(_this->internal->xdg_toplevel_icon_manager_v1); 2709 + xdg_toplevel_icon_v1_add_buffer(wind->xdg_toplevel_icon_v1, wind->icon.wl_buffer, 1); 2710 + 2711 + #ifdef HAVE_LIBDECOR_H 2712 + if (wind->shell_surface_type == WAYLAND_SURFACE_LIBDECOR && wind->shell_surface.libdecor.frame) { 2713 + toplevel = libdecor_frame_get_xdg_toplevel(wind->shell_surface.libdecor.frame); 2714 + } else 2715 + #endif 2716 + if (wind->shell_surface_type == WAYLAND_SURFACE_XDG_TOPLEVEL && wind->shell_surface.xdg.roleobj.toplevel) { 2717 + toplevel = wind->shell_surface.xdg.roleobj.toplevel; 2718 + } 2719 + 2720 + if (toplevel) { 2721 + xdg_toplevel_icon_manager_v1_set_icon(_this->internal->xdg_toplevel_icon_manager_v1, toplevel, wind->xdg_toplevel_icon_v1); 2722 + } 2723 + 2724 + return true; 2725 + } 2726 + 2669 2727 bool Wayland_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window) 2670 2728 { 2671 2729 SDL_WindowData *wind = window->internal; ··· 2802 2860 } else { 2803 2861 Wayland_RemoveWindowDataFromExternalList(wind); 2804 2862 } 2863 + 2864 + if (wind->xdg_toplevel_icon_v1) { 2865 + xdg_toplevel_icon_v1_destroy(wind->xdg_toplevel_icon_v1); 2866 + } 2867 + 2868 + Wayland_ReleaseSHMBuffer(&wind->icon); 2805 2869 2806 2870 SDL_free(wind); 2807 2871 WAYLAND_wl_display_flush(data->display);
+5
src/video/wayland/SDL_waylandwindow.h
··· 28 28 #include "../../events/SDL_touch_c.h" 29 29 30 30 #include "SDL_waylandvideo.h" 31 + #include "SDL_waylandshmbuffer.h" 31 32 32 33 struct SDL_WaylandInput; 33 34 ··· 98 99 struct zxdg_exported_v2 *exported; 99 100 struct xdg_dialog_v1 *xdg_dialog_v1; 100 101 struct wp_alpha_modifier_surface_v1 *wp_alpha_modifier_surface_v1; 102 + struct xdg_toplevel_icon_v1 *xdg_toplevel_icon_v1; 101 103 struct frog_color_managed_surface *frog_color_managed_surface; 102 104 103 105 SDL_AtomicInt swap_interval_ready; ··· 109 111 110 112 char *app_id; 111 113 float windowed_scale_factor; 114 + 115 + struct Wayland_SHMBuffer icon; 112 116 113 117 struct 114 118 { ··· 202 206 extern void Wayland_ShowWindowSystemMenu(SDL_Window *window, int x, int y); 203 207 extern void Wayland_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window); 204 208 extern bool Wayland_SuspendScreenSaver(SDL_VideoDevice *_this); 209 + extern bool Wayland_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *icon); 205 210 206 211 extern bool Wayland_SetWindowHitTest(SDL_Window *window, bool enabled); 207 212 extern bool Wayland_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation);
+203
wayland-protocols/xdg-toplevel-icon-v1.xml
··· 1 + <?xml version="1.0" encoding="UTF-8"?> 2 + <protocol name="xdg_toplevel_icon_v1"> 3 + 4 + <copyright> 5 + Copyright © 2023-2024 Matthias Klumpp 6 + Copyright © 2024 David Edmundson 7 + 8 + Permission is hereby granted, free of charge, to any person obtaining a 9 + copy of this software and associated documentation files (the "Software"), 10 + to deal in the Software without restriction, including without limitation 11 + the rights to use, copy, modify, merge, publish, distribute, sublicense, 12 + and/or sell copies of the Software, and to permit persons to whom the 13 + Software is furnished to do so, subject to the following conditions: 14 + 15 + The above copyright notice and this permission notice (including the next 16 + paragraph) shall be included in all copies or substantial portions of the 17 + Software. 18 + 19 + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 22 + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 + DEALINGS IN THE SOFTWARE. 26 + </copyright> 27 + 28 + <description summary="protocol to assign icons to toplevels"> 29 + This protocol allows clients to set icons for their toplevel surfaces 30 + either via the XDG icon stock (using an icon name), or from pixel data. 31 + 32 + A toplevel icon represents the individual toplevel (unlike the application 33 + or launcher icon, which represents the application as a whole), and may be 34 + shown in window switchers, window overviews and taskbars that list 35 + individual windows. 36 + 37 + This document adheres to RFC 2119 when using words like "must", 38 + "should", "may", etc. 39 + 40 + Warning! The protocol described in this file is currently in the testing 41 + phase. Backward compatible changes may be added together with the 42 + corresponding interface version bump. Backward incompatible changes can 43 + only be done by creating a new major version of the extension. 44 + </description> 45 + 46 + <interface name="xdg_toplevel_icon_manager_v1" version="1"> 47 + <description summary="interface to manage toplevel icons"> 48 + This interface allows clients to create toplevel window icons and set 49 + them on toplevel windows to be displayed to the user. 50 + </description> 51 + 52 + <request name="destroy" type="destructor"> 53 + <description summary="destroy the toplevel icon manager"> 54 + Destroy the toplevel icon manager. 55 + This does not destroy objects created with the manager. 56 + </description> 57 + </request> 58 + 59 + <request name="create_icon"> 60 + <description summary="create a new icon instance"> 61 + Creates a new icon object. This icon can then be attached to a 62 + xdg_toplevel via the 'set_icon' request. 63 + </description> 64 + <arg name="id" type="new_id" interface="xdg_toplevel_icon_v1"/> 65 + </request> 66 + 67 + <request name="set_icon"> 68 + <description summary="set an icon on a toplevel window"> 69 + This request assigns the icon 'icon' to 'toplevel', or clears the 70 + toplevel icon if 'icon' was null. 71 + This state is double-buffered and is applied on the next 72 + wl_surface.commit of the toplevel. 73 + 74 + After making this call, the xdg_toplevel_icon_v1 provided as 'icon' 75 + can be destroyed by the client without 'toplevel' losing its icon. 76 + The xdg_toplevel_icon_v1 is immutable from this point, and any 77 + future attempts to change it must raise the 78 + 'xdg_toplevel_icon_v1.immutable' protocol error. 79 + 80 + The compositor must set the toplevel icon from either the pixel data 81 + the icon provides, or by loading a stock icon using the icon name. 82 + See the description of 'xdg_toplevel_icon_v1' for details. 83 + 84 + If 'icon' is set to null, the icon of the respective toplevel is reset 85 + to its default icon (usually the icon of the application, derived from 86 + its desktop-entry file, or a placeholder icon). 87 + If this request is passed an icon with no pixel buffers or icon name 88 + assigned, the icon must be reset just like if 'icon' was null. 89 + </description> 90 + <arg name="toplevel" type="object" interface="xdg_toplevel" summary="the toplevel to act on"/> 91 + <arg name="icon" type="object" interface="xdg_toplevel_icon_v1" allow-null="true"/> 92 + </request> 93 + 94 + <event name="icon_size"> 95 + <description summary="describes a supported &amp; preferred icon size"> 96 + This event indicates an icon size the compositor prefers to be 97 + available if the client has scalable icons and can render to any size. 98 + 99 + When the 'xdg_toplevel_icon_manager_v1' object is created, the 100 + compositor may send one or more 'icon_size' events to describe the list 101 + of preferred icon sizes. If the compositor has no size preference, it 102 + may not send any 'icon_size' event, and it is up to the client to 103 + decide a suitable icon size. 104 + 105 + A sequence of 'icon_size' events must be finished with a 'done' event. 106 + If the compositor has no size preferences, it must still send the 107 + 'done' event, without any preceding 'icon_size' events. 108 + </description> 109 + <arg name="size" type="int" 110 + summary="the edge size of the square icon in surface-local coordinates, e.g. 64"/> 111 + </event> 112 + 113 + <event name="done"> 114 + <description summary="all information has been sent"> 115 + This event is sent after all 'icon_size' events have been sent. 116 + </description> 117 + </event> 118 + </interface> 119 + 120 + <interface name="xdg_toplevel_icon_v1" version="1"> 121 + <description summary="a toplevel window icon"> 122 + This interface defines a toplevel icon. 123 + An icon can have a name, and multiple buffers. 124 + In order to be applied, the icon must have either a name, or at least 125 + one buffer assigned. Applying an empty icon (with no buffer or name) to 126 + a toplevel should reset its icon to the default icon. 127 + 128 + It is up to compositor policy whether to prefer using a buffer or loading 129 + an icon via its name. See 'set_name' and 'add_buffer' for details. 130 + </description> 131 + 132 + <enum name="error"> 133 + <entry name="invalid_buffer" 134 + summary="the provided buffer does not satisfy requirements" 135 + value="1"/> 136 + <entry name="immutable" 137 + summary="the icon has already been assigned to a toplevel and must not be changed" 138 + value="2"/> 139 + <entry name="no_buffer" 140 + summary="the provided buffer has been destroyed before the toplevel icon" 141 + value="3"/> 142 + </enum> 143 + 144 + <request name="destroy" type="destructor"> 145 + <description summary="destroy the icon object"> 146 + Destroys the 'xdg_toplevel_icon_v1' object. 147 + The icon must still remain set on every toplevel it was assigned to, 148 + until the toplevel icon is reset explicitly. 149 + </description> 150 + </request> 151 + 152 + <request name="set_name"> 153 + <description summary="set an icon name"> 154 + This request assigns an icon name to this icon. 155 + Any previously set name is overridden. 156 + 157 + The compositor must resolve 'icon_name' according to the lookup rules 158 + described in the XDG icon theme specification[1] using the 159 + environment's current icon theme. 160 + 161 + If the compositor does not support icon names or cannot resolve 162 + 'icon_name' according to the XDG icon theme specification it must 163 + fall back to using pixel buffer data instead. 164 + 165 + If this request is made after the icon has been assigned to a toplevel 166 + via 'set_icon', a 'immutable' error must be raised. 167 + 168 + [1]: https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html 169 + </description> 170 + <arg name="icon_name" type="string"/> 171 + </request> 172 + 173 + <request name="add_buffer"> 174 + <description summary="add icon data from a pixel buffer"> 175 + This request adds pixel data supplied as wl_buffer to the icon. 176 + 177 + The client should add pixel data for all icon sizes and scales that 178 + it can provide, or which are explicitly requested by the compositor 179 + via 'icon_size' events on xdg_toplevel_icon_manager_v1. 180 + 181 + The wl_buffer supplying pixel data as 'buffer' must be backed by wl_shm 182 + and must be a square (width and height being equal). 183 + If any of these buffer requirements are not fulfilled, a 'invalid_buffer' 184 + error must be raised. 185 + 186 + If this icon instance already has a buffer of the same size and scale 187 + from a previous 'add_buffer' request, data from the last request 188 + overrides the preexisting pixel data. 189 + 190 + The wl_buffer must be kept alive for as long as the xdg_toplevel_icon 191 + it is associated with is not destroyed, otherwise a 'no_buffer' error 192 + is raised. The buffer contents must not be modified after it was 193 + assigned to the icon. 194 + 195 + If this request is made after the icon has been assigned to a toplevel 196 + via 'set_icon', a 'immutable' error must be raised. 197 + </description> 198 + <arg name="buffer" type="object" interface="wl_buffer"/> 199 + <arg name="scale" type="int" 200 + summary="the scaling factor of the icon, e.g. 1"/> 201 + </request> 202 + </interface> 203 + </protocol>