Skip to content

Commit

Permalink
toplevel_icon
Browse files Browse the repository at this point in the history
  • Loading branch information
davidedmundson committed Apr 18, 2024
1 parent a923db9 commit c6190d7
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 0 deletions.
4 changes: 4 additions & 0 deletions src/video/wayland/SDL_waylandvideo.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
#include "xdg-foreign-unstable-v2-client-protocol.h"
#include "xdg-output-unstable-v1-client-protocol.h"
#include "xdg-shell-client-protocol.h"
#include "xdg-toplevel-icon-v1-client-protocol.h"

#ifdef HAVE_LIBDECOR_H
#include <libdecor.h>
Expand Down Expand Up @@ -485,6 +486,7 @@ static SDL_VideoDevice *Wayland_CreateDevice(void)
device->SetWindowMinimumSize = Wayland_SetWindowMinimumSize;
device->SetWindowMaximumSize = Wayland_SetWindowMaximumSize;
device->SetWindowModalFor = Wayland_SetWindowModalFor;
device->SetWindowIcon = Wayland_SetWindowIcon;
device->SetWindowTitle = Wayland_SetWindowTitle;
device->GetWindowSizeInPixels = Wayland_GetWindowSizeInPixels;
device->DestroyWindow = Wayland_DestroyWindow;
Expand Down Expand Up @@ -1091,6 +1093,8 @@ static void display_handle_global(void *data, struct wl_registry *registry, uint
} else if (SDL_strcmp(interface, "kde_output_order_v1") == 0) {
d->kde_output_order = wl_registry_bind(d->registry, id, &kde_output_order_v1_interface, 1);
kde_output_order_v1_add_listener(d->kde_output_order, &kde_output_order_listener, d);
} else if (SDL_strcmp(interface, "xdg_toplevel_icon_v1") == 0) {
d->xdg_toplevel_icon = wl_registry_bind(d->registry, id, &xdg_toplevel_icon_v1_interface, 1);
}
}

Expand Down
1 change: 1 addition & 0 deletions src/video/wayland/SDL_waylandvideo.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ struct SDL_VideoData
struct zwp_input_timestamps_manager_v1 *input_timestamps_manager;
struct zxdg_exporter_v2 *zxdg_exporter_v2;
struct kde_output_order_v1 *kde_output_order;
struct xdg_toplevel_icon_v1 *xdg_toplevel_icon;

struct xkb_context *xkb_context;
struct SDL_WaylandInput *input;
Expand Down
46 changes: 46 additions & 0 deletions src/video/wayland/SDL_waylandwindow.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
#include "SDL_waylandevents_c.h"
#include "SDL_waylandwindow.h"
#include "SDL_waylandvideo.h"
#include "SDL_waylandshmbuffer.h"

#include "../../SDL_hints_c.h"

#include "xdg-shell-client-protocol.h"
Expand All @@ -39,6 +41,7 @@
#include "viewporter-client-protocol.h"
#include "fractional-scale-v1-client-protocol.h"
#include "xdg-foreign-unstable-v2-client-protocol.h"
#include "xdg-toplevel-icon-v1-client-protocol.h"

#ifdef HAVE_LIBDECOR_H
#include <libdecor.h>
Expand Down Expand Up @@ -1654,6 +1657,11 @@ void Wayland_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)

/* Restore state that was set prior to this call */
Wayland_SetWindowTitle(_this, window);
if (data->pending_icon) {
Wayland_SetWindowIcon(_this, window, data->pending_icon);
SDL_DestroySurface(data->pending_icon);
data->pending_icon = NULL;
}

/* We have to wait until the surface gets a "configure" event, or use of
* this surface will fail. This is a new rule for xdg_shell.
Expand Down Expand Up @@ -2476,6 +2484,44 @@ void Wayland_GetWindowSizeInPixels(SDL_VideoDevice *_this, SDL_Window *window, i
*h = data->current.drawable_height;
}

int Wayland_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *icon)
{
SDL_WindowData *wind = window->driverdata;
SDL_VideoData *viddata = _this->driverdata;

if (!viddata->xdg_toplevel_icon) {
return SDL_SetError("Unable to set the window's icon. Missing compositor support");
}

if (wind->shell_surface_type == WAYLAND_SURFACE_XDG_TOPLEVEL && wind->shell_surface.xdg.roleobj.toplevel) { // or libdecor path
if (icon) {
struct Wayland_SHMBuffer shmBuffer;
if (Wayland_AllocSHMBuffer(icon->w, icon->h, &shmBuffer) != 0) {
return SDL_SetError("Unable to set the window's icon. Failed to allocate icon memory");
}

SDL_PremultiplyAlpha(icon->w, icon->h,
icon->format->format, icon->pixels, icon->pitch,
SDL_PIXELFORMAT_ARGB8888, shmBuffer.shm_data, icon->w * 4);

xdg_toplevel_icon_v1_set_icon_buffer(viddata->xdg_toplevel_icon, wind->shell_surface.xdg.roleobj.toplevel, shmBuffer.wl_buffer);
Wayland_ReleaseSHMBuffer(&shmBuffer);
} else {
xdg_toplevel_icon_v1_set_icon_buffer(viddata->xdg_toplevel_icon, wind->shell_surface.xdg.roleobj.toplevel, NULL);
}
} else { // top level is not yet shown, cache the icon for sending later
if (wind->pending_icon) {
SDL_DestroySurface(wind->pending_icon);
wind->pending_icon = NULL;
}
if (icon) {
wind->pending_icon = SDL_DuplicateSurface(icon);
}
}

return 0;
}

void Wayland_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_WindowData *wind = window->driverdata;
Expand Down
4 changes: 4 additions & 0 deletions src/video/wayland/SDL_waylandwindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,9 @@ struct SDL_WindowData

SDL_HitTestResult hit_test_result;

/** Icon set before a window is created*/
SDL_Surface* pending_icon;

struct wl_list external_window_list_link;
};

Expand All @@ -199,6 +202,7 @@ extern void Wayland_SetWindowMinimumSize(SDL_VideoDevice *_this, SDL_Window *win
extern void Wayland_SetWindowMaximumSize(SDL_VideoDevice *_this, SDL_Window *window);
extern void Wayland_GetWindowSizeInPixels(SDL_VideoDevice *_this, SDL_Window *window, int *w, int *h);
extern int Wayland_SetWindowModalFor(SDL_VideoDevice *_this, SDL_Window *modal_window, SDL_Window *parent_window);
extern int Wayland_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *icon);
extern void Wayland_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window);
extern void Wayland_ShowWindowSystemMenu(SDL_Window *window, int x, int y);
extern void Wayland_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window);
Expand Down
140 changes: 140 additions & 0 deletions wayland-protocols/xdg-toplevel-icon-v1.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="xdg_toplevel_icon_v1">

<copyright>
Copyright © 2023-2024 Matthias Klumpp

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
</copyright>

<description summary="protocol to assign explicit icons to toplevels">
This protocol allows clients to set icons for their toplevel surfaces
either via an XDG icon stock (using an icon name), or from pixel data.

A toplevel icon represents the individual toplevel (unlike the application
or launcher icon, which represents the application as a whole), and may be
shown in window switchers, window overviews and taskbars that list
individual windows.

This document adheres to RFC 2119 when using words like "must",
"should", "may", etc.

Warning! The protocol described in this file is currently in the testing
phase. Backward compatible changes may be added together with the
corresponding interface version bump. Backward incompatible changes can
only be done by creating a new major version of the extension.
</description>

<interface name="xdg_toplevel_icon_v1" version="1">
<description summary="interface to set toplevel window icons">
This interface provides a way for clients to set a dedicated icon for
their toplevels to represent them in overviews.

Icons are supposed to be static, and while it is allowed to change an
icon for an already existing window, icons should not be used to convey
status information or try to be animated.
Compositors may rate-limit toplevel icon changes.

An icon can either be set from an XDG stock icon via 'set_icon_name' or
from pixel data via 'set_icon_buffer'.

In case both pixel data and an icon name are set by the client, the named
icon must always be preferred as long as it is valid and found in the
current theme.

The compositor may alter the icon as it sees fit (for example, scale it,
desaturate it or tint it) in order to integrate it with the environment.
</description>

<enum name="error">
<entry name="wrong_format" summary="the provided icon does not match the requested format"
value="1"/>
</enum>

<request name="set_icon_name">
<description summary="set the toplevel icon via a stock icon name, or clear the icon">
This request assigns an icon to 'toplevel' using its XDG icon-theme
name, or clears the toplevel icon.
This state is double-buffered and is applied on the next
wl_surface.commit of the toplevel.

The request must specify an icon name for a stock icon in the icon
theme.
If 'icon_name' is set to null, the icon of the respective toplevel is
reset to its default icon (usually the icon of the application,
derived from its desktop-entry file, or a placeholder icon).

The compositor must resolve 'icon_name' according to the lookup rules
described in the XDG icon theme specification[1] using the
environment's current icon theme.
If the icon name can not be resolved to a valid icon, the compositor
must emit an 'icon_failed' event. If an icon was successfully set,
an 'icon_assigned' event must be emitted.

If the icon name is null and the icon is reset to its default,
an 'icon_assigned' event must always be emitted in response.

This request must be honored if sent as part of the xdg_toplevel's
configure sequence. The client can send it at a later time to request
an update of the icon.

If a compositor does not support setting an icon by its name, it must
reply with 'icon_failed' to all 'set_icon_name' requests.

[1]: https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
</description>
<arg name="toplevel" type="object" interface="xdg_toplevel" summary="the toplevel to act on"/>
<arg name="icon_name" type="string" allow-null="true"/>
</request>


<request name="set_icon_buffer">
<description summary="set a toplevel icon from a pixel buffer">
This request assigns a window icon to 'toplevel' using pixel data
supplied as wl_buffer.
This state is double-buffered and is applied on the next
wl_surface.commit of the toplevel.

This request provides the compositor with pixel data for the window
icon of 'toplevel' for the scaling factor 'scale'.

The client should submit pixel data for all icon sizes it can provide.

The wl_buffer supplying pixel data as 'icon' must be backed by wl_shm
and must be a square (width and height being equal).
If any of the buffer requirements are not fulfilled, a 'wrong_format'
error must be raised.

The client may invoke this request multiple times to provide an icon in
multiple sizes.
If the compositor alreaey has a buffer of the same size and scale from
a previous 'set_icon_buffer' request, data from the latest request
overrides the preexisting pixel data.
To reset to icon to the compositor's default, 'set_icon_name' with a
'icon_name' of null should be called.
</description>
<arg name="toplevel" type="object" interface="xdg_toplevel" summary="the toplevel to act on"/>
<arg name="icon" type="object" interface="wl_buffer"/>
</request>


</interface>

</protocol>

0 comments on commit c6190d7

Please sign in to comment.