Files
packages/anda/lib/terra-glfw/0005-Implement-glfwSetWindowIcon.patch
T
2025-12-09 19:02:26 +08:00

384 lines
17 KiB
Diff

From f8e6fca745f27adc8f40f022d415b4ac7cbc375e Mon Sep 17 00:00:00 2001
From: JakobDev <jakobdev@gmx.de>
Date: Wed, 12 Feb 2025 19:24:19 +0100
Subject: [PATCH] Wayland: Implement glfwSetWindowIcon
---
deps/wayland/xdg-toplevel-icon-v1.xml | 205 ++++++++++++++++++++++++++
include/GLFW/glfw3.h | 6 +-
src/CMakeLists.txt | 1 +
src/wl_init.c | 14 ++
src/wl_platform.h | 1 +
src/wl_window.c | 52 ++++++-
6 files changed, 274 insertions(+), 5 deletions(-)
create mode 100644 deps/wayland/xdg-toplevel-icon-v1.xml
diff --git a/deps/wayland/xdg-toplevel-icon-v1.xml b/deps/wayland/xdg-toplevel-icon-v1.xml
new file mode 100644
index 0000000000..fc409fef7c
--- /dev/null
+++ b/deps/wayland/xdg-toplevel-icon-v1.xml
@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="xdg_toplevel_icon_v1">
+
+ <copyright>
+ Copyright © 2023-2024 Matthias Klumpp
+ Copyright © 2024 David Edmundson
+
+ 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 icons to toplevels">
+ This protocol allows clients to set icons for their toplevel surfaces
+ either via the 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_manager_v1" version="1">
+ <description summary="interface to manage toplevel icons">
+ This interface allows clients to create toplevel window icons and set
+ them on toplevel windows to be displayed to the user.
+ </description>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy the toplevel icon manager">
+ Destroy the toplevel icon manager.
+ This does not destroy objects created with the manager.
+ </description>
+ </request>
+
+ <request name="create_icon">
+ <description summary="create a new icon instance">
+ Creates a new icon object. This icon can then be attached to a
+ xdg_toplevel via the 'set_icon' request.
+ </description>
+ <arg name="id" type="new_id" interface="xdg_toplevel_icon_v1"/>
+ </request>
+
+ <request name="set_icon">
+ <description summary="set an icon on a toplevel window">
+ This request assigns the icon 'icon' to 'toplevel', or clears the
+ toplevel icon if 'icon' was null.
+ This state is double-buffered and is applied on the next
+ wl_surface.commit of the toplevel.
+
+ After making this call, the xdg_toplevel_icon_v1 provided as 'icon'
+ can be destroyed by the client without 'toplevel' losing its icon.
+ The xdg_toplevel_icon_v1 is immutable from this point, and any
+ future attempts to change it must raise the
+ 'xdg_toplevel_icon_v1.immutable' protocol error.
+
+ The compositor must set the toplevel icon from either the pixel data
+ the icon provides, or by loading a stock icon using the icon name.
+ See the description of 'xdg_toplevel_icon_v1' for details.
+
+ If 'icon' 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).
+ If this request is passed an icon with no pixel buffers or icon name
+ assigned, the icon must be reset just like if 'icon' was null.
+ </description>
+ <arg name="toplevel" type="object" interface="xdg_toplevel" summary="the toplevel to act on"/>
+ <arg name="icon" type="object" interface="xdg_toplevel_icon_v1" allow-null="true"/>
+ </request>
+
+ <event name="icon_size">
+ <description summary="describes a supported &amp; preferred icon size">
+ This event indicates an icon size the compositor prefers to be
+ available if the client has scalable icons and can render to any size.
+
+ When the 'xdg_toplevel_icon_manager_v1' object is created, the
+ compositor may send one or more 'icon_size' events to describe the list
+ of preferred icon sizes. If the compositor has no size preference, it
+ may not send any 'icon_size' event, and it is up to the client to
+ decide a suitable icon size.
+
+ A sequence of 'icon_size' events must be finished with a 'done' event.
+ If the compositor has no size preferences, it must still send the
+ 'done' event, without any preceding 'icon_size' events.
+ </description>
+ <arg name="size" type="int"
+ summary="the edge size of the square icon in surface-local coordinates, e.g. 64"/>
+ </event>
+
+ <event name="done">
+ <description summary="all information has been sent">
+ This event is sent after all 'icon_size' events have been sent.
+ </description>
+ </event>
+ </interface>
+
+ <interface name="xdg_toplevel_icon_v1" version="1">
+ <description summary="a toplevel window icon">
+ This interface defines a toplevel icon.
+ An icon can have a name, and multiple buffers.
+ In order to be applied, the icon must have either a name, or at least
+ one buffer assigned. Applying an empty icon (with no buffer or name) to
+ a toplevel should reset its icon to the default icon.
+
+ It is up to compositor policy whether to prefer using a buffer or loading
+ an icon via its name. See 'set_name' and 'add_buffer' for details.
+ </description>
+
+ <enum name="error">
+ <entry name="invalid_buffer"
+ summary="the provided buffer does not satisfy requirements"
+ value="1"/>
+ <entry name="immutable"
+ summary="the icon has already been assigned to a toplevel and must not be changed"
+ value="2"/>
+ <entry name="no_buffer"
+ summary="the provided buffer has been destroyed before the toplevel icon"
+ value="3"/>
+ </enum>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy the icon object">
+ Destroys the 'xdg_toplevel_icon_v1' object.
+ The icon must still remain set on every toplevel it was assigned to,
+ until the toplevel icon is reset explicitly.
+ </description>
+ </request>
+
+ <request name="set_name">
+ <description summary="set an icon name">
+ This request assigns an icon name to this icon.
+ Any previously set name is overridden.
+
+ 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 compositor does not support icon names or cannot resolve
+ 'icon_name' according to the XDG icon theme specification it must
+ fall back to using pixel buffer data instead.
+
+ If this request is made after the icon has been assigned to a toplevel
+ via 'set_icon', a 'immutable' error must be raised.
+
+ [1]: https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
+ </description>
+ <arg name="icon_name" type="string"/>
+ </request>
+
+ <request name="add_buffer">
+ <description summary="add icon data from a pixel buffer">
+ This request adds pixel data supplied as wl_buffer to the icon.
+
+ The client should add pixel data for all icon sizes and scales that
+ it can provide, or which are explicitly requested by the compositor
+ via 'icon_size' events on xdg_toplevel_icon_manager_v1.
+
+ The wl_buffer supplying pixel data as 'buffer' must be backed by wl_shm
+ and must be a square (width and height being equal).
+ If any of these buffer requirements are not fulfilled, a 'invalid_buffer'
+ error must be raised.
+
+ If this icon instance already has a buffer of the same size and scale
+ from a previous 'add_buffer' request, data from the last request
+ overrides the preexisting pixel data.
+
+ The wl_buffer must be kept alive for as long as the xdg_toplevel_icon
+ it is associated with is not destroyed, otherwise a 'no_buffer' error
+ is raised. The buffer contents must not be modified after it was
+ assigned to the icon. As a result, the region of the wl_shm_pool's
+ backing storage used for the wl_buffer must not be modified after this
+ request is sent. The wl_buffer.release event is unused.
+
+ If this request is made after the icon has been assigned to a toplevel
+ via 'set_icon', a 'immutable' error must be raised.
+ </description>
+ <arg name="buffer" type="object" interface="wl_buffer"/>
+ <arg name="scale" type="int"
+ summary="the scaling factor of the icon, e.g. 1"/>
+ </request>
+ </interface>
+</protocol>
diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h
index 79b0628849..1dd1cda85a 100644
--- a/include/GLFW/glfw3.h
+++ b/include/GLFW/glfw3.h
@@ -3399,9 +3399,9 @@ GLFWAPI void glfwSetWindowTitle(GLFWwindow* window, const char* title);
*
* [bundle-guide]: https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/
*
- * @remark @wayland There is no existing protocol to change an icon, the
- * window will thus inherit the one defined in the application's desktop file.
- * This function will emit @ref GLFW_FEATURE_UNAVAILABLE.
+ * @remark @wayland This only works on compositors implementing the XDG toplevel icon protocol.
+ * This function will emit @ref GLFW_FEATURE_UNAVAILABLE if this protocol is not available.
+ * The icon must be square. Otherwise @ref GLFW_INVALID_VALUE will be emited.
*
* @thread_safety This function must only be called from the main thread.
*
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 1a085b2b6a..8118e45ed6 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -105,6 +105,7 @@ if (GLFW_BUILD_WAYLAND)
generate_wayland_protocol("fractional-scale-v1.xml")
generate_wayland_protocol("xdg-activation-v1.xml")
generate_wayland_protocol("xdg-decoration-unstable-v1.xml")
+ generate_wayland_protocol("xdg-toplevel-icon-v1.xml")
endif()
if (WIN32 AND GLFW_BUILD_SHARED_LIBRARY)
diff --git a/src/wl_init.c b/src/wl_init.c
index ef9e450360..0ff46c1891 100644
--- a/src/wl_init.c
+++ b/src/wl_init.c
@@ -49,6 +49,7 @@
#include "fractional-scale-v1-client-protocol.h"
#include "xdg-activation-v1-client-protocol.h"
#include "idle-inhibit-unstable-v1-client-protocol.h"
+#include "xdg-toplevel-icon-v1-client-protocol.h"
// NOTE: Versions of wayland-scanner prior to 1.17.91 named every global array of
// wl_interface pointers 'types', making it impossible to combine several unmodified
@@ -91,6 +92,10 @@
#include "idle-inhibit-unstable-v1-client-protocol-code.h"
#undef types
+#define types _glfw_toplevel_icon_types
+#include "xdg-toplevel-icon-v1-client-protocol-code.h"
+#undef types
+
static void wmBaseHandlePing(void* userData,
struct xdg_wm_base* wmBase,
uint32_t serial)
@@ -208,6 +213,13 @@ static void registryHandleGlobal(void* userData,
&wp_fractional_scale_manager_v1_interface,
1);
}
+ else if (strcmp(interface, xdg_toplevel_icon_manager_v1_interface.name) == 0)
+ {
+ _glfw.wl.toplevelIconManager =
+ wl_registry_bind(registry, name,
+ &xdg_toplevel_icon_manager_v1_interface,
+ 1);
+ }
}
static void registryHandleGlobalRemove(void* userData,
@@ -988,6 +1000,8 @@ void _glfwTerminateWayland(void)
xdg_activation_v1_destroy(_glfw.wl.activationManager);
if (_glfw.wl.fractionalScaleManager)
wp_fractional_scale_manager_v1_destroy(_glfw.wl.fractionalScaleManager);
+ if (_glfw.wl.toplevelIconManager)
+ xdg_toplevel_icon_manager_v1_destroy(_glfw.wl.toplevelIconManager);
if (_glfw.wl.registry)
wl_registry_destroy(_glfw.wl.registry);
if (_glfw.wl.display)
diff --git a/src/wl_platform.h b/src/wl_platform.h
index afa6f50af5..86116e086d 100644
--- a/src/wl_platform.h
+++ b/src/wl_platform.h
@@ -438,6 +438,7 @@ typedef struct _GLFWlibraryWayland
struct zwp_idle_inhibit_manager_v1* idleInhibitManager;
struct xdg_activation_v1* activationManager;
struct wp_fractional_scale_manager_v1* fractionalScaleManager;
+ struct xdg_toplevel_icon_manager_v1* toplevelIconManager;
_GLFWofferWayland* offers;
unsigned int offerCount;
diff --git a/src/wl_window.c b/src/wl_window.c
index 72c1a40260..0fa20bb2bb 100644
--- a/src/wl_window.c
+++ b/src/wl_window.c
@@ -51,6 +51,7 @@
#include "xdg-activation-v1-client-protocol.h"
#include "idle-inhibit-unstable-v1-client-protocol.h"
#include "fractional-scale-v1-client-protocol.h"
+#include "xdg-toplevel-icon-v1-client-protocol.h"
#define GLFW_BORDER_SIZE 4
#define GLFW_CAPTION_HEIGHT 24
@@ -2227,8 +2228,55 @@ void _glfwSetWindowTitleWayland(_GLFWwindow* window, const char* title)
void _glfwSetWindowIconWayland(_GLFWwindow* window,
int count, const GLFWimage* images)
{
- _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
- "Wayland: The platform does not support setting the window icon");
+ if (!_glfw.wl.toplevelIconManager)
+ {
+ _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
+ "Wayland: The platform does not support setting the window icon");
+ return;
+ }
+
+ if (!count)
+ {
+ if (window->wl.libdecor.frame)
+ xdg_toplevel_icon_manager_v1_set_icon(_glfw.wl.toplevelIconManager,
+ libdecor_frame_get_xdg_toplevel(window->wl.libdecor.frame),
+ NULL);
+ else if (window->wl.xdg.toplevel)
+ xdg_toplevel_icon_manager_v1_set_icon(_glfw.wl.toplevelIconManager, window->wl.xdg.toplevel, NULL);
+ return;
+ }
+
+ for (int i = 0; i < count; i++)
+ {
+ if (images[i].width != images[i].height)
+ {
+ _glfwInputError(GLFW_INVALID_VALUE,
+ "Wayland: The icon must be a square");
+ return;
+ }
+ }
+
+ struct xdg_toplevel_icon_v1 *icon = xdg_toplevel_icon_manager_v1_create_icon(_glfw.wl.toplevelIconManager);
+ struct wl_buffer *bufferArr[count];
+
+ for (int i = 0; i < count; i++)
+ {
+ bufferArr[i] = createShmBuffer(&images[i]);
+ xdg_toplevel_icon_v1_add_buffer(icon, bufferArr[i], 1);
+ }
+
+ if (window->wl.libdecor.frame)
+ xdg_toplevel_icon_manager_v1_set_icon(_glfw.wl.toplevelIconManager,
+ libdecor_frame_get_xdg_toplevel(window->wl.libdecor.frame),
+ icon);
+ else if (window->wl.xdg.toplevel)
+ xdg_toplevel_icon_manager_v1_set_icon(_glfw.wl.toplevelIconManager, window->wl.xdg.toplevel, icon);
+ xdg_toplevel_icon_v1_destroy(icon);
+
+ for (int i = 0; i < count; i++)
+ {
+ wl_buffer_destroy(bufferArr[i]);
+ }
}
void _glfwGetWindowPosWayland(_GLFWwindow* window, int* xpos, int* ypos)