diff --git a/src/meson.build b/src/meson.build index 49f3c4fe3..bfa8306d2 100644 --- a/src/meson.build +++ b/src/meson.build @@ -709,8 +709,12 @@ if have_wayland 'wayland/meta-wayland-xdg-foreign.c', 'wayland/meta-wayland-xdg-foreign.h', 'wayland/meta-wayland-xdg-foreign-private.h', + 'wayland/meta-wayland-xdg-session-manager.c', + 'wayland/meta-wayland-xdg-session-manager.h', 'wayland/meta-wayland-xdg-session-state.c', 'wayland/meta-wayland-xdg-session-state.h', + 'wayland/meta-wayland-xdg-session.c', + 'wayland/meta-wayland-xdg-session.h', 'wayland/meta-wayland-xdg-shell.c', 'wayland/meta-wayland-xdg-shell.h', 'wayland/meta-wayland-xdg-dialog.c', diff --git a/src/wayland/meta-wayland-private.h b/src/wayland/meta-wayland-private.h index 9cf193677..5016f7465 100644 --- a/src/wayland/meta-wayland-private.h +++ b/src/wayland/meta-wayland-private.h @@ -103,6 +103,7 @@ struct _MetaWaylandCompositor MetaWaylandTabletManager *tablet_manager; MetaWaylandActivation *activation; MetaWaylandXdgForeign *foreign; + MetaWaylandXdgSessionManager *session_manager; GHashTable *scheduled_surface_associations; diff --git a/src/wayland/meta-wayland-versions.h b/src/wayland/meta-wayland-versions.h index 897bd3cc3..f0d035506 100644 --- a/src/wayland/meta-wayland-versions.h +++ b/src/wayland/meta-wayland-versions.h @@ -60,3 +60,4 @@ #define META_XX_COLOR_MANAGEMENT_VERSION 1 #define META_XDG_DIALOG_VERSION 1 #define META_WP_DRM_LEASE_DEVICE_V1_VERSION 1 +#define META_XDG_SESSION_MANAGER_V1_VERSION 1 diff --git a/src/wayland/meta-wayland-xdg-session-manager.c b/src/wayland/meta-wayland-xdg-session-manager.c new file mode 100644 index 000000000..6eee0b0e1 --- /dev/null +++ b/src/wayland/meta-wayland-xdg-session-manager.c @@ -0,0 +1,397 @@ +/* + * Copyright (C) 2024 Red Hat + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + */ + +#include "config.h" + +#include "wayland/meta-wayland-xdg-session-manager.h" + +#include + +#include "wayland/meta-wayland-xdg-shell.h" +#include "wayland/meta-wayland-xdg-session.h" +#include "wayland/meta-wayland-private.h" +#include "core/meta-debug-control-private.h" +#include "core/meta-session-manager.h" + +#include "session-management-v1-server-protocol.h" + +typedef struct _MetaWaylandXdgSessionManager +{ + MetaWaylandCompositor *compositor; + struct wl_global *global; + + GHashTable *sessions; + GHashTable *session_states; +} MetaWaylandXdgSessionManager; + +static void xdg_session_manager_remove_session (MetaWaylandXdgSessionManager *session_manager, + MetaWaylandXdgSession *session); + +static void +xdg_session_manager_destroy (struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy (resource); +} + +static gboolean +on_restore_toplevel (MetaWaylandXdgSession *session, + MetaWaylandXdgToplevel *xdg_toplevel, + const char *name, + MetaWaylandXdgSessionManager *xdg_session_manager) +{ + MetaContext *context = + meta_wayland_compositor_get_context (xdg_session_manager->compositor); + MetaSessionManager *session_manager = + meta_context_get_session_manager (context); + MetaSessionState *session_state; + MetaWaylandSurface *surface; + MetaWindow *window; + + session_state = + meta_session_manager_get_session (META_SESSION_MANAGER (session_manager), + META_TYPE_WAYLAND_XDG_SESSION_STATE, + meta_wayland_xdg_session_get_id (session)); + + surface = + meta_wayland_surface_role_get_surface (META_WAYLAND_SURFACE_ROLE (xdg_toplevel)); + if (!surface) + return FALSE; + + window = meta_wayland_surface_get_toplevel_window (surface); + if (!window) + return FALSE; + + if (!meta_session_state_restore_window (session_state, name, window)) + return FALSE; + + meta_wayland_xdg_toplevel_set_hint_restored (xdg_toplevel); + + return TRUE; +} + +static void +on_save_toplevel (MetaWaylandXdgSession *session, + MetaWaylandXdgToplevel *xdg_toplevel, + const char *name, + MetaWindow *window, + MetaWaylandXdgSessionManager *xdg_session_manager) +{ + MetaContext *context = + meta_wayland_compositor_get_context (xdg_session_manager->compositor); + MetaSessionManager *session_manager = + meta_context_get_session_manager (context); + MetaSessionState *session_state; + + session_state = + meta_session_manager_get_session (META_SESSION_MANAGER (session_manager), + META_TYPE_WAYLAND_XDG_SESSION_STATE, + meta_wayland_xdg_session_get_id (session)); + + meta_session_state_save_window (session_state, name, window); +} + +static void +on_remove_toplevel (MetaWaylandXdgSession *session, + const char *name, + MetaWaylandXdgSessionManager *xdg_session_manager) +{ + MetaContext *context = + meta_wayland_compositor_get_context (xdg_session_manager->compositor); + MetaSessionManager *session_manager = + meta_context_get_session_manager (context); + MetaSessionState *session_state; + + session_state = + meta_session_manager_get_session (session_manager, + META_TYPE_WAYLAND_XDG_SESSION_STATE, + meta_wayland_xdg_session_get_id (session)); + + meta_session_state_remove_window (session_state, name); +} + +static void +on_session_destroyed (MetaWaylandXdgSession *session, + MetaWaylandXdgSessionManager *session_manager) +{ + xdg_session_manager_remove_session (session_manager, session); +} + +static void +on_session_delete (MetaWaylandXdgSession *xdg_session, + MetaWaylandXdgSessionManager *xdg_session_manager) +{ + MetaContext *context = + meta_wayland_compositor_get_context (xdg_session_manager->compositor); + MetaSessionManager *session_manager = + meta_context_get_session_manager (context); + const char *session_id = meta_wayland_xdg_session_get_id (xdg_session); + + g_hash_table_remove (xdg_session_manager->session_states, session_id); + meta_session_manager_delete_session (session_manager, session_id); +} + +static void +xdg_session_manager_remove_session (MetaWaylandXdgSessionManager *session_manager, + MetaWaylandXdgSession *session) +{ + const char *session_id = meta_wayland_xdg_session_get_id (session); + + g_signal_handlers_disconnect_by_func (session, + on_session_destroyed, + session_manager); + g_signal_handlers_disconnect_by_func (session, + on_restore_toplevel, + session_manager); + g_signal_handlers_disconnect_by_func (session, + on_save_toplevel, + session_manager); + g_signal_handlers_disconnect_by_func (session, + on_remove_toplevel, + session_manager); + g_signal_handlers_disconnect_by_func (session, + on_session_delete, + session_manager); + + g_hash_table_remove (session_manager->sessions, session_id); +} + +static char * +generate_session_id (MetaWaylandXdgSessionManager *session_manager) +{ + while (TRUE) + { + g_autofree char *id = NULL; + + id = g_uuid_string_random (); + if (!g_hash_table_lookup (session_manager->sessions, id)) + return g_steal_pointer (&id); + } +} + +static void +xdg_session_manager_get_session (struct wl_client *client, + struct wl_resource *resource, + uint32_t id, + uint32_t reason_value, + const char *session_id) +{ + MetaWaylandXdgSessionManager *xdg_session_manager = + wl_resource_get_user_data (resource); + MetaContext *context = + meta_wayland_compositor_get_context (xdg_session_manager->compositor); + MetaSessionManager *session_manager = + meta_context_get_session_manager (context); + g_autoptr (MetaSessionState) session_state = NULL; + g_autoptr (MetaWaylandXdgSession) session = NULL; + g_autofree char *name = NULL, *stolen_name = NULL; + gboolean created = FALSE; + + /* Unknown session ID is the same as NULL */ + if (session_id && + !meta_session_manager_get_session_exists (session_manager, session_id)) + session_id = NULL; + + if (session_id) + { + MetaWaylandXdgSession *prev_session; + + prev_session = + g_hash_table_lookup (xdg_session_manager->sessions, session_id); + + if (prev_session) + { + if (meta_wayland_xdg_session_is_same_client (prev_session, client)) + { + wl_resource_post_error (resource, + XX_SESSION_MANAGER_V1_ERROR_IN_USE, + "Session %s already in use", + session_id); + return; + } + + /* Replace existing session */ + meta_wayland_xdg_session_emit_replaced (prev_session); + xdg_session_manager_remove_session (xdg_session_manager, + prev_session); + } + + name = g_strdup (session_id); + } + else + { + name = generate_session_id (xdg_session_manager); + created = TRUE; + } + + if (!g_hash_table_steal_extended (xdg_session_manager->session_states, + name, + (gpointer *) &stolen_name, + (gpointer *) &session_state)) + { + session_state = + meta_session_manager_get_session (session_manager, + META_TYPE_WAYLAND_XDG_SESSION_STATE, + name); + } + + session = meta_wayland_xdg_session_new (META_WAYLAND_XDG_SESSION_STATE (session_state), + client, + wl_resource_get_version (resource), + id); + g_signal_connect (session, "destroyed", + G_CALLBACK (on_session_destroyed), xdg_session_manager); + g_signal_connect (session, "restore-toplevel", + G_CALLBACK (on_restore_toplevel), xdg_session_manager); + g_signal_connect (session, "save-toplevel", + G_CALLBACK (on_save_toplevel), xdg_session_manager); + g_signal_connect (session, "remove-toplevel", + G_CALLBACK (on_remove_toplevel), xdg_session_manager); + g_signal_connect (session, "delete", + G_CALLBACK (on_session_delete), xdg_session_manager); + + if (created) + meta_wayland_xdg_session_emit_created (session); + else + meta_wayland_xdg_session_emit_restored (session); + + g_hash_table_insert (xdg_session_manager->sessions, + g_strdup (name), + session); + + g_hash_table_insert (xdg_session_manager->session_states, + g_strdup (name), + g_steal_pointer (&session_state)); +} + +static const struct xx_session_manager_v1_interface meta_xdg_session_manager_interface = { + xdg_session_manager_destroy, + xdg_session_manager_get_session, +}; + +static void +bind_session_manager (struct wl_client *client, + void *data, + uint32_t version, + uint32_t id) +{ + MetaWaylandXdgSessionManager *session_manager = data; + struct wl_resource *resource; + + resource = wl_resource_create (client, &xx_session_manager_v1_interface, + version, id); + wl_resource_set_implementation (resource, &meta_xdg_session_manager_interface, + session_manager, NULL); +} + +static void +update_enabled (MetaWaylandXdgSessionManager *session_manager) +{ + MetaWaylandCompositor *compositor = session_manager->compositor; + MetaDebugControl *debug_control = + meta_context_get_debug_control (compositor->context); + gboolean is_enabled; + + is_enabled = + meta_debug_control_is_session_management_protocol_enabled (debug_control); + + if (is_enabled && !session_manager->global) + { + struct wl_display *wayland_display; + + wayland_display = + meta_wayland_compositor_get_wayland_display (compositor); + + session_manager->global = + wl_global_create (wayland_display, + &xx_session_manager_v1_interface, + META_XDG_SESSION_MANAGER_V1_VERSION, + session_manager, bind_session_manager); + if (!session_manager->global) + g_error ("Could not create session manager global"); + } + else if (!is_enabled) + { + g_clear_pointer (&session_manager->global, wl_global_destroy); + } +} + +static MetaWaylandXdgSessionManager * +meta_wayland_session_manager_new (MetaWaylandCompositor *compositor) +{ + MetaWaylandXdgSessionManager *session_manager; + + session_manager = g_new0 (MetaWaylandXdgSessionManager, 1); + session_manager->compositor = compositor; + + session_manager->sessions = + g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); + session_manager->session_states = + g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, g_object_unref); + + return session_manager; +} + +static void +meta_wayland_session_manager_free (MetaWaylandXdgSessionManager *session_manager) +{ + g_clear_pointer (&session_manager->sessions, g_hash_table_unref); + g_clear_pointer (&session_manager->session_states, g_hash_table_unref); + g_free (session_manager); +} + +static void +on_protocol_enabled_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + MetaWaylandXdgSessionManager *session_manager = user_data; + + update_enabled (session_manager); +} + +void +meta_wayland_xdg_session_management_init (MetaWaylandCompositor *compositor) +{ + MetaDebugControl *debug_control = + meta_context_get_debug_control (compositor->context); + + compositor->session_manager = meta_wayland_session_manager_new (compositor); + + g_signal_connect (debug_control, "notify::session-management-protocol", + G_CALLBACK (on_protocol_enabled_changed), + compositor->session_manager); + + update_enabled (compositor->session_manager); +} + +void +meta_wayland_xdg_session_management_finalize (MetaWaylandCompositor *compositor) +{ + MetaDebugControl *debug_control = + meta_context_get_debug_control (compositor->context); + + g_signal_handlers_disconnect_by_func (debug_control, + on_protocol_enabled_changed, + compositor->session_manager); + + g_clear_pointer (&compositor->session_manager, + meta_wayland_session_manager_free); +} diff --git a/src/wayland/meta-wayland-xdg-session-manager.h b/src/wayland/meta-wayland-xdg-session-manager.h new file mode 100644 index 000000000..2b73f6972 --- /dev/null +++ b/src/wayland/meta-wayland-xdg-session-manager.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2024 Red Hat + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + */ + +#pragma once + +#include "wayland/meta-wayland-types.h" + +void meta_wayland_xdg_session_management_init (MetaWaylandCompositor *compositor); + +void meta_wayland_xdg_session_management_finalize (MetaWaylandCompositor *compositor); diff --git a/src/wayland/meta-wayland-xdg-session.c b/src/wayland/meta-wayland-xdg-session.c new file mode 100644 index 000000000..0863f463d --- /dev/null +++ b/src/wayland/meta-wayland-xdg-session.c @@ -0,0 +1,457 @@ +/* + * Copyright (C) 2024 Red Hat + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + */ + +#include "config.h" + +#include "wayland/meta-wayland-xdg-session.h" + +#include + +#include "wayland/meta-wayland-xdg-session-state.h" +#include "wayland/meta-wayland-xdg-shell.h" + +#include "session-management-v1-server-protocol.h" + + +typedef struct _MetaWaylandXdgToplevelSession +{ + grefcount ref_count; + MetaWaylandSurface *surface; + struct wl_resource *resource; + MetaWaylandXdgSession *session; + char *name; +} MetaWaylandXdgToplevelSession; + +enum +{ + DESTROYED, + RESTORE_TOPLEVEL, + SAVE_TOPLEVEL, + REMOVE_TOPLEVEL, + DELETE, + + N_SIGNALS +}; + +static guint signals[N_SIGNALS]; + +struct _MetaWaylandXdgSession +{ + GObject parent; + + char *id; + struct wl_resource *resource; + GHashTable *toplevels; /* name -> MetaWaylandXdgToplevelSession */ +}; + +G_DEFINE_FINAL_TYPE (MetaWaylandXdgSession, + meta_wayland_xdg_session, + G_TYPE_OBJECT) + +static void on_window_unmanaging (MetaWindow *window, + MetaWaylandXdgToplevelSession *toplevel_session); + +static MetaWaylandXdgToplevelSession * +meta_wayland_xdg_toplevel_session_ref (MetaWaylandXdgToplevelSession *toplevel_session) +{ + g_ref_count_inc (&toplevel_session->ref_count); + return toplevel_session; +} + +static void +meta_wayland_xdg_toplevel_session_unref (MetaWaylandXdgToplevelSession *toplevel_session) +{ + if (g_ref_count_dec (&toplevel_session->ref_count)) + { + MetaWindow *window = NULL; + MetaWaylandSurface *surface = toplevel_session->surface; + + if (surface) + window = meta_wayland_surface_get_toplevel_window (surface); + + if (window) + { + g_signal_handlers_disconnect_by_func (window, + on_window_unmanaging, + toplevel_session); + } + + g_free (toplevel_session->name); + g_free (toplevel_session); + } +} + +static void +xdg_toplevel_session_destroy (struct wl_client *wl_client, + struct wl_resource *resource) +{ + wl_resource_destroy (resource); +} + +static void +xdg_toplevel_session_remove (struct wl_client *wl_client, + struct wl_resource *resource) +{ + MetaWaylandXdgToplevelSession *toplevel_session = + wl_resource_get_user_data (resource); + MetaWaylandXdgSession *session = toplevel_session->session; + + if (session) + { + g_signal_emit (session, signals[REMOVE_TOPLEVEL], 0, toplevel_session->name); + g_hash_table_remove (session->toplevels, toplevel_session->name); + } + + wl_resource_destroy (resource); +} + +static const struct xx_toplevel_session_v1_interface meta_xdg_toplevel_session_interface = { + xdg_toplevel_session_destroy, + xdg_toplevel_session_remove, +}; + +static void +xdg_toplevel_session_destructor (struct wl_resource *resource) +{ + MetaWaylandXdgToplevelSession *toplevel_session = + wl_resource_get_user_data (resource); + + meta_wayland_xdg_toplevel_session_unref (toplevel_session); +} + +static MetaWaylandXdgToplevelSession * +meta_wayland_xdg_toplevel_session_new (MetaWaylandXdgSession *xdg_session, + MetaWaylandSurface *surface, + const char *name, + struct wl_client *wl_client, + uint32_t version, + uint32_t id) +{ + MetaWaylandXdgToplevelSession *toplevel_session; + + toplevel_session = g_new0 (MetaWaylandXdgToplevelSession, 1); + g_ref_count_init (&toplevel_session->ref_count); + toplevel_session->surface = surface; + toplevel_session->session = xdg_session; + toplevel_session->name = g_strdup (name); + toplevel_session->resource = + wl_resource_create (wl_client, + &xx_toplevel_session_v1_interface, + version, id); + wl_resource_set_implementation (toplevel_session->resource, + &meta_xdg_toplevel_session_interface, + meta_wayland_xdg_toplevel_session_ref (toplevel_session), + xdg_toplevel_session_destructor); + + return toplevel_session; +} + +static void +meta_wayland_xdg_toplevel_session_emit_restored (MetaWaylandXdgToplevelSession *toplevel_session) +{ + MetaWaylandXdgToplevel *xdg_toplevel = + META_WAYLAND_XDG_TOPLEVEL (toplevel_session->surface->role); + struct wl_resource *xdg_toplevel_resource = + meta_wayland_xdg_toplevel_get_resource (xdg_toplevel); + + xx_toplevel_session_v1_send_restored (toplevel_session->resource, + xdg_toplevel_resource); +} + +static void +meta_wayland_xdg_session_dispose (GObject *object) +{ + MetaWaylandXdgSession *session = META_WAYLAND_XDG_SESSION (object); + + g_clear_pointer (&session->id, g_free); + g_clear_pointer (&session->toplevels, g_hash_table_unref); + + G_OBJECT_CLASS (meta_wayland_xdg_session_parent_class)->dispose (object); +} + +static void +meta_wayland_xdg_session_class_init (MetaWaylandXdgSessionClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = meta_wayland_xdg_session_dispose; + + signals[DESTROYED] = + g_signal_new ("destroyed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + NULL, + G_TYPE_NONE, 0); + + signals[RESTORE_TOPLEVEL] = + g_signal_new ("restore-toplevel", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + g_signal_accumulator_true_handled, + NULL, + NULL, + G_TYPE_BOOLEAN, 2, + META_TYPE_WAYLAND_XDG_TOPLEVEL, + G_TYPE_STRING); + signals[SAVE_TOPLEVEL] = + g_signal_new ("save-toplevel", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 3, + META_TYPE_WAYLAND_XDG_TOPLEVEL, + G_TYPE_STRING, + META_TYPE_WINDOW); + signals[REMOVE_TOPLEVEL] = + g_signal_new ("remove-toplevel", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 1, + G_TYPE_STRING); + signals[DELETE] = + g_signal_new ("delete", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 0); +} + +static void +meta_wayland_xdg_session_init (MetaWaylandXdgSession *session) +{ +} + +static void +xdg_session_destroy (struct wl_client *wl_client, + struct wl_resource *resource) +{ + wl_resource_destroy (resource); +} + +static void +xdg_session_remove (struct wl_client *wl_client, + struct wl_resource *resource) +{ + MetaWaylandXdgSession *session = + META_WAYLAND_XDG_SESSION (wl_resource_get_user_data (resource)); + + g_signal_emit (session, signals[DELETE], 0); + + wl_resource_destroy (resource); +} + +static void +on_window_unmanaging (MetaWindow *window, + MetaWaylandXdgToplevelSession *toplevel_session) +{ + MetaWaylandXdgSession *session = toplevel_session->session; + + if (session) + { + MetaWaylandXdgToplevel *xdg_toplevel = + META_WAYLAND_XDG_TOPLEVEL (toplevel_session->surface->role); + + g_signal_emit (session, signals[SAVE_TOPLEVEL], 0, + xdg_toplevel, toplevel_session->name, window); + } + + toplevel_session->surface = NULL; +} + +static void +xdg_session_add_toplevel (struct wl_client *wl_client, + struct wl_resource *resource, + uint32_t id, + struct wl_resource *toplevel_resource, + const char *name) +{ + MetaWaylandXdgSession *session = + META_WAYLAND_XDG_SESSION (wl_resource_get_user_data (resource)); + MetaWaylandXdgToplevel *xdg_toplevel = + wl_resource_get_user_data (toplevel_resource); + MetaWaylandSurfaceRole *surface_role; + MetaWaylandSurface *surface; + MetaWaylandXdgToplevelSession *toplevel_session; + MetaWindow *window; + + if (g_hash_table_lookup (session->toplevels, name)) + { + wl_resource_post_error (resource, XX_SESSION_V1_ERROR_NAME_IN_USE, + "Name of toplevel was already in use"); + return; + } + + surface_role = META_WAYLAND_SURFACE_ROLE (xdg_toplevel); + surface = meta_wayland_surface_role_get_surface (surface_role); + toplevel_session = + meta_wayland_xdg_toplevel_session_new (session, surface, name, + wl_client, + wl_resource_get_version (resource), + id); + g_hash_table_insert (session->toplevels, g_strdup (name), toplevel_session); + + window = meta_wayland_surface_get_toplevel_window (surface); + if (window) + { + g_signal_connect (window, "unmanaging", + G_CALLBACK (on_window_unmanaging), toplevel_session); + } +} + +static void +xdg_session_restore_toplevel (struct wl_client *wl_client, + struct wl_resource *resource, + uint32_t id, + struct wl_resource *toplevel_resource, + const char *name) +{ + MetaWaylandXdgSession *session = + META_WAYLAND_XDG_SESSION (wl_resource_get_user_data (resource)); + MetaWaylandXdgToplevel *xdg_toplevel = + wl_resource_get_user_data (toplevel_resource); + MetaWaylandSurfaceRole *surface_role; + MetaWaylandSurface *surface; + MetaWaylandXdgToplevelSession *toplevel_session; + MetaWindow *window; + gboolean restored = FALSE; + + if (g_hash_table_lookup (session->toplevels, name)) + { + wl_resource_post_error (resource, XX_SESSION_V1_ERROR_NAME_IN_USE, + "Name of toplevel was already in use"); + return; + } + + surface_role = META_WAYLAND_SURFACE_ROLE (xdg_toplevel); + surface = meta_wayland_surface_role_get_surface (surface_role); + if (meta_wayland_surface_has_initial_commit (surface)) + { + wl_resource_post_error (resource, XX_SESSION_V1_ERROR_ALREADY_MAPPED, + "Tried to restore an already mapped toplevel"); + return; + } + + toplevel_session = + meta_wayland_xdg_toplevel_session_new (session, surface, name, + wl_client, + wl_resource_get_version (resource), + id); + g_hash_table_insert (session->toplevels, g_strdup (name), toplevel_session); + + window = meta_wayland_surface_get_toplevel_window (surface); + if (window) + { + g_signal_connect (window, "unmanaging", + G_CALLBACK (on_window_unmanaging), toplevel_session); + } + + g_signal_emit (session, + signals[RESTORE_TOPLEVEL], 0, + xdg_toplevel, name, &restored); + + if (restored) + meta_wayland_xdg_toplevel_session_emit_restored (toplevel_session); +} + +static const struct xx_session_v1_interface meta_xdg_session_interface = { + xdg_session_destroy, + xdg_session_remove, + xdg_session_add_toplevel, + xdg_session_restore_toplevel, +}; + +static void +xdg_session_destructor (struct wl_resource *resource) +{ + MetaWaylandXdgSession *session = + META_WAYLAND_XDG_SESSION (wl_resource_get_user_data (resource)); + GHashTableIter iter; + gpointer value; + + g_signal_emit (session, signals[DESTROYED], 0); + + g_hash_table_iter_init (&iter, session->toplevels); + while (g_hash_table_iter_next (&iter, NULL, &value)) + { + MetaWaylandXdgToplevelSession *toplevel_session = value; + + toplevel_session->session = NULL; + } + + g_object_unref (session); +} + +MetaWaylandXdgSession * +meta_wayland_xdg_session_new (MetaWaylandXdgSessionState *session_state, + struct wl_client *wl_client, + uint32_t version, + uint32_t id) +{ + g_autoptr (MetaWaylandXdgSession) session = NULL; + + session = g_object_new (META_TYPE_WAYLAND_XDG_SESSION, NULL); + session->id = + g_strdup (meta_session_state_get_name (META_SESSION_STATE (session_state))); + session->toplevels = + g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, + (GDestroyNotify) meta_wayland_xdg_toplevel_session_unref); + session->resource = wl_resource_create (wl_client, + &xx_session_v1_interface, + version, id); + wl_resource_set_implementation (session->resource, + &meta_xdg_session_interface, + g_object_ref (session), + xdg_session_destructor); + + return g_steal_pointer (&session); +} + +const char * +meta_wayland_xdg_session_get_id (MetaWaylandXdgSession *session) +{ + return session->id; +} + +void +meta_wayland_xdg_session_emit_created (MetaWaylandXdgSession *session) +{ + xx_session_v1_send_created (session->resource, session->id); +} + +void +meta_wayland_xdg_session_emit_replaced (MetaWaylandXdgSession *session) +{ + xx_session_v1_send_replaced (session->resource); +} + +void +meta_wayland_xdg_session_emit_restored (MetaWaylandXdgSession *session) +{ + xx_session_v1_send_restored (session->resource); +} + +gboolean +meta_wayland_xdg_session_is_same_client (MetaWaylandXdgSession *session, + struct wl_client *client) +{ + return wl_resource_get_client (session->resource) == client; +} diff --git a/src/wayland/meta-wayland-xdg-session.h b/src/wayland/meta-wayland-xdg-session.h new file mode 100644 index 000000000..f94a7a9b7 --- /dev/null +++ b/src/wayland/meta-wayland-xdg-session.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2024 Red Hat + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + */ + +#pragma once + +#include + +#include "wayland/meta-wayland-types.h" +#include "wayland/meta-wayland-xdg-session-state.h" + +#define META_TYPE_WAYLAND_XDG_SESSION (meta_wayland_xdg_session_get_type ()) +G_DECLARE_FINAL_TYPE (MetaWaylandXdgSession, + meta_wayland_xdg_session, + META, WAYLAND_XDG_SESSION, + GObject) + +MetaWaylandXdgSession * meta_wayland_xdg_session_new (MetaWaylandXdgSessionState *session_state, + struct wl_client *wl_client, + uint32_t version, + uint32_t id); + +const char * meta_wayland_xdg_session_get_id (MetaWaylandXdgSession *session); + +void meta_wayland_xdg_session_emit_created (MetaWaylandXdgSession *session); + +void meta_wayland_xdg_session_emit_replaced (MetaWaylandXdgSession *session); + +void meta_wayland_xdg_session_emit_restored (MetaWaylandXdgSession *session); + +gboolean meta_wayland_xdg_session_is_same_client (MetaWaylandXdgSession *session, + struct wl_client *client); diff --git a/src/wayland/meta-wayland.c b/src/wayland/meta-wayland.c index d38efd178..fd7b6e473 100644 --- a/src/wayland/meta-wayland.c +++ b/src/wayland/meta-wayland.c @@ -48,6 +48,7 @@ #include "wayland/meta-wayland-inhibit-shortcuts-dialog.h" #include "wayland/meta-wayland-inhibit-shortcuts.h" #include "wayland/meta-wayland-legacy-xdg-foreign.h" +#include "wayland/meta-wayland-linux-drm-syncobj.h" #include "wayland/meta-wayland-outputs.h" #include "wayland/meta-wayland-presentation-time-private.h" #include "wayland/meta-wayland-private.h" @@ -58,7 +59,7 @@ #include "wayland/meta-wayland-transaction.h" #include "wayland/meta-wayland-xdg-dialog.h" #include "wayland/meta-wayland-xdg-foreign.h" -#include "wayland/meta-wayland-linux-drm-syncobj.h" +#include "wayland/meta-wayland-xdg-session-manager.h" #ifdef HAVE_XWAYLAND #include "wayland/meta-wayland-x11-interop.h" @@ -690,6 +691,7 @@ meta_wayland_compositor_finalize (GObject *object) MetaBackend *backend = meta_context_get_backend (compositor->context); ClutterActor *stage = meta_backend_get_stage (backend); + meta_wayland_xdg_session_management_finalize (compositor); meta_wayland_activation_finalize (compositor); meta_wayland_outputs_finalize (compositor); meta_wayland_presentation_time_finalize (compositor); @@ -892,6 +894,7 @@ meta_wayland_compositor_new (MetaContext *context) meta_wayland_drm_syncobj_init (compositor); meta_wayland_init_xdg_wm_dialog (compositor); meta_wayland_init_color_management (compositor); + meta_wayland_xdg_session_management_init (compositor); #ifdef HAVE_NATIVE_BACKEND meta_wayland_drm_lease_manager_init (compositor);