Skip to content

Commit 188b47a

Browse files
committed
Wayland: Implement the xdg-toplevel-icon-v1 protocol
Closes #87747 Requires a compositor which supports xdg-toplevel-icon-v1. As of this commit only KWin supports this protocol.
1 parent 9282ed3 commit 188b47a

File tree

7 files changed

+358
-0
lines changed

7 files changed

+358
-0
lines changed

platform/linuxbsd/wayland/SCsub

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ objects = [
6464
generate_from_xml(
6565
"xdg_system_bell", "#thirdparty/wayland-protocols/staging/xdg-system-bell/xdg-system-bell-v1.xml"
6666
),
67+
generate_from_xml(
68+
"xdg_toplevel_icon", "#thirdparty/wayland-protocols/staging/xdg-toplevel-icon/xdg-toplevel-icon-v1.xml"
69+
),
6770
# Unstable protocols
6871
generate_from_xml(
6972
"idle_inhibit", "#thirdparty/wayland-protocols/unstable/idle-inhibit/idle-inhibit-unstable-v1.xml"

platform/linuxbsd/wayland/display_server_wayland.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ bool DisplayServerWayland::has_feature(Feature p_feature) const {
177177
case FEATURE_CURSOR_SHAPE:
178178
case FEATURE_CUSTOM_CURSOR_SHAPE:
179179
case FEATURE_WINDOW_TRANSPARENCY:
180+
case FEATURE_ICON:
180181
case FEATURE_HIDPI:
181182
case FEATURE_SWAP_BUFFERS:
182183
case FEATURE_KEEP_SCREEN_ON:
@@ -1810,6 +1811,11 @@ void DisplayServerWayland::swap_buffers() {
18101811
#endif
18111812
}
18121813

1814+
void DisplayServerWayland::set_icon(const Ref<Image> &p_icon) {
1815+
MutexLock mutex_lock(wayland_thread.mutex);
1816+
wayland_thread.set_icon(p_icon);
1817+
}
1818+
18131819
void DisplayServerWayland::set_context(Context p_context) {
18141820
MutexLock mutex_lock(wayland_thread.mutex);
18151821

platform/linuxbsd/wayland/display_server_wayland.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,8 @@ class DisplayServerWayland : public DisplayServer {
342342
virtual void release_rendering_thread() override;
343343
virtual void swap_buffers() override;
344344

345+
virtual void set_icon(const Ref<Image> &p_icon) override;
346+
345347
virtual void set_context(Context p_context) override;
346348

347349
virtual bool is_window_transparency_available() const override;

platform/linuxbsd/wayland/wayland_thread.cpp

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030

3131
#include "wayland_thread.h"
3232

33+
#include "core/config/engine.h"
34+
3335
#ifdef WAYLAND_ENABLED
3436

3537
#ifdef __FreeBSD__
@@ -574,6 +576,12 @@ void WaylandThread::_wl_registry_on_global(void *data, struct wl_registry *wl_re
574576
return;
575577
}
576578

579+
if (strcmp(interface, xdg_toplevel_icon_manager_v1_interface.name) == 0) {
580+
registry->xdg_toplevel_icon_manager = (struct xdg_toplevel_icon_manager_v1 *)wl_registry_bind(wl_registry, name, &xdg_toplevel_icon_manager_v1_interface, 1);
581+
registry->xdg_toplevel_icon_manager_name = name;
582+
return;
583+
}
584+
577585
if (strcmp(interface, xdg_activation_v1_interface.name) == 0) {
578586
registry->xdg_activation = (struct xdg_activation_v1 *)wl_registry_bind(wl_registry, name, &xdg_activation_v1_interface, 1);
579587
registry->xdg_activation_name = name;
@@ -818,6 +826,25 @@ void WaylandThread::_wl_registry_on_global_remove(void *data, struct wl_registry
818826
return;
819827
}
820828

829+
if (name == registry->xdg_toplevel_icon_manager_name) {
830+
if (registry->xdg_toplevel_icon_manager) {
831+
xdg_toplevel_icon_manager_v1_destroy(registry->xdg_toplevel_icon_manager);
832+
registry->xdg_toplevel_icon_manager = nullptr;
833+
}
834+
835+
if (registry->wayland_thread->xdg_icon) {
836+
xdg_toplevel_icon_v1_destroy(registry->wayland_thread->xdg_icon);
837+
}
838+
839+
if (registry->wayland_thread->icon_buffer) {
840+
wl_buffer_destroy(registry->wayland_thread->icon_buffer);
841+
}
842+
843+
registry->xdg_toplevel_icon_manager_name = 0;
844+
845+
return;
846+
}
847+
821848
if (name == registry->xdg_activation_name) {
822849
if (registry->xdg_activation) {
823850
xdg_activation_v1_destroy(registry->xdg_activation);
@@ -3509,6 +3536,13 @@ void WaylandThread::window_create(DisplayServer::WindowID p_window_id, int p_wid
35093536
ws.libdecor_frame = libdecor_decorate(libdecor_context, ws.wl_surface, (struct libdecor_frame_interface *)&libdecor_frame_interface, &ws);
35103537
libdecor_frame_map(ws.libdecor_frame);
35113538

3539+
if (registry.xdg_toplevel_icon_manager) {
3540+
xdg_toplevel *toplevel = libdecor_frame_get_xdg_toplevel(ws.libdecor_frame);
3541+
if (toplevel != nullptr) {
3542+
xdg_toplevel_icon_manager_v1_set_icon(registry.xdg_toplevel_icon_manager, toplevel, xdg_icon);
3543+
}
3544+
}
3545+
35123546
decorated = true;
35133547
}
35143548
#endif
@@ -3529,6 +3563,10 @@ void WaylandThread::window_create(DisplayServer::WindowID p_window_id, int p_wid
35293563

35303564
decorated = true;
35313565
}
3566+
3567+
if (registry.xdg_toplevel_icon_manager) {
3568+
xdg_toplevel_icon_manager_v1_set_icon(registry.xdg_toplevel_icon_manager, ws.xdg_toplevel, xdg_icon);
3569+
}
35323570
}
35333571

35343572
ws.frame_callback = wl_surface_frame(ws.wl_surface);
@@ -4081,6 +4119,81 @@ void WaylandThread::window_set_app_id(DisplayServer::WindowID p_window_id, const
40814119
}
40824120
}
40834121

4122+
void WaylandThread::set_icon(const Ref<Image> &p_icon) {
4123+
ERR_FAIL_COND(p_icon.is_null());
4124+
4125+
Size2i icon_size = p_icon->get_size();
4126+
ERR_FAIL_COND(icon_size.width != icon_size.height);
4127+
4128+
if (!registry.xdg_toplevel_icon_manager) {
4129+
return;
4130+
}
4131+
4132+
if (xdg_icon) {
4133+
xdg_toplevel_icon_v1_destroy(xdg_icon);
4134+
}
4135+
4136+
if (icon_buffer) {
4137+
wl_buffer_destroy(icon_buffer);
4138+
}
4139+
4140+
// NOTE: The stride is the width of the icon in bytes.
4141+
uint32_t icon_stride = icon_size.width * 4;
4142+
uint32_t data_size = icon_stride * icon_size.height;
4143+
4144+
// We need a shared memory object file descriptor in order to create a
4145+
// wl_buffer through wl_shm.
4146+
int fd = WaylandThread::_allocate_shm_file(data_size);
4147+
ERR_FAIL_COND(fd == -1);
4148+
4149+
uint32_t *buffer_data = (uint32_t *)mmap(nullptr, data_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
4150+
4151+
// Create the Wayland buffer.
4152+
struct wl_shm_pool *shm_pool = wl_shm_create_pool(registry.wl_shm, fd, data_size);
4153+
icon_buffer = wl_shm_pool_create_buffer(shm_pool, 0, icon_size.width, icon_size.height, icon_stride, WL_SHM_FORMAT_ARGB8888);
4154+
wl_shm_pool_destroy(shm_pool);
4155+
4156+
// Fill the cursor buffer with the image data.
4157+
for (uint32_t index = 0; index < (uint32_t)(icon_size.width * icon_size.height); index++) {
4158+
int row_index = index / icon_size.width;
4159+
int column_index = (index % icon_size.width);
4160+
4161+
buffer_data[index] = p_icon->get_pixel(column_index, row_index).to_argb32();
4162+
4163+
// Wayland buffers, unless specified, require associated alpha, so we'll just
4164+
// associate the alpha in-place.
4165+
uint8_t *pixel_data = (uint8_t *)&buffer_data[index];
4166+
pixel_data[0] = pixel_data[0] * pixel_data[3] / 255;
4167+
pixel_data[1] = pixel_data[1] * pixel_data[3] / 255;
4168+
pixel_data[2] = pixel_data[2] * pixel_data[3] / 255;
4169+
}
4170+
4171+
xdg_icon = xdg_toplevel_icon_manager_v1_create_icon(registry.xdg_toplevel_icon_manager);
4172+
xdg_toplevel_icon_v1_add_buffer(xdg_icon, icon_buffer, icon_size.width);
4173+
4174+
if (Engine::get_singleton()->is_editor_hint() || Engine::get_singleton()->is_project_manager_hint()) {
4175+
// Setting a name allows the godot icon to be overridden by a system theme.
4176+
// We only want the project manager and editor to get themed,
4177+
// Games will get icons with the protocol and themed icons with .desktop entries.
4178+
// NOTE: should be synced with the icon name in misc/dist/linuxbsd/Godot.desktop
4179+
xdg_toplevel_icon_v1_set_name(xdg_icon, "godot");
4180+
}
4181+
4182+
for (KeyValue<DisplayServer::WindowID, WindowState> &pair : windows) {
4183+
WindowState &ws = pair.value;
4184+
#ifdef LIBDECOR_ENABLED
4185+
if (ws.libdecor_frame) {
4186+
xdg_toplevel *toplevel = libdecor_frame_get_xdg_toplevel(ws.libdecor_frame);
4187+
ERR_FAIL_NULL(toplevel);
4188+
xdg_toplevel_icon_manager_v1_set_icon(registry.xdg_toplevel_icon_manager, toplevel, xdg_icon);
4189+
}
4190+
#endif
4191+
if (ws.xdg_toplevel) {
4192+
xdg_toplevel_icon_manager_v1_set_icon(registry.xdg_toplevel_icon_manager, ws.xdg_toplevel, xdg_icon);
4193+
}
4194+
}
4195+
}
4196+
40844197
DisplayServer::WindowMode WaylandThread::window_get_mode(DisplayServer::WindowID p_window_id) const {
40854198
ERR_FAIL_COND_V(!windows.has(p_window_id), DisplayServer::WINDOW_MODE_WINDOWED);
40864199
const WindowState &ws = windows[p_window_id];
@@ -4333,6 +4446,10 @@ Error WaylandThread::init() {
43334446
WARN_PRINT("FIFO protocol not found! Frame pacing will be degraded.");
43344447
}
43354448

4449+
if (!registry.xdg_toplevel_icon_manager_name) {
4450+
WARN_PRINT("xdg-toplevel-icon protocol not found! Cannot set window icon.");
4451+
}
4452+
43364453
// Wait for seat capabilities.
43374454
wl_display_roundtrip(wl_display);
43384455

@@ -5000,6 +5117,18 @@ void WaylandThread::destroy() {
50005117
xdg_system_bell_v1_destroy(registry.xdg_system_bell);
50015118
}
50025119

5120+
if (registry.xdg_toplevel_icon_manager) {
5121+
xdg_toplevel_icon_manager_v1_destroy(registry.xdg_toplevel_icon_manager);
5122+
5123+
if (xdg_icon) {
5124+
xdg_toplevel_icon_v1_destroy(xdg_icon);
5125+
}
5126+
5127+
if (icon_buffer) {
5128+
wl_buffer_destroy(icon_buffer);
5129+
}
5130+
}
5131+
50035132
if (registry.xdg_decoration_manager) {
50045133
zxdg_decoration_manager_v1_destroy(registry.xdg_decoration_manager);
50055134
}

platform/linuxbsd/wayland/wayland_thread.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
#include "wayland/protocol/xdg_foreign_v2.gen.h"
7171
#include "wayland/protocol/xdg_shell.gen.h"
7272
#include "wayland/protocol/xdg_system_bell.gen.h"
73+
#include "wayland/protocol/xdg_toplevel_icon.gen.h"
7374

7475
// NOTE: Deprecated.
7576
#include "wayland/protocol/xdg_foreign_v1.gen.h"
@@ -197,6 +198,9 @@ class WaylandThread {
197198
struct xdg_system_bell_v1 *xdg_system_bell = nullptr;
198199
uint32_t xdg_system_bell_name = 0;
199200

201+
struct xdg_toplevel_icon_manager_v1 *xdg_toplevel_icon_manager = nullptr;
202+
uint32_t xdg_toplevel_icon_manager_name = 0;
203+
200204
struct xdg_activation_v1 *xdg_activation = nullptr;
201205
uint32_t xdg_activation_name = 0;
202206

@@ -543,6 +547,9 @@ class WaylandThread {
543547

544548
List<Ref<Message>> messages;
545549

550+
xdg_toplevel_icon_v1 *xdg_icon = nullptr;
551+
wl_buffer *icon_buffer = nullptr;
552+
546553
String cursor_theme_name;
547554
int unscaled_cursor_size = 24;
548555

@@ -1023,6 +1030,8 @@ class WaylandThread {
10231030

10241031
void beep() const;
10251032

1033+
void set_icon(const Ref<Image> &p_icon);
1034+
10261035
void window_create(DisplayServer::WindowID p_window_id, int p_width, int p_height);
10271036
void window_create_popup(DisplayServer::WindowID p_window_id, DisplayServer::WindowID p_parent_id, Rect2i p_rect);
10281037
void window_destroy(DisplayServer::WindowID p_window_Id);
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
xdg_toplevel_icon protocol
2+
3+
Maintainers:
4+
Matthias Klumpp <[email protected]> (@mak)

0 commit comments

Comments
 (0)