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+
40844197DisplayServer::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 }
0 commit comments