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 );
@@ -3521,6 +3548,13 @@ void WaylandThread::window_create(DisplayServer::WindowID p_window_id, int p_wid
35213548 ws.libdecor_frame = libdecor_decorate (libdecor_context, ws.wl_surface , (struct libdecor_frame_interface *)&libdecor_frame_interface, &ws);
35223549 libdecor_frame_map (ws.libdecor_frame );
35233550
3551+ if (registry.xdg_toplevel_icon_manager ) {
3552+ xdg_toplevel *toplevel = libdecor_frame_get_xdg_toplevel (ws.libdecor_frame );
3553+ if (toplevel != nullptr ) {
3554+ xdg_toplevel_icon_manager_v1_set_icon (registry.xdg_toplevel_icon_manager , toplevel, xdg_icon);
3555+ }
3556+ }
3557+
35243558 decorated = true ;
35253559 }
35263560#endif
@@ -3541,6 +3575,10 @@ void WaylandThread::window_create(DisplayServer::WindowID p_window_id, int p_wid
35413575
35423576 decorated = true ;
35433577 }
3578+
3579+ if (registry.xdg_toplevel_icon_manager ) {
3580+ xdg_toplevel_icon_manager_v1_set_icon (registry.xdg_toplevel_icon_manager , ws.xdg_toplevel , xdg_icon);
3581+ }
35443582 }
35453583
35463584 ws.frame_callback = wl_surface_frame (ws.wl_surface );
@@ -4093,6 +4131,81 @@ void WaylandThread::window_set_app_id(DisplayServer::WindowID p_window_id, const
40934131 }
40944132}
40954133
4134+ void WaylandThread::set_icon (const Ref<Image> &p_icon) {
4135+ ERR_FAIL_COND (p_icon.is_null ());
4136+
4137+ Size2i icon_size = p_icon->get_size ();
4138+ ERR_FAIL_COND (icon_size.width != icon_size.height );
4139+
4140+ if (!registry.xdg_toplevel_icon_manager ) {
4141+ return ;
4142+ }
4143+
4144+ if (xdg_icon) {
4145+ xdg_toplevel_icon_v1_destroy (xdg_icon);
4146+ }
4147+
4148+ if (icon_buffer) {
4149+ wl_buffer_destroy (icon_buffer);
4150+ }
4151+
4152+ // NOTE: The stride is the width of the icon in bytes.
4153+ uint32_t icon_stride = icon_size.width * 4 ;
4154+ uint32_t data_size = icon_stride * icon_size.height ;
4155+
4156+ // We need a shared memory object file descriptor in order to create a
4157+ // wl_buffer through wl_shm.
4158+ int fd = WaylandThread::_allocate_shm_file (data_size);
4159+ ERR_FAIL_COND (fd == -1 );
4160+
4161+ uint32_t *buffer_data = (uint32_t *)mmap (nullptr , data_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 );
4162+
4163+ // Create the Wayland buffer.
4164+ struct wl_shm_pool *shm_pool = wl_shm_create_pool (registry.wl_shm , fd, data_size);
4165+ icon_buffer = wl_shm_pool_create_buffer (shm_pool, 0 , icon_size.width , icon_size.height , icon_stride, WL_SHM_FORMAT_ARGB8888);
4166+ wl_shm_pool_destroy (shm_pool);
4167+
4168+ // Fill the cursor buffer with the image data.
4169+ for (uint32_t index = 0 ; index < (uint32_t )(icon_size.width * icon_size.height ); index++) {
4170+ int row_index = index / icon_size.width ;
4171+ int column_index = (index % icon_size.width );
4172+
4173+ buffer_data[index] = p_icon->get_pixel (column_index, row_index).to_argb32 ();
4174+
4175+ // Wayland buffers, unless specified, require associated alpha, so we'll just
4176+ // associate the alpha in-place.
4177+ uint8_t *pixel_data = (uint8_t *)&buffer_data[index];
4178+ pixel_data[0 ] = pixel_data[0 ] * pixel_data[3 ] / 255 ;
4179+ pixel_data[1 ] = pixel_data[1 ] * pixel_data[3 ] / 255 ;
4180+ pixel_data[2 ] = pixel_data[2 ] * pixel_data[3 ] / 255 ;
4181+ }
4182+
4183+ xdg_icon = xdg_toplevel_icon_manager_v1_create_icon (registry.xdg_toplevel_icon_manager );
4184+ xdg_toplevel_icon_v1_add_buffer (xdg_icon, icon_buffer, icon_size.width );
4185+
4186+ if (Engine::get_singleton ()->is_editor_hint () || Engine::get_singleton ()->is_project_manager_hint ()) {
4187+ // Setting a name allows the godot icon to be overridden by a system theme.
4188+ // We only want the project manager and editor to get themed,
4189+ // Games will get icons with the protocol and themed icons with .desktop entries.
4190+ // NOTE: should be synced with the icon name in misc/dist/linuxbsd/Godot.desktop
4191+ xdg_toplevel_icon_v1_set_name (xdg_icon, " godot" );
4192+ }
4193+
4194+ for (KeyValue<DisplayServer::WindowID, WindowState> &pair : windows) {
4195+ WindowState &ws = pair.value ;
4196+ #ifdef LIBDECOR_ENABLED
4197+ if (ws.libdecor_frame ) {
4198+ xdg_toplevel *toplevel = libdecor_frame_get_xdg_toplevel (ws.libdecor_frame );
4199+ ERR_FAIL_NULL (toplevel);
4200+ xdg_toplevel_icon_manager_v1_set_icon (registry.xdg_toplevel_icon_manager , toplevel, xdg_icon);
4201+ }
4202+ #endif
4203+ if (ws.xdg_toplevel ) {
4204+ xdg_toplevel_icon_manager_v1_set_icon (registry.xdg_toplevel_icon_manager , ws.xdg_toplevel , xdg_icon);
4205+ }
4206+ }
4207+ }
4208+
40964209DisplayServer::WindowMode WaylandThread::window_get_mode (DisplayServer::WindowID p_window_id) const {
40974210 ERR_FAIL_COND_V (!windows.has (p_window_id), DisplayServer::WINDOW_MODE_WINDOWED);
40984211 const WindowState &ws = windows[p_window_id];
@@ -4345,6 +4458,10 @@ Error WaylandThread::init() {
43454458 WARN_PRINT (" FIFO protocol not found! Frame pacing will be degraded." );
43464459 }
43474460
4461+ if (!registry.xdg_toplevel_icon_manager_name ) {
4462+ WARN_PRINT (" xdg-toplevel-icon protocol not found! Cannot set window icon." );
4463+ }
4464+
43484465 // Wait for seat capabilities.
43494466 wl_display_roundtrip (wl_display);
43504467
@@ -5012,6 +5129,18 @@ void WaylandThread::destroy() {
50125129 xdg_system_bell_v1_destroy (registry.xdg_system_bell );
50135130 }
50145131
5132+ if (registry.xdg_toplevel_icon_manager ) {
5133+ xdg_toplevel_icon_manager_v1_destroy (registry.xdg_toplevel_icon_manager );
5134+
5135+ if (xdg_icon) {
5136+ xdg_toplevel_icon_v1_destroy (xdg_icon);
5137+ }
5138+
5139+ if (icon_buffer) {
5140+ wl_buffer_destroy (icon_buffer);
5141+ }
5142+ }
5143+
50155144 if (registry.xdg_decoration_manager ) {
50165145 zxdg_decoration_manager_v1_destroy (registry.xdg_decoration_manager );
50175146 }
0 commit comments