diff --git a/0001-window-Don-t-switch-workspaces-if-users-from-forged-.patch b/0001-window-Don-t-switch-workspaces-if-users-from-forged-.patch new file mode 100644 index 0000000..0adcfc9 --- /dev/null +++ b/0001-window-Don-t-switch-workspaces-if-users-from-forged-.patch @@ -0,0 +1,101 @@ +From f1da6553e13e3c9a9ed91370dcf435fa4a19fb2b Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 6 Jun 2024 13:01:41 -0400 +Subject: [PATCH 1/6] window: Don't switch workspaces if users from forged + activation messages + +--- + src/core/window.c | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +diff --git a/src/core/window.c b/src/core/window.c +index 7d86adece..e787dbce0 100644 +--- a/src/core/window.c ++++ b/src/core/window.c +@@ -3687,74 +3687,81 @@ meta_window_unshade (MetaWindow *window, + "Focusing window %s after unshading it", + window->desc); + meta_window_focus (window, timestamp); + + set_net_wm_state (window); + } + } + + static gboolean + unminimize_func (MetaWindow *window, + void *data) + { + meta_window_unminimize (window); + return TRUE; + } + + static void + unminimize_window_and_all_transient_parents (MetaWindow *window) + { + meta_window_unminimize (window); + meta_window_foreach_ancestor (window, unminimize_func, NULL); + } + + void + meta_window_activate_full (MetaWindow *window, + guint32 timestamp, + MetaClientType source_indication, + MetaWorkspace *workspace) + { + MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; +- gboolean allow_workspace_switch; ++ gboolean allow_workspace_switch = FALSE; + + if (window->unmanaging) + { + g_warning ("Trying to activate unmanaged window '%s'", window->desc); + return; + } + + meta_topic (META_DEBUG_FOCUS, + "_NET_ACTIVE_WINDOW message sent for %s at time %u " + "by client type %u.", + window->desc, timestamp, source_indication); + +- allow_workspace_switch = (timestamp != 0); ++ if (window->display->last_user_time == timestamp) ++ { ++ /* Only allow workspace switches if this activation message uses the same ++ * timestamp as the last user interaction ++ */ ++ allow_workspace_switch = TRUE; ++ } ++ + if (timestamp != 0 && + XSERVER_TIME_IS_BEFORE (timestamp, window->display->last_user_time)) + { + meta_topic (META_DEBUG_FOCUS, + "last_user_time (%u) is more recent; ignoring " + " _NET_ACTIVE_WINDOW message.", + window->display->last_user_time); + meta_window_set_demands_attention(window); + return; + } + + if (timestamp == 0) + timestamp = meta_display_get_current_time_roundtrip (window->display); + + meta_window_set_user_time (window, timestamp); + + /* disable show desktop mode unless we're a desktop component */ + maybe_leave_show_desktop_mode (window); + + /* Get window on current or given workspace */ + if (workspace == NULL) + workspace = workspace_manager->active_workspace; + + /* For non-transient windows, we just set up a pulsing indicator, + rather than move windows or workspaces. + See http://bugzilla.gnome.org/show_bug.cgi?id=482354 */ + if (window->transient_for == NULL && + !allow_workspace_switch && + !meta_window_located_on_workspace (window, workspace)) + { +-- +2.44.0 + diff --git a/0002-core-events-Count-shell-interactions-has-user-intera.patch b/0002-core-events-Count-shell-interactions-has-user-intera.patch new file mode 100644 index 0000000..8e93f66 --- /dev/null +++ b/0002-core-events-Count-shell-interactions-has-user-intera.patch @@ -0,0 +1,133 @@ +From b2cf9836373a446d674ecce251e3e42bb863dc75 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 6 Jun 2024 13:32:03 -0400 +Subject: [PATCH 2/6] core/events: Count shell interactions has user + interactions too + +mutter keeps track of the last time the user used the system to +decide whether or not to prevent focus stealing. + +Right now, it only considers user interactions with application +windows, not interactions with the compositor chrome. + +That means a user could start loading an application, +switch workspaces, and get forcefully pulled back when the +application finishes loading. + +This commit fixes that problem by updating the user time on shell +interactions as well. +--- + src/core/events.c | 38 ++++++++++++++++++++++++-------------- + 1 file changed, 24 insertions(+), 14 deletions(-) + +diff --git a/src/core/events.c b/src/core/events.c +index 775104229..4d25b6dc0 100644 +--- a/src/core/events.c ++++ b/src/core/events.c +@@ -288,79 +288,89 @@ meta_display_handle_event (MetaDisplay *display, + if (source) + meta_backend_update_last_device (backend, source); + } + + #ifdef HAVE_WAYLAND + if (meta_is_wayland_compositor () && event->type == CLUTTER_MOTION) + { + MetaCursorRenderer *cursor_renderer; + ClutterInputDevice *device; + + device = clutter_event_get_device (event); + cursor_renderer = meta_backend_get_cursor_renderer_for_device (backend, + device); + if (cursor_renderer) + meta_cursor_renderer_update_position (cursor_renderer); + + if (device == clutter_seat_get_pointer (clutter_input_device_get_seat (device))) + { + MetaCursorTracker *cursor_tracker = + meta_backend_get_cursor_tracker (backend); + + meta_cursor_tracker_invalidate_position (cursor_tracker); + } + } + #endif + + window = get_window_for_event (display, event); + + display->current_time = event->any.time; + +- if (window && !window->override_redirect && +- (event->type == CLUTTER_KEY_PRESS || +- event->type == CLUTTER_BUTTON_PRESS || +- event->type == CLUTTER_TOUCH_BEGIN)) ++ if (event->type == CLUTTER_KEY_PRESS || ++ event->type == CLUTTER_BUTTON_PRESS || ++ event->type == CLUTTER_TOUCH_BEGIN) + { +- if (META_CURRENT_TIME == display->current_time) ++ if (window && !window->override_redirect) + { +- /* We can't use missing (i.e. invalid) timestamps to set user time, +- * nor do we want to use them to sanity check other timestamps. +- * See bug 313490 for more details. +- */ +- meta_warning ("Event has no timestamp! You may be using a broken " +- "program such as xse. Please ask the authors of that " +- "program to fix it."); ++ if (META_CURRENT_TIME == display->current_time) ++ { ++ /* We can't use missing (i.e. invalid) timestamps to set user time, ++ * nor do we want to use them to sanity check other timestamps. ++ * See bug 313490 for more details. ++ */ ++ meta_warning ("Event has no timestamp! You may be using a broken " ++ "program such as xse. Please ask the authors of that " ++ "program to fix it."); ++ } ++ else ++ { ++ meta_window_set_user_time (window, display->current_time); ++ meta_display_sanity_check_timestamps (display, display->current_time); ++ } + } + else + { +- meta_window_set_user_time (window, display->current_time); +- meta_display_sanity_check_timestamps (display, display->current_time); ++ /* Always update user time to the last time the user did an event, even ++ * if it was to shell chrome or a notification or something. ++ */ ++ if (XSERVER_TIME_IS_BEFORE (display->last_user_time, display->current_time)) ++ display->last_user_time = display->current_time; + } + } + + gesture_tracker = meta_display_get_gesture_tracker (display); + + if (meta_gesture_tracker_handle_event (gesture_tracker, event)) + { + bypass_wayland = bypass_clutter = TRUE; + goto out; + } + + if (display->event_route == META_EVENT_ROUTE_WINDOW_OP) + { + if (meta_window_handle_mouse_grab_op_event (window, event)) + { + bypass_clutter = TRUE; + bypass_wayland = TRUE; + goto out; + } + } + + /* For key events, it's important to enforce single-handling, or + * we can get into a confused state. So if a keybinding is + * handled (because it's one of our hot-keys, or because we are + * in a keyboard-grabbed mode like moving a window, we don't + * want to pass the key event to the compositor or Wayland at all. + */ + if (meta_keybindings_process_event (display, window, event)) + { + bypass_clutter = TRUE; +-- +2.44.0 + diff --git a/0003-core-window-Split-cgroup-out-to-separate-struct.patch b/0003-core-window-Split-cgroup-out-to-separate-struct.patch new file mode 100644 index 0000000..70a52de --- /dev/null +++ b/0003-core-window-Split-cgroup-out-to-separate-struct.patch @@ -0,0 +1,816 @@ +From 0c104d85654318978d10d0fadf33ceea92e29c0a Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 13 Jun 2024 12:58:21 -0400 +Subject: [PATCH 3/6] core/window: Split cgroup out to separate struct + +We're going to need to attach other information to the cgroup, +aside from the cgroup path, so this commit separates it out +to its own struct, shared with other windows using the same +cgroup. +--- + src/core/display-private.h | 17 +++++++++ + src/core/display.c | 70 ++++++++++++++++++++++++++++++++++++++ + src/core/window-private.h | 2 ++ + src/core/window.c | 44 ++++++++++++++++++++++++ + 4 files changed, 133 insertions(+) + +diff --git a/src/core/display-private.h b/src/core/display-private.h +index 3d690fcb6..fb17d20a6 100644 +--- a/src/core/display-private.h ++++ b/src/core/display-private.h +@@ -78,98 +78,106 @@ typedef enum + META_TILE_LEFT, + META_TILE_RIGHT, + META_TILE_MAXIMIZED + } MetaTileMode; + + typedef enum + { + /* Normal interaction where you're interacting with windows. + * Events go to windows normally. */ + META_EVENT_ROUTE_NORMAL, + + /* In a window operation like moving or resizing. All events + * goes to MetaWindow, but not to the actual client window. */ + META_EVENT_ROUTE_WINDOW_OP, + + /* In a compositor grab operation. All events go to the + * compositor plugin. */ + META_EVENT_ROUTE_COMPOSITOR_GRAB, + + /* A Wayland application has a popup open. All events go to + * the Wayland application. */ + META_EVENT_ROUTE_WAYLAND_POPUP, + + /* The user is clicking on a window button. */ + META_EVENT_ROUTE_FRAME_BUTTON, + } MetaEventRoute; + + typedef void (* MetaDisplayWindowFunc) (MetaWindow *window, + gpointer user_data); + ++typedef struct _MetaCGroup MetaCGroup; ++struct _MetaCGroup { ++ GFile *path; ++ ++ grefcount ref_count; ++}; ++ + struct _MetaDisplay + { + GObject parent_instance; + + MetaX11Display *x11_display; + + int clutter_event_filter; + + /* Our best guess as to the "currently" focused window (that is, the + * window that we expect will be focused at the point when the X + * server processes our next request), and the serial of the request + * or event that caused this. + */ + MetaWindow *focus_window; + + /* last timestamp passed to XSetInputFocus */ + guint32 last_focus_time; + + /* last user interaction time in any app */ + guint32 last_user_time; + + /* whether we're using mousenav (only relevant for sloppy&mouse focus modes; + * !mouse_mode means "keynav mode") + */ + guint mouse_mode : 1; + + /* Helper var used when focus_new_windows setting is 'strict'; only + * relevant in 'strict' mode and if the focus window is a terminal. + * In that case, we don't allow new windows to take focus away from + * a terminal, but if the user explicitly did something that should + * allow a different window to gain focus (e.g. global keybinding or + * clicking on a dock), then we will allow the transfer. + */ + guint allow_terminal_deactivation : 1; + + /*< private-ish >*/ + GHashTable *stamps; + GHashTable *wayland_windows; ++ GHashTable *cgroups; + + /* serials of leave/unmap events that may + * correspond to an enter event we should + * ignore + */ + unsigned long ignored_crossing_serials[N_IGNORED_CROSSING_SERIALS]; + + guint32 current_time; + + /* We maintain a sequence counter, incremented for each #MetaWindow + * created. This is exposed by meta_window_get_stable_sequence() + * but is otherwise not used inside mutter. + * + * It can be useful to plugins which want to sort windows in a + * stable fashion. + */ + guint32 window_sequence_counter; + + /* Pings which we're waiting for a reply from */ + GSList *pending_pings; + + /* Pending focus change */ + guint focus_timeout_id; + + /* Pending autoraise */ + guint autoraise_timeout_id; + MetaWindow* autoraise_window; + + /* Event routing */ + MetaEventRoute event_route; +@@ -254,60 +262,69 @@ struct _MetaDisplayClass + * + * See the docs for meta_display_xserver_time_is_before(). + */ + #define XSERVER_TIME_IS_BEFORE(time1, time2) \ + ( (time1) == 0 || \ + (XSERVER_TIME_IS_BEFORE_ASSUMING_REAL_TIMESTAMPS(time1, time2) && \ + (time2) != 0) \ + ) + + gboolean meta_display_open (void); + + void meta_display_manage_all_xwindows (MetaDisplay *display); + void meta_display_unmanage_windows (MetaDisplay *display, + guint32 timestamp); + + /* Utility function to compare the stacking of two windows */ + int meta_display_stack_cmp (const void *a, + const void *b); + + /* Each MetaWindow is uniquely identified by a 64-bit "stamp"; unlike a + * a MetaWindow *, a stamp will never be recycled + */ + MetaWindow* meta_display_lookup_stamp (MetaDisplay *display, + guint64 stamp); + void meta_display_register_stamp (MetaDisplay *display, + guint64 *stampp, + MetaWindow *window); + void meta_display_unregister_stamp (MetaDisplay *display, + guint64 stamp); + ++void meta_display_register_cgroup (MetaDisplay *display, ++ MetaWindow *window, ++ const char *path); ++void meta_display_unregister_cgroup (MetaDisplay *display, ++ MetaWindow *window); ++ ++MetaCGroup* meta_cgroup_ref (MetaCGroup *cgroup); ++gboolean meta_cgroup_unref (MetaCGroup *cgroup); ++ + /* A "stack id" is a XID or a stamp */ + #define META_STACK_ID_IS_X11(id) ((id) < G_GUINT64_CONSTANT(0x100000000)) + + META_EXPORT_TEST + MetaWindow* meta_display_lookup_stack_id (MetaDisplay *display, + guint64 stack_id); + + /* for debug logging only; returns a human-description of the stack + * ID - a small number of buffers are recycled, so the result must + * be used immediately or copied */ + const char *meta_display_describe_stack_id (MetaDisplay *display, + guint64 stack_id); + + void meta_display_register_wayland_window (MetaDisplay *display, + MetaWindow *window); + void meta_display_unregister_wayland_window (MetaDisplay *display, + MetaWindow *window); + + void meta_display_notify_window_created (MetaDisplay *display, + MetaWindow *window); + + META_EXPORT_TEST + GSList* meta_display_list_windows (MetaDisplay *display, + MetaListWindowsFlags flags); + + MetaDisplay* meta_display_for_x_display (Display *xdisplay); + + META_EXPORT_TEST + MetaDisplay* meta_get_display (void); + +diff --git a/src/core/display.c b/src/core/display.c +index 4b58a5d2f..937defd2c 100644 +--- a/src/core/display.c ++++ b/src/core/display.c +@@ -833,60 +833,61 @@ meta_display_open (void) + + i = 0; + while (i < N_IGNORED_CROSSING_SERIALS) + { + display->ignored_crossing_serials[i] = 0; + ++i; + } + + display->current_time = META_CURRENT_TIME; + + display->grab_resize_timeout_id = 0; + display->grab_have_keyboard = FALSE; + + display->grab_op = META_GRAB_OP_NONE; + display->grab_window = NULL; + display->grab_tile_mode = META_TILE_NONE; + display->grab_tile_monitor_number = -1; + + meta_display_cleanup_edges (display); + + meta_display_init_keys (display); + + meta_prefs_add_listener (prefs_changed_callback, display); + + /* Get events */ + meta_display_init_events (display); + + display->stamps = g_hash_table_new (g_int64_hash, + g_int64_equal); + display->wayland_windows = g_hash_table_new (NULL, NULL); ++ display->cgroups = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + monitor_manager = meta_backend_get_monitor_manager (backend); + g_signal_connect (monitor_manager, "monitors-changed-internal", + G_CALLBACK (on_monitors_changed_internal), display); + + display->pad_action_mapper = meta_pad_action_mapper_new (monitor_manager); + + settings = meta_backend_get_settings (backend); + g_signal_connect (settings, "ui-scaling-factor-changed", + G_CALLBACK (on_ui_scaling_factor_changed), display); + + display->compositor = create_compositor (display); + + meta_display_set_cursor (display, META_CURSOR_DEFAULT); + + display->stack = meta_stack_new (display); + display->stack_tracker = meta_stack_tracker_new (display); + + display->workspace_manager = meta_workspace_manager_new (display); + + display->startup_notification = meta_startup_notification_new (display); + + display->bell = meta_bell_new (display); + + display->selection = meta_selection_new (display); + meta_clipboard_manager_init (display); + + #ifdef HAVE_WAYLAND + if (meta_is_wayland_compositor ()) + { +@@ -1095,60 +1096,61 @@ meta_display_close (MetaDisplay *display, + meta_prefs_remove_listener (prefs_changed_callback, display); + + meta_display_remove_autoraise_callback (display); + + g_clear_object (&display->gesture_tracker); + + g_clear_handle_id (&display->focus_timeout_id, g_source_remove); + g_clear_handle_id (&display->tile_preview_timeout_id, g_source_remove); + + if (display->work_area_later != 0) + meta_later_remove (display->work_area_later); + if (display->check_fullscreen_later != 0) + meta_later_remove (display->check_fullscreen_later); + + /* Stop caring about events */ + meta_display_free_events (display); + + g_clear_pointer (&display->compositor, meta_compositor_destroy); + + meta_display_shutdown_x11 (display); + + g_clear_object (&display->stack); + g_clear_pointer (&display->stack_tracker, + meta_stack_tracker_free); + + /* Must be after all calls to meta_window_unmanage() since they + * unregister windows + */ + g_hash_table_destroy (display->wayland_windows); + g_hash_table_destroy (display->stamps); ++ g_hash_table_destroy (display->cgroups); + + meta_display_shutdown_keys (display); + + g_clear_object (&display->bell); + g_clear_object (&display->startup_notification); + g_clear_object (&display->workspace_manager); + g_clear_object (&display->sound_player); + + meta_clipboard_manager_shutdown (display); + g_clear_object (&display->selection); + g_clear_object (&display->pad_action_mapper); + + g_object_unref (display); + the_display = NULL; + + meta_quit (META_EXIT_SUCCESS); + } + + /** + * meta_display_for_x_display: + * @xdisplay: An X display + * + * Returns the singleton MetaDisplay if @xdisplay matches the X display it's + * managing; otherwise gives a warning and returns %NULL. When we were claiming + * to be able to manage multiple displays, this was supposed to find the + * display out of the list which matched that display. Now it's merely an + * extra sanity check. + * + * Returns: The singleton X display, or %NULL if @xdisplay isn't the one + * we're managing. +@@ -1491,60 +1493,128 @@ meta_display_set_input_focus (MetaDisplay *display, + + meta_display_update_focus_window (display, window); + + display->last_focus_time = timestamp; + + if (window == NULL || window != display->autoraise_window) + meta_display_remove_autoraise_callback (display); + } + + void + meta_display_unset_input_focus (MetaDisplay *display, + guint32 timestamp) + { + meta_display_set_input_focus (display, NULL, FALSE, timestamp); + } + + void + meta_display_register_wayland_window (MetaDisplay *display, + MetaWindow *window) + { + g_hash_table_add (display->wayland_windows, window); + } + + void + meta_display_unregister_wayland_window (MetaDisplay *display, + MetaWindow *window) + { + g_hash_table_remove (display->wayland_windows, window); + } + ++MetaCGroup* ++meta_cgroup_new (const char *path) ++{ ++ MetaCGroup *cgroup; ++ ++ cgroup = g_new0 (MetaCGroup, 1); ++ cgroup->path = g_file_new_for_path (path); ++ g_ref_count_init (&cgroup->ref_count); ++ ++ return cgroup; ++} ++ ++MetaCGroup* ++meta_cgroup_ref (MetaCGroup *cgroup) ++{ ++ g_ref_count_inc (&cgroup->ref_count); ++ return cgroup; ++} ++ ++gboolean ++meta_cgroup_unref (MetaCGroup *cgroup) ++{ ++ if (!g_ref_count_dec (&cgroup->ref_count)) ++ return FALSE; ++ ++ g_clear_object (&cgroup->path); ++ g_free (cgroup); ++ ++ return TRUE; ++} ++ ++void ++meta_display_register_cgroup (MetaDisplay *display, ++ MetaWindow *window, ++ const char *path) ++{ ++ MetaCGroup *cgroup; ++ ++ cgroup = g_hash_table_lookup (display->cgroups, path); ++ ++ if (cgroup) ++ { ++ window->cgroup = meta_cgroup_ref (cgroup); ++ return; ++ } ++ ++ cgroup = meta_cgroup_new (path); ++ g_hash_table_insert (display->cgroups, g_file_get_path (cgroup->path), cgroup); ++} ++ ++void ++meta_display_unregister_cgroup (MetaDisplay *display, ++ MetaWindow *window) ++{ ++ g_autofree const char *path = NULL; ++ MetaCGroup *cgroup = g_steal_pointer (&window->cgroup); ++ ++ if (!cgroup) ++ return; ++ ++ path = g_file_get_path (cgroup->path); ++ ++ if (!meta_cgroup_unref (cgroup)) ++ return; ++ ++ g_hash_table_remove (display->cgroups, path); ++} ++ + MetaWindow* + meta_display_lookup_stamp (MetaDisplay *display, + guint64 stamp) + { + return g_hash_table_lookup (display->stamps, &stamp); + } + + void + meta_display_register_stamp (MetaDisplay *display, + guint64 *stampp, + MetaWindow *window) + { + g_return_if_fail (g_hash_table_lookup (display->stamps, stampp) == NULL); + + g_hash_table_insert (display->stamps, stampp, window); + } + + void + meta_display_unregister_stamp (MetaDisplay *display, + guint64 stamp) + { + g_return_if_fail (g_hash_table_lookup (display->stamps, &stamp) != NULL); + + g_hash_table_remove (display->stamps, &stamp); + } + + MetaWindow* + meta_display_lookup_stack_id (MetaDisplay *display, + guint64 stack_id) + { +diff --git a/src/core/window-private.h b/src/core/window-private.h +index d1730c988..8a0aebb38 100644 +--- a/src/core/window-private.h ++++ b/src/core/window-private.h +@@ -193,60 +193,62 @@ struct _MetaWindow + * binary data + */ + char *res_class; + char *res_name; + char *role; + char *sm_client_id; + char *wm_client_machine; + + char *startup_id; + char *mutter_hints; + char *sandboxed_app_id; + char *gtk_theme_variant; + char *gtk_application_id; + char *gtk_unique_bus_name; + char *gtk_application_object_path; + char *gtk_window_object_path; + char *gtk_app_menu_object_path; + char *gtk_menubar_object_path; + + Window xtransient_for; + Window xgroup_leader; + Window xclient_leader; + MetaWindow *transient_for; + + /* Initial workspace property */ + int initial_workspace; + + /* Initial timestamp property */ + guint32 initial_timestamp; + ++ MetaCGroup *cgroup; ++ + /* Whether this is an override redirect window or not */ + guint override_redirect : 1; + + /* Whether we're maximized */ + guint maximized_horizontally : 1; + guint maximized_vertically : 1; + + /* Whether we have to maximize/minimize after placement */ + guint maximize_horizontally_after_placement : 1; + guint maximize_vertically_after_placement : 1; + guint minimize_after_placement : 1; + + /* The current tile mode */ + MetaTileMode tile_mode; + + /* The last "full" maximized/unmaximized state. We need to keep track of + * that to toggle between normal/tiled or maximized/tiled states. */ + guint saved_maximize : 1; + int tile_monitor_number; + + struct { + MetaEdgeConstraint top; + MetaEdgeConstraint right; + MetaEdgeConstraint bottom; + MetaEdgeConstraint left; + } edge_constraints; + + double tile_hfraction; + + uint64_t preferred_output_winsys_id; +diff --git a/src/core/window.c b/src/core/window.c +index e787dbce0..f5ecd6438 100644 +--- a/src/core/window.c ++++ b/src/core/window.c +@@ -70,60 +70,65 @@ + #include "cogl/cogl.h" + #include "core/boxes-private.h" + #include "core/constraints.h" + #include "core/edge-resistance.h" + #include "core/frame.h" + #include "core/keybindings-private.h" + #include "core/meta-workspace-manager-private.h" + #include "core/place.h" + #include "core/stack.h" + #include "core/util-private.h" + #include "core/workspace-private.h" + #include "meta/compositor-mutter.h" + #include "meta/group.h" + #include "meta/meta-cursor-tracker.h" + #include "meta/meta-enum-types.h" + #include "meta/meta-x11-errors.h" + #include "meta/prefs.h" + #include "ui/ui.h" + #include "x11/meta-x11-display-private.h" + #include "x11/window-props.h" + #include "x11/window-x11.h" + #include "x11/xprops.h" + + #ifdef HAVE_WAYLAND + #include "wayland/meta-wayland-private.h" + #include "wayland/meta-wayland-surface.h" + #include "wayland/meta-window-wayland.h" + #include "wayland/meta-window-xwayland.h" + #endif + ++#ifdef HAVE_LIBSYSTEMD ++#include ++#endif ++ ++ + /* Windows that unmaximize to a size bigger than that fraction of the workarea + * will be scaled down to that size (while maintaining aspect ratio). + * Windows that cover an area greater then this size are automaximized on map. + */ + #define MAX_UNMAXIMIZED_WINDOW_AREA .8 + + #define SNAP_SECURITY_LABEL_PREFIX "snap." + + static int destroying_windows_disallowed = 0; + + /* Each window has a "stamp" which is a non-recycled 64-bit ID. They + * start after the end of the XID space so that, for stacking + * we can keep a guint64 that represents one or the other + */ + static guint64 next_window_stamp = G_GUINT64_CONSTANT(0x100000000); + + static void invalidate_work_areas (MetaWindow *window); + static void set_wm_state (MetaWindow *window); + static void set_net_wm_state (MetaWindow *window); + static void meta_window_set_above (MetaWindow *window, + gboolean new_value); + + static void meta_window_show (MetaWindow *window); + static void meta_window_hide (MetaWindow *window); + + static void meta_window_save_rect (MetaWindow *window); + + static void ensure_mru_position_after (MetaWindow *window, + MetaWindow *after_this_one); + +@@ -303,60 +308,63 @@ meta_window_real_get_client_pid (MetaWindow *window) + } + + static void + meta_window_finalize (GObject *object) + { + MetaWindow *window = META_WINDOW (object); + + if (window->icon) + cairo_surface_destroy (window->icon); + + if (window->mini_icon) + cairo_surface_destroy (window->mini_icon); + + if (window->frame_bounds) + cairo_region_destroy (window->frame_bounds); + + if (window->shape_region) + cairo_region_destroy (window->shape_region); + + if (window->opaque_region) + cairo_region_destroy (window->opaque_region); + + if (window->input_region) + cairo_region_destroy (window->input_region); + + if (window->transient_for) + g_object_unref (window->transient_for); + + g_free (window->sm_client_id); + g_free (window->wm_client_machine); ++ ++ meta_display_unregister_cgroup (window->display, window); ++ + g_free (window->startup_id); + g_free (window->role); + g_free (window->res_class); + g_free (window->res_name); + g_free (window->title); + g_free (window->desc); + g_free (window->sandboxed_app_id); + g_free (window->gtk_theme_variant); + g_free (window->gtk_application_id); + g_free (window->gtk_unique_bus_name); + g_free (window->gtk_application_object_path); + g_free (window->gtk_window_object_path); + g_free (window->gtk_app_menu_object_path); + g_free (window->gtk_menubar_object_path); + g_free (window->placement.rule); + + G_OBJECT_CLASS (meta_window_parent_class)->finalize (object); + } + + static void + meta_window_get_property(GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) + { + MetaWindow *win = META_WINDOW (object); + + switch (prop_id) + { + case PROP_TITLE: +@@ -1129,60 +1137,61 @@ _meta_window_shared_new (MetaDisplay *display, + window->has_minimize_func = TRUE; + window->has_maximize_func = TRUE; + window->has_move_func = TRUE; + window->has_resize_func = TRUE; + + window->has_shade_func = TRUE; + + window->has_fullscreen_func = TRUE; + + window->always_sticky = FALSE; + + window->skip_taskbar = FALSE; + window->skip_pager = FALSE; + window->skip_from_window_list = FALSE; + window->wm_state_above = FALSE; + window->wm_state_below = FALSE; + window->wm_state_demands_attention = FALSE; + + window->res_class = NULL; + window->res_name = NULL; + window->role = NULL; + window->sm_client_id = NULL; + window->wm_client_machine = NULL; + window->is_remote = FALSE; + window->startup_id = NULL; + + window->client_pid = 0; + + window->xtransient_for = None; + window->xclient_leader = None; ++ window->cgroup = NULL; + + window->type = META_WINDOW_NORMAL; + + window->struts = NULL; + + window->layer = META_LAYER_LAST; /* invalid value */ + window->stack_position = -1; + window->initial_workspace = 0; /* not used */ + window->initial_timestamp = 0; /* not used */ + + window->compositor_private = NULL; + + window->monitor = meta_window_calculate_main_logical_monitor (window); + if (window->monitor) + window->preferred_output_winsys_id = window->monitor->winsys_id; + else + window->preferred_output_winsys_id = UINT_MAX; + + window->tile_match = NULL; + + /* Assign this #MetaWindow a sequence number which can be used + * for sorting. + */ + window->stable_sequence = ++display->window_sequence_counter; + + window->opacity = 0xFF; + + if (window->override_redirect) + { + window->decorated = FALSE; +@@ -7640,60 +7649,95 @@ meta_window_get_transient_for (MetaWindow *window) + else if (window->xtransient_for) + return meta_x11_display_lookup_x_window (window->display->x11_display, + window->xtransient_for); + else + return NULL; + } + + /** + * meta_window_get_pid: + * @window: a #MetaWindow + * + * Returns the pid of the process that created this window, if available + * to the windowing system. + * + * Note that the value returned by this is vulnerable to spoofing attacks + * by the client. + * + * Return value: the pid, or 0 if not known. + */ + pid_t + meta_window_get_pid (MetaWindow *window) + { + g_return_val_if_fail (META_IS_WINDOW (window), 0); + + if (window->client_pid == 0) + window->client_pid = META_WINDOW_GET_CLASS (window)->get_client_pid (window); + + return window->client_pid; + } + ++static void ++meta_window_read_cgroup (MetaWindow *window) ++{ ++#ifdef HAVE_LIBSYSTEMD ++ g_autofree char *contents = NULL; ++ g_autofree char *complete_path = NULL; ++ g_autofree char *unit_name = NULL; ++ g_autofree char *unit_path = NULL; ++ char *unit_end; ++ pid_t pid; ++ ++ if (window->cgroup) ++ return; ++ ++ pid = meta_window_get_pid (window); ++ if (pid < 1) ++ return; ++ ++ if (sd_pid_get_cgroup (pid, &contents) < 0) ++ return; ++ g_strstrip (contents); ++ ++ complete_path = g_strdup_printf ("%s%s", "/sys/fs/cgroup", contents); ++ ++ if (sd_pid_get_user_unit (pid, &unit_name) < 0) ++ return; ++ g_strstrip (unit_name); ++ ++ unit_end = strstr (complete_path, unit_name) + strlen (unit_name); ++ *unit_end = '\0'; ++ ++ meta_display_register_cgroup (window->display, window, complete_path); ++#endif ++} ++ + /** + * meta_window_get_client_machine: + * @window: a #MetaWindow + * + * Returns name of the client machine from which this windows was created, + * if known (obtained from the WM_CLIENT_MACHINE property). + * + * Return value: (transfer none): the machine name, or NULL; the string is + * owned by the window manager and should not be freed or modified by the + * caller. + */ + const char * + meta_window_get_client_machine (MetaWindow *window) + { + g_return_val_if_fail (META_IS_WINDOW (window), NULL); + + return window->wm_client_machine; + } + + /** + * meta_window_is_remote: + * @window: a #MetaWindow + * + * Returns: %TRUE if this window originates from a host + * different from the one running mutter. + */ + gboolean + meta_window_is_remote (MetaWindow *window) + { + return window->is_remote; +-- +2.44.0 + diff --git a/0004-window-Track-workspace-per-cgroup.patch b/0004-window-Track-workspace-per-cgroup.patch new file mode 100644 index 0000000..684b2b2 --- /dev/null +++ b/0004-window-Track-workspace-per-cgroup.patch @@ -0,0 +1,685 @@ +From 9befece38b28581958d87d9393ff9c788213d2fb Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 13 Jun 2024 13:47:01 -0400 +Subject: [PATCH 4/6] window: Track workspace per-cgroup + +If an application doesn't tell us what workspace to start on we +need to guess. Currently our guess is "currently active workspace", +but that's not really always the best choice. + +This commit adds code to track the workspace other windows in the +same cgroup (~application) are running on and uses that to decide +which workspace to start on instead. +--- + src/core/display-private.h | 6 ++++++ + src/core/display.c | 31 +++++++++++++++++++++++++++++++ + src/core/window-private.h | 1 + + src/core/window.c | 36 ++++++++++++++++++++++++++++++++---- + 4 files changed, 70 insertions(+), 4 deletions(-) + +diff --git a/src/core/display-private.h b/src/core/display-private.h +index fb17d20a6..2b8d9e0d0 100644 +--- a/src/core/display-private.h ++++ b/src/core/display-private.h +@@ -81,60 +81,63 @@ typedef enum + } MetaTileMode; + + typedef enum + { + /* Normal interaction where you're interacting with windows. + * Events go to windows normally. */ + META_EVENT_ROUTE_NORMAL, + + /* In a window operation like moving or resizing. All events + * goes to MetaWindow, but not to the actual client window. */ + META_EVENT_ROUTE_WINDOW_OP, + + /* In a compositor grab operation. All events go to the + * compositor plugin. */ + META_EVENT_ROUTE_COMPOSITOR_GRAB, + + /* A Wayland application has a popup open. All events go to + * the Wayland application. */ + META_EVENT_ROUTE_WAYLAND_POPUP, + + /* The user is clicking on a window button. */ + META_EVENT_ROUTE_FRAME_BUTTON, + } MetaEventRoute; + + typedef void (* MetaDisplayWindowFunc) (MetaWindow *window, + gpointer user_data); + + typedef struct _MetaCGroup MetaCGroup; + struct _MetaCGroup { + GFile *path; ++ guint32 user_time; ++ MetaWorkspace *last_active_workspace; ++ gboolean has_startup_sequence; + + grefcount ref_count; + }; + + struct _MetaDisplay + { + GObject parent_instance; + + MetaX11Display *x11_display; + + int clutter_event_filter; + + /* Our best guess as to the "currently" focused window (that is, the + * window that we expect will be focused at the point when the X + * server processes our next request), and the serial of the request + * or event that caused this. + */ + MetaWindow *focus_window; + + /* last timestamp passed to XSetInputFocus */ + guint32 last_focus_time; + + /* last user interaction time in any app */ + guint32 last_user_time; + + /* whether we're using mousenav (only relevant for sloppy&mouse focus modes; + * !mouse_mode means "keynav mode") + */ + guint mouse_mode : 1; + +@@ -270,60 +273,63 @@ struct _MetaDisplayClass + + gboolean meta_display_open (void); + + void meta_display_manage_all_xwindows (MetaDisplay *display); + void meta_display_unmanage_windows (MetaDisplay *display, + guint32 timestamp); + + /* Utility function to compare the stacking of two windows */ + int meta_display_stack_cmp (const void *a, + const void *b); + + /* Each MetaWindow is uniquely identified by a 64-bit "stamp"; unlike a + * a MetaWindow *, a stamp will never be recycled + */ + MetaWindow* meta_display_lookup_stamp (MetaDisplay *display, + guint64 stamp); + void meta_display_register_stamp (MetaDisplay *display, + guint64 *stampp, + MetaWindow *window); + void meta_display_unregister_stamp (MetaDisplay *display, + guint64 stamp); + + void meta_display_register_cgroup (MetaDisplay *display, + MetaWindow *window, + const char *path); + void meta_display_unregister_cgroup (MetaDisplay *display, + MetaWindow *window); + + MetaCGroup* meta_cgroup_ref (MetaCGroup *cgroup); + gboolean meta_cgroup_unref (MetaCGroup *cgroup); ++void meta_cgroup_update_workspace (MetaCGroup *cgroup, ++ MetaWorkspace *workspace, ++ guint32 timestamp); + + /* A "stack id" is a XID or a stamp */ + #define META_STACK_ID_IS_X11(id) ((id) < G_GUINT64_CONSTANT(0x100000000)) + + META_EXPORT_TEST + MetaWindow* meta_display_lookup_stack_id (MetaDisplay *display, + guint64 stack_id); + + /* for debug logging only; returns a human-description of the stack + * ID - a small number of buffers are recycled, so the result must + * be used immediately or copied */ + const char *meta_display_describe_stack_id (MetaDisplay *display, + guint64 stack_id); + + void meta_display_register_wayland_window (MetaDisplay *display, + MetaWindow *window); + void meta_display_unregister_wayland_window (MetaDisplay *display, + MetaWindow *window); + + void meta_display_notify_window_created (MetaDisplay *display, + MetaWindow *window); + + META_EXPORT_TEST + GSList* meta_display_list_windows (MetaDisplay *display, + MetaListWindowsFlags flags); + + MetaDisplay* meta_display_for_x_display (Display *xdisplay); + + META_EXPORT_TEST + MetaDisplay* meta_get_display (void); +diff --git a/src/core/display.c b/src/core/display.c +index 937defd2c..f30f9a268 100644 +--- a/src/core/display.c ++++ b/src/core/display.c +@@ -1524,94 +1524,121 @@ MetaCGroup* + meta_cgroup_new (const char *path) + { + MetaCGroup *cgroup; + + cgroup = g_new0 (MetaCGroup, 1); + cgroup->path = g_file_new_for_path (path); + g_ref_count_init (&cgroup->ref_count); + + return cgroup; + } + + MetaCGroup* + meta_cgroup_ref (MetaCGroup *cgroup) + { + g_ref_count_inc (&cgroup->ref_count); + return cgroup; + } + + gboolean + meta_cgroup_unref (MetaCGroup *cgroup) + { + if (!g_ref_count_dec (&cgroup->ref_count)) + return FALSE; + + g_clear_object (&cgroup->path); + g_free (cgroup); + + return TRUE; + } + ++void ++meta_cgroup_update_workspace (MetaCGroup *cgroup, ++ MetaWorkspace *workspace, ++ guint32 timestamp) ++{ ++ if (!cgroup) ++ return; ++ ++ if (!XSERVER_TIME_IS_BEFORE (cgroup->user_time, timestamp)) ++ return; ++ ++ cgroup->user_time = timestamp; ++ ++ if (cgroup->last_active_workspace) ++ g_object_remove_weak_pointer (G_OBJECT (cgroup->last_active_workspace), ++ (gpointer *) &cgroup->last_active_workspace); ++ ++ cgroup->last_active_workspace = workspace; ++ ++ g_object_add_weak_pointer (G_OBJECT (workspace), ++ (gpointer *) &cgroup->last_active_workspace); ++} ++ + void + meta_display_register_cgroup (MetaDisplay *display, + MetaWindow *window, + const char *path) + { + MetaCGroup *cgroup; + + cgroup = g_hash_table_lookup (display->cgroups, path); + + if (cgroup) + { + window->cgroup = meta_cgroup_ref (cgroup); + return; + } + + cgroup = meta_cgroup_new (path); + g_hash_table_insert (display->cgroups, g_file_get_path (cgroup->path), cgroup); + } + + void + meta_display_unregister_cgroup (MetaDisplay *display, + MetaWindow *window) + { + g_autofree const char *path = NULL; + MetaCGroup *cgroup = g_steal_pointer (&window->cgroup); + + if (!cgroup) + return; + + path = g_file_get_path (cgroup->path); + + if (!meta_cgroup_unref (cgroup)) + return; + ++ if (cgroup->last_active_workspace) ++ g_object_remove_weak_pointer (G_OBJECT (cgroup->last_active_workspace), ++ (gpointer *) &cgroup->last_active_workspace); ++ + g_hash_table_remove (display->cgroups, path); + } + + MetaWindow* + meta_display_lookup_stamp (MetaDisplay *display, + guint64 stamp) + { + return g_hash_table_lookup (display->stamps, &stamp); + } + + void + meta_display_register_stamp (MetaDisplay *display, + guint64 *stampp, + MetaWindow *window) + { + g_return_if_fail (g_hash_table_lookup (display->stamps, stampp) == NULL); + + g_hash_table_insert (display->stamps, stampp, window); + } + + void + meta_display_unregister_stamp (MetaDisplay *display, + guint64 stamp) + { + g_return_if_fail (g_hash_table_lookup (display->stamps, &stamp) != NULL); + + g_hash_table_remove (display->stamps, &stamp); + } + + MetaWindow* +@@ -3374,60 +3401,64 @@ meta_display_apply_startup_properties (MetaDisplay *display, + meta_startup_sequence_get_id (sequence), + window->desc); + + meta_startup_sequence_complete (sequence); + } + } + + /* Still no startup ID? Bail. */ + if (!startup_id) + return FALSE; + + /* We might get this far and not know the sequence ID (if the window + * already had a startup ID stored), so let's look for one if we don't + * already know it. + */ + if (sequence == NULL) + { + sequence = + meta_startup_notification_lookup_sequence (display->startup_notification, + startup_id); + } + + if (sequence != NULL) + { + gboolean changed_something = FALSE; + + meta_topic (META_DEBUG_STARTUP, + "Found startup sequence for window %s ID \"%s\"", + window->desc, startup_id); + ++ meta_window_read_cgroup (window); ++ if (window->cgroup) ++ window->cgroup->has_startup_sequence = TRUE; ++ + if (!window->initial_workspace_set) + { + int space = meta_startup_sequence_get_workspace (sequence); + if (space >= 0) + { + meta_topic (META_DEBUG_STARTUP, + "Setting initial window workspace to %d based on startup info", + space); + + window->initial_workspace_set = TRUE; + window->initial_workspace = space; + changed_something = TRUE; + } + } + + if (!window->initial_timestamp_set) + { + guint32 timestamp = meta_startup_sequence_get_timestamp (sequence); + meta_topic (META_DEBUG_STARTUP, + "Setting initial window timestamp to %u based on startup info", + timestamp); + + window->initial_timestamp_set = TRUE; + window->initial_timestamp = timestamp; + changed_something = TRUE; + } + + return changed_something; + } + else +diff --git a/src/core/window-private.h b/src/core/window-private.h +index 8a0aebb38..41782aa99 100644 +--- a/src/core/window-private.h ++++ b/src/core/window-private.h +@@ -860,31 +860,32 @@ void meta_window_update_resize (MetaWindow *window, + MetaEdgeResistanceFlags flags, + int x, int y, + gboolean force); + + void meta_window_move_resize_internal (MetaWindow *window, + MetaMoveResizeFlags flags, + MetaGravity gravity, + MetaRectangle frame_rect); + + void meta_window_grab_op_began (MetaWindow *window, MetaGrabOp op); + void meta_window_grab_op_ended (MetaWindow *window, MetaGrabOp op); + + void meta_window_set_alive (MetaWindow *window, gboolean is_alive); + + gboolean meta_window_has_pointer (MetaWindow *window); + + void meta_window_emit_size_changed (MetaWindow *window); + + MetaPlacementRule *meta_window_get_placement_rule (MetaWindow *window); + + void meta_window_force_placement (MetaWindow *window, + gboolean force_move); + + void meta_window_force_restore_shortcuts (MetaWindow *window, + ClutterInputDevice *source); + + gboolean meta_window_shortcuts_inhibited (MetaWindow *window, + ClutterInputDevice *source); + gboolean meta_window_is_stackable (MetaWindow *window); + gboolean meta_window_is_focus_async (MetaWindow *window); ++void meta_window_read_cgroup (MetaWindow *window); + #endif +diff --git a/src/core/window.c b/src/core/window.c +index f5ecd6438..5c7b2e8cf 100644 +--- a/src/core/window.c ++++ b/src/core/window.c +@@ -1296,79 +1296,88 @@ _meta_window_shared_new (MetaDisplay *display, + * added to all the MRU lists + */ + window->on_all_workspaces_requested = TRUE; + + on_all_workspaces = TRUE; + } + else if (!on_all_workspaces) + { + meta_topic (META_DEBUG_PLACEMENT, + "Window %s is initially on space %d", + window->desc, window->initial_workspace); + + workspace = meta_workspace_manager_get_workspace_by_index (workspace_manager, + window->initial_workspace); + } + + /* Ignore when a window requests to be placed on a non-existent workspace + */ + if (on_all_workspaces || workspace != NULL) + set_workspace_state (window, on_all_workspaces, workspace); + } + + /* override-redirect windows are subtly different from other windows + * with window->on_all_workspaces == TRUE. Other windows are part of + * some workspace (so they can return to that if the flag is turned off), + * but appear on other workspaces. override-redirect windows are part + * of no workspace. + */ + if (!window->override_redirect && window->workspace == NULL) + { ++ meta_window_read_cgroup (window); + if (window->transient_for != NULL) + { + meta_topic (META_DEBUG_PLACEMENT, + "Putting window %s on same workspace as parent %s", + window->desc, window->transient_for->desc); + + g_warn_if_fail (!window->transient_for->override_redirect); + set_workspace_state (window, + window->transient_for->on_all_workspaces, + window->transient_for->workspace); + } + else if (window->on_all_workspaces) + { + meta_topic (META_DEBUG_PLACEMENT, + "Putting window %s on all workspaces", + window->desc); + + set_workspace_state (window, TRUE, NULL); + } ++ else if (window->cgroup && window->cgroup->last_active_workspace != NULL && ++ !window->cgroup->has_startup_sequence) ++ { ++ meta_topic (META_DEBUG_PLACEMENT, ++ "Putting window %s on active workspace", ++ window->desc); ++ set_workspace_state (window, FALSE, window->cgroup->last_active_workspace); ++ } + else + { + meta_topic (META_DEBUG_PLACEMENT, + "Putting window %s on active workspace", + window->desc); + + set_workspace_state (window, FALSE, workspace_manager->active_workspace); + } + + meta_window_update_struts (window); + } + + meta_window_main_monitor_changed (window, NULL); + + /* Must add window to stack before doing move/resize, since the + * window might have fullscreen size (i.e. should have been + * fullscreen'd; acrobat is one such braindead case; it withdraws + * and remaps its window whenever trying to become fullscreen...) + * and thus constraints may try to auto-fullscreen it which also + * means restacking it. + */ + if (meta_window_is_stackable (window)) + meta_stack_add (window->display->stack, + window); + else if (window->override_redirect) + window->layer = META_LAYER_OVERRIDE_REDIRECT; /* otherwise set by MetaStack */ + + if (!window->override_redirect) + { + /* FIXME we have a tendency to set this then immediately +@@ -3736,63 +3745,71 @@ meta_window_activate_full (MetaWindow *window, + "by client type %u.", + window->desc, timestamp, source_indication); + + if (window->display->last_user_time == timestamp) + { + /* Only allow workspace switches if this activation message uses the same + * timestamp as the last user interaction + */ + allow_workspace_switch = TRUE; + } + + if (timestamp != 0 && + XSERVER_TIME_IS_BEFORE (timestamp, window->display->last_user_time)) + { + meta_topic (META_DEBUG_FOCUS, + "last_user_time (%u) is more recent; ignoring " + " _NET_ACTIVE_WINDOW message.", + window->display->last_user_time); + meta_window_set_demands_attention(window); + return; + } + + if (timestamp == 0) + timestamp = meta_display_get_current_time_roundtrip (window->display); + + meta_window_set_user_time (window, timestamp); + + /* disable show desktop mode unless we're a desktop component */ + maybe_leave_show_desktop_mode (window); + +- /* Get window on current or given workspace */ +- if (workspace == NULL) +- workspace = workspace_manager->active_workspace; ++ /* Get window on last active, current, or given workspace */ ++ if (workspace == NULL) ++ { ++ meta_window_read_cgroup (window); ++ if (window->cgroup && ++ window->cgroup->last_active_workspace != NULL && ++ !window->cgroup->has_startup_sequence) ++ workspace = window->cgroup->last_active_workspace; ++ else ++ workspace = workspace_manager->active_workspace; ++ } + + /* For non-transient windows, we just set up a pulsing indicator, + rather than move windows or workspaces. + See http://bugzilla.gnome.org/show_bug.cgi?id=482354 */ + if (window->transient_for == NULL && + !allow_workspace_switch && + !meta_window_located_on_workspace (window, workspace)) + { + meta_window_set_demands_attention (window); + /* We've marked it as demanding, don't need to do anything else. */ + return; + } + else if (window->transient_for != NULL) + { + /* Move transients to current workspace - preference dialogs should appear over + the source window. */ + meta_window_change_workspace (window, workspace); + } + + if (window->shaded) + meta_window_unshade (window, timestamp); + + unminimize_window_and_all_transient_parents (window); + + if (meta_prefs_get_raise_on_click () || + source_indication == META_CLIENT_TYPE_PAGER) + meta_window_raise (window); + + meta_topic (META_DEBUG_FOCUS, + "Focusing window %s due to activation", +@@ -7165,60 +7182,71 @@ meta_window_set_user_time (MetaWindow *window, + g_return_if_fail (!window->override_redirect); + + /* Only update the time if this timestamp is newer... */ + if (window->net_wm_user_time_set && + XSERVER_TIME_IS_BEFORE (timestamp, window->net_wm_user_time)) + { + meta_topic (META_DEBUG_STARTUP, + "Window %s _NET_WM_USER_TIME not updated to %u, because it " + "is less than %u", + window->desc, timestamp, window->net_wm_user_time); + } + else + { + meta_topic (META_DEBUG_STARTUP, + "Window %s has _NET_WM_USER_TIME of %u", + window->desc, timestamp); + window->net_wm_user_time_set = TRUE; + window->net_wm_user_time = timestamp; + if (XSERVER_TIME_IS_BEFORE (window->display->last_user_time, timestamp)) + window->display->last_user_time = timestamp; + + /* If this is a terminal, user interaction with it means the user likely + * doesn't want to have focus transferred for now due to new windows. + */ + if (meta_prefs_get_focus_new_windows () == G_DESKTOP_FOCUS_NEW_WINDOWS_STRICT && + window_is_terminal (window)) + window->display->allow_terminal_deactivation = FALSE; + + g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_USER_TIME]); + } ++ ++ if (!window->cgroup) ++ meta_window_read_cgroup (window); ++ ++ if (window->cgroup) ++ { ++ MetaWorkspace *workspace = meta_window_get_workspace (window); ++ ++ if (workspace) ++ meta_cgroup_update_workspace (window->cgroup, workspace, timestamp); ++ } + } + + /** + * meta_window_get_stable_sequence: + * @window: A #MetaWindow + * + * The stable sequence number is a monotonicially increasing + * unique integer assigned to each #MetaWindow upon creation. + * + * This number can be useful for sorting windows in a stable + * fashion. + * + * Returns: Internal sequence number for this window + */ + guint32 + meta_window_get_stable_sequence (MetaWindow *window) + { + g_return_val_if_fail (META_IS_WINDOW (window), 0); + + return window->stable_sequence; + } + + /* Sets the demands_attention hint on a window, but only + * if it's at least partially obscured (see #305882). + */ + void + meta_window_set_demands_attention (MetaWindow *window) + { + MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; + MetaRectangle candidate_rect, other_rect; +@@ -7649,61 +7677,61 @@ meta_window_get_transient_for (MetaWindow *window) + else if (window->xtransient_for) + return meta_x11_display_lookup_x_window (window->display->x11_display, + window->xtransient_for); + else + return NULL; + } + + /** + * meta_window_get_pid: + * @window: a #MetaWindow + * + * Returns the pid of the process that created this window, if available + * to the windowing system. + * + * Note that the value returned by this is vulnerable to spoofing attacks + * by the client. + * + * Return value: the pid, or 0 if not known. + */ + pid_t + meta_window_get_pid (MetaWindow *window) + { + g_return_val_if_fail (META_IS_WINDOW (window), 0); + + if (window->client_pid == 0) + window->client_pid = META_WINDOW_GET_CLASS (window)->get_client_pid (window); + + return window->client_pid; + } + +-static void ++void + meta_window_read_cgroup (MetaWindow *window) + { + #ifdef HAVE_LIBSYSTEMD + g_autofree char *contents = NULL; + g_autofree char *complete_path = NULL; + g_autofree char *unit_name = NULL; + g_autofree char *unit_path = NULL; + char *unit_end; + pid_t pid; + + if (window->cgroup) + return; + + pid = meta_window_get_pid (window); + if (pid < 1) + return; + + if (sd_pid_get_cgroup (pid, &contents) < 0) + return; + g_strstrip (contents); + + complete_path = g_strdup_printf ("%s%s", "/sys/fs/cgroup", contents); + + if (sd_pid_get_user_unit (pid, &unit_name) < 0) + return; + g_strstrip (unit_name); + + unit_end = strstr (complete_path, unit_name) + strlen (unit_name); + *unit_end = '\0'; + +-- +2.44.0 + diff --git a/0005-core-display-Avoid-placement-heuristcs-for-apps-that.patch b/0005-core-display-Avoid-placement-heuristcs-for-apps-that.patch new file mode 100644 index 0000000..91ab085 --- /dev/null +++ b/0005-core-display-Avoid-placement-heuristcs-for-apps-that.patch @@ -0,0 +1,556 @@ +From 62e4f7153738b404b05b1ee59d088bc52fd86bc0 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Mon, 8 Jul 2024 09:54:01 -0400 +Subject: [PATCH 5/6] core/display: Avoid placement heuristcs for apps that can + do startup notification + +We recently introduced heuristics to decide which workspace to put an an +application window on. Those heuristics are only used if an application +doesn't support startup notification (since startup notification +provides a means to give that information without heuristics). + +Unfortunately, sometimes applications that support startup notification +aren't started with it. In those cases, it's wrong to fall back to +heuristics, because those heuristics are a big change in behavior, and +cause applications to break (like terminals launched from the desktop +starting on the wrong workspace) + +This commit adds code to try to deduce the application from its cgroup, +and then check if it supports startup notification, even if it's not +started with it in any given instance. +--- + src/core/display-private.h | 1 + + src/core/display.c | 113 +++++++++++++++++++++++++++++++++++++ + src/core/window.c | 10 +++- + 3 files changed, 122 insertions(+), 2 deletions(-) + +diff --git a/src/core/display-private.h b/src/core/display-private.h +index 2b8d9e0d0..3c7e0898b 100644 +--- a/src/core/display-private.h ++++ b/src/core/display-private.h +@@ -81,60 +81,61 @@ typedef enum + } MetaTileMode; + + typedef enum + { + /* Normal interaction where you're interacting with windows. + * Events go to windows normally. */ + META_EVENT_ROUTE_NORMAL, + + /* In a window operation like moving or resizing. All events + * goes to MetaWindow, but not to the actual client window. */ + META_EVENT_ROUTE_WINDOW_OP, + + /* In a compositor grab operation. All events go to the + * compositor plugin. */ + META_EVENT_ROUTE_COMPOSITOR_GRAB, + + /* A Wayland application has a popup open. All events go to + * the Wayland application. */ + META_EVENT_ROUTE_WAYLAND_POPUP, + + /* The user is clicking on a window button. */ + META_EVENT_ROUTE_FRAME_BUTTON, + } MetaEventRoute; + + typedef void (* MetaDisplayWindowFunc) (MetaWindow *window, + gpointer user_data); + + typedef struct _MetaCGroup MetaCGroup; + struct _MetaCGroup { + GFile *path; ++ GAppInfo *app_info; + guint32 user_time; + MetaWorkspace *last_active_workspace; + gboolean has_startup_sequence; + + grefcount ref_count; + }; + + struct _MetaDisplay + { + GObject parent_instance; + + MetaX11Display *x11_display; + + int clutter_event_filter; + + /* Our best guess as to the "currently" focused window (that is, the + * window that we expect will be focused at the point when the X + * server processes our next request), and the serial of the request + * or event that caused this. + */ + MetaWindow *focus_window; + + /* last timestamp passed to XSetInputFocus */ + guint32 last_focus_time; + + /* last user interaction time in any app */ + guint32 last_user_time; + + /* whether we're using mousenav (only relevant for sloppy&mouse focus modes; + * !mouse_mode means "keynav mode") +diff --git a/src/core/display.c b/src/core/display.c +index f30f9a268..4c9038e62 100644 +--- a/src/core/display.c ++++ b/src/core/display.c +@@ -65,60 +65,62 @@ + #include "core/meta-clipboard-manager.h" + #include "core/meta-workspace-manager-private.h" + #include "core/util-private.h" + #include "core/window-private.h" + #include "core/workspace-private.h" + #include "meta/compositor-mutter.h" + #include "meta/compositor.h" + #include "meta/main.h" + #include "meta/meta-backend.h" + #include "meta/meta-enum-types.h" + #include "meta/meta-sound-player.h" + #include "meta/meta-x11-errors.h" + #include "meta/prefs.h" + #include "x11/meta-startup-notification-x11.h" + #include "x11/meta-x11-display-private.h" + #include "x11/window-x11.h" + #include "x11/xprops.h" + + #ifdef HAVE_WAYLAND + #include "compositor/meta-compositor-native.h" + #include "compositor/meta-compositor-server.h" + #include "wayland/meta-xwayland-private.h" + #include "wayland/meta-wayland-tablet-seat.h" + #include "wayland/meta-wayland-tablet-pad.h" + #endif + + #ifdef HAVE_NATIVE_BACKEND + #include "backends/native/meta-backend-native.h" + #endif + ++#include ++ + /* + * SECTION:pings + * + * Sometimes we want to see whether a window is responding, + * so we send it a "ping" message and see whether it sends us back a "pong" + * message within a reasonable time. Here we have a system which lets us + * nominate one function to be called if we get the pong in time and another + * function if we don't. The system is rather more complicated than it needs + * to be, since we only ever use it to destroy windows which are asked to + * close themselves and don't do so within a reasonable amount of time, and + * therefore we always use the same callbacks. It's possible that we might + * use it for other things in future, or on the other hand we might decide + * that we're never going to do so and simplify it a bit. + */ + + /** + * MetaPingData: + * + * Describes a ping on a window. When we send a ping to a window, we build + * one of these structs, and it eventually gets passed to the timeout function + * or to the function which handles the response from the window. If the window + * does or doesn't respond to the ping, we use this information to deal with + * these facts; we have a handler function for each. + */ + typedef struct + { + MetaWindow *window; + guint32 serial; + guint ping_timeout_id; + } MetaPingData; +@@ -1493,85 +1495,196 @@ meta_display_set_input_focus (MetaDisplay *display, + + meta_display_update_focus_window (display, window); + + display->last_focus_time = timestamp; + + if (window == NULL || window != display->autoraise_window) + meta_display_remove_autoraise_callback (display); + } + + void + meta_display_unset_input_focus (MetaDisplay *display, + guint32 timestamp) + { + meta_display_set_input_focus (display, NULL, FALSE, timestamp); + } + + void + meta_display_register_wayland_window (MetaDisplay *display, + MetaWindow *window) + { + g_hash_table_add (display->wayland_windows, window); + } + + void + meta_display_unregister_wayland_window (MetaDisplay *display, + MetaWindow *window) + { + g_hash_table_remove (display->wayland_windows, window); + } + ++static void ++unescape_app_id (char **app_id) ++{ ++ char *p = *app_id; ++ char *q = *app_id; ++ ++ while (*p != '\0') ++ { ++ if (*p == '\\' && ++ p[1] == 'x' && ++ g_ascii_isxdigit (p[2]) && ++ g_ascii_isxdigit (p[3])) ++ { ++ char escape_code[3] = { p[2], p[3], '\0' }; ++ *q = (char) g_ascii_strtoll (escape_code, NULL, 16); ++ p += strlen ("\\xAA"); ++ } ++ else ++ { ++ *q = *p; ++ p++; ++ } ++ q++; ++ } ++ *q = '\0'; ++} ++ ++/* The possible formats are: ++ * ++ * /sys/fs/cgroup/user.slice/user-1000.slice/user@1000.service/app.slice/app-dbus\x2d:1.2\x2dorg.gnome.Totem.slice/dbus-:1.2-org.gnome.Totem@0.service ++ * /sys/fs/cgroup/user.slice/user-1000.slice/user@1000.service/app.slice/app-org.gnome.Terminal.slice/gnome-terminal-server.service ++ * /sys/fs/cgroup/user.slice/user-1000.slice/user@1000.service/app.slice/app-gnome-org.gnome.Evince-12345.scope ++ */ ++static char * ++extract_app_id_from_cgroup (const char *cgroup) ++{ ++ g_auto (GStrv) path_components = NULL; ++ const char *unit_name = NULL; ++ size_t i; ++ g_autofree char *app_id = NULL; ++ char *start_delimiter = NULL; ++ char *end_delimiter = NULL; ++ int app_id_length; ++ ++ path_components = g_strsplit (cgroup, G_DIR_SEPARATOR_S, -1); ++ ++ for (i = 0; path_components[i]; i++) ++ { ++ if (!g_str_equal (path_components[i], "app.slice")) ++ continue; ++ ++ unit_name = path_components[i + 1]; ++ break; ++ } ++ ++ if (!unit_name) ++ return NULL; ++ ++ if (!g_str_has_prefix (unit_name, "app-")) ++ return NULL; ++ ++ end_delimiter = g_strrstr (unit_name, ".slice"); ++ ++ if (!end_delimiter) ++ end_delimiter = strrchr (unit_name, '-'); ++ ++ if (end_delimiter == NULL || end_delimiter == unit_name) ++ return NULL; ++ ++ start_delimiter = end_delimiter - 1; ++ while (start_delimiter > cgroup && *start_delimiter != '-') ++ start_delimiter--; ++ ++ if (start_delimiter == NULL || start_delimiter == unit_name) ++ return NULL; ++ ++ app_id_length = end_delimiter - (start_delimiter + 1); ++ app_id = g_strdup_printf ("%.*s.desktop", app_id_length, start_delimiter + 1); ++ ++ unescape_app_id (&app_id); ++ ++ if (g_str_has_prefix (app_id, "dbus-")) ++ { ++ const char *dbus_prefix; ++ dbus_prefix = strchr (app_id + strlen ("dbus-") + 1, '-'); ++ ++ if (dbus_prefix) ++ { ++ char *stripped_app_id = strdup (dbus_prefix + 1); ++ g_clear_pointer (&app_id, g_free); ++ app_id = g_steal_pointer (&stripped_app_id); ++ } ++ } ++ ++ return g_steal_pointer (&app_id); ++} ++ + MetaCGroup* + meta_cgroup_new (const char *path) + { + MetaCGroup *cgroup; ++ g_autofree char *app_id = NULL; + + cgroup = g_new0 (MetaCGroup, 1); + cgroup->path = g_file_new_for_path (path); + g_ref_count_init (&cgroup->ref_count); + ++ app_id = extract_app_id_from_cgroup (path); ++ ++ if (app_id) ++ { ++ g_autoptr (GDesktopAppInfo) app_info = NULL; ++ ++ app_info = g_desktop_app_info_new (app_id); ++ ++ if (app_info) ++ cgroup->app_info = G_APP_INFO (g_steal_pointer (&app_info)); ++ } ++ + return cgroup; + } + + MetaCGroup* + meta_cgroup_ref (MetaCGroup *cgroup) + { + g_ref_count_inc (&cgroup->ref_count); + return cgroup; + } + + gboolean + meta_cgroup_unref (MetaCGroup *cgroup) + { + if (!g_ref_count_dec (&cgroup->ref_count)) + return FALSE; + ++ g_clear_object (&cgroup->app_info); + g_clear_object (&cgroup->path); + g_free (cgroup); + + return TRUE; + } + + void + meta_cgroup_update_workspace (MetaCGroup *cgroup, + MetaWorkspace *workspace, + guint32 timestamp) + { + if (!cgroup) + return; + + if (!XSERVER_TIME_IS_BEFORE (cgroup->user_time, timestamp)) + return; + + cgroup->user_time = timestamp; + + if (cgroup->last_active_workspace) + g_object_remove_weak_pointer (G_OBJECT (cgroup->last_active_workspace), + (gpointer *) &cgroup->last_active_workspace); + + cgroup->last_active_workspace = workspace; + + g_object_add_weak_pointer (G_OBJECT (workspace), + (gpointer *) &cgroup->last_active_workspace); + } + + void +diff --git a/src/core/window.c b/src/core/window.c +index 5c7b2e8cf..d36e45992 100644 +--- a/src/core/window.c ++++ b/src/core/window.c +@@ -74,60 +74,62 @@ + #include "core/frame.h" + #include "core/keybindings-private.h" + #include "core/meta-workspace-manager-private.h" + #include "core/place.h" + #include "core/stack.h" + #include "core/util-private.h" + #include "core/workspace-private.h" + #include "meta/compositor-mutter.h" + #include "meta/group.h" + #include "meta/meta-cursor-tracker.h" + #include "meta/meta-enum-types.h" + #include "meta/meta-x11-errors.h" + #include "meta/prefs.h" + #include "ui/ui.h" + #include "x11/meta-x11-display-private.h" + #include "x11/window-props.h" + #include "x11/window-x11.h" + #include "x11/xprops.h" + + #ifdef HAVE_WAYLAND + #include "wayland/meta-wayland-private.h" + #include "wayland/meta-wayland-surface.h" + #include "wayland/meta-window-wayland.h" + #include "wayland/meta-window-xwayland.h" + #endif + + #ifdef HAVE_LIBSYSTEMD + #include + #endif + ++#include ++ + + /* Windows that unmaximize to a size bigger than that fraction of the workarea + * will be scaled down to that size (while maintaining aspect ratio). + * Windows that cover an area greater then this size are automaximized on map. + */ + #define MAX_UNMAXIMIZED_WINDOW_AREA .8 + + #define SNAP_SECURITY_LABEL_PREFIX "snap." + + static int destroying_windows_disallowed = 0; + + /* Each window has a "stamp" which is a non-recycled 64-bit ID. They + * start after the end of the XID space so that, for stacking + * we can keep a guint64 that represents one or the other + */ + static guint64 next_window_stamp = G_GUINT64_CONSTANT(0x100000000); + + static void invalidate_work_areas (MetaWindow *window); + static void set_wm_state (MetaWindow *window); + static void set_net_wm_state (MetaWindow *window); + static void meta_window_set_above (MetaWindow *window, + gboolean new_value); + + static void meta_window_show (MetaWindow *window); + static void meta_window_hide (MetaWindow *window); + + static void meta_window_save_rect (MetaWindow *window); + + static void ensure_mru_position_after (MetaWindow *window, + MetaWindow *after_this_one); +@@ -1317,61 +1319,63 @@ _meta_window_shared_new (MetaDisplay *display, + + /* override-redirect windows are subtly different from other windows + * with window->on_all_workspaces == TRUE. Other windows are part of + * some workspace (so they can return to that if the flag is turned off), + * but appear on other workspaces. override-redirect windows are part + * of no workspace. + */ + if (!window->override_redirect && window->workspace == NULL) + { + meta_window_read_cgroup (window); + if (window->transient_for != NULL) + { + meta_topic (META_DEBUG_PLACEMENT, + "Putting window %s on same workspace as parent %s", + window->desc, window->transient_for->desc); + + g_warn_if_fail (!window->transient_for->override_redirect); + set_workspace_state (window, + window->transient_for->on_all_workspaces, + window->transient_for->workspace); + } + else if (window->on_all_workspaces) + { + meta_topic (META_DEBUG_PLACEMENT, + "Putting window %s on all workspaces", + window->desc); + + set_workspace_state (window, TRUE, NULL); + } + else if (window->cgroup && window->cgroup->last_active_workspace != NULL && +- !window->cgroup->has_startup_sequence) ++ !window->cgroup->has_startup_sequence && ++ (!window->cgroup->app_info || ++ !g_desktop_app_info_get_boolean (G_DESKTOP_APP_INFO (window->cgroup->app_info), "StartupNotify"))) + { + meta_topic (META_DEBUG_PLACEMENT, + "Putting window %s on active workspace", + window->desc); + set_workspace_state (window, FALSE, window->cgroup->last_active_workspace); + } + else + { + meta_topic (META_DEBUG_PLACEMENT, + "Putting window %s on active workspace", + window->desc); + + set_workspace_state (window, FALSE, workspace_manager->active_workspace); + } + + meta_window_update_struts (window); + } + + meta_window_main_monitor_changed (window, NULL); + + /* Must add window to stack before doing move/resize, since the + * window might have fullscreen size (i.e. should have been + * fullscreen'd; acrobat is one such braindead case; it withdraws + * and remaps its window whenever trying to become fullscreen...) + * and thus constraints may try to auto-fullscreen it which also + * means restacking it. + */ + if (meta_window_is_stackable (window)) + meta_stack_add (window->display->stack, + window); +@@ -3751,61 +3755,63 @@ meta_window_activate_full (MetaWindow *window, + * timestamp as the last user interaction + */ + allow_workspace_switch = TRUE; + } + + if (timestamp != 0 && + XSERVER_TIME_IS_BEFORE (timestamp, window->display->last_user_time)) + { + meta_topic (META_DEBUG_FOCUS, + "last_user_time (%u) is more recent; ignoring " + " _NET_ACTIVE_WINDOW message.", + window->display->last_user_time); + meta_window_set_demands_attention(window); + return; + } + + if (timestamp == 0) + timestamp = meta_display_get_current_time_roundtrip (window->display); + + meta_window_set_user_time (window, timestamp); + + /* disable show desktop mode unless we're a desktop component */ + maybe_leave_show_desktop_mode (window); + + /* Get window on last active, current, or given workspace */ + if (workspace == NULL) + { + meta_window_read_cgroup (window); + if (window->cgroup && + window->cgroup->last_active_workspace != NULL && +- !window->cgroup->has_startup_sequence) ++ !window->cgroup->has_startup_sequence && ++ (!window->cgroup->app_info || ++ !g_desktop_app_info_get_boolean (G_DESKTOP_APP_INFO (window->cgroup->app_info), "StartupNotify"))) + workspace = window->cgroup->last_active_workspace; + else + workspace = workspace_manager->active_workspace; + } + + /* For non-transient windows, we just set up a pulsing indicator, + rather than move windows or workspaces. + See http://bugzilla.gnome.org/show_bug.cgi?id=482354 */ + if (window->transient_for == NULL && + !allow_workspace_switch && + !meta_window_located_on_workspace (window, workspace)) + { + meta_window_set_demands_attention (window); + /* We've marked it as demanding, don't need to do anything else. */ + return; + } + else if (window->transient_for != NULL) + { + /* Move transients to current workspace - preference dialogs should appear over + the source window. */ + meta_window_change_workspace (window, workspace); + } + + if (window->shaded) + meta_window_unshade (window, timestamp); + + unminimize_window_and_all_transient_parents (window); + + if (meta_prefs_get_raise_on_click () || + source_indication == META_CLIENT_TYPE_PAGER) +-- +2.44.0 + diff --git a/0006-meson-Add-optional-libsystemd-dependency.patch b/0006-meson-Add-optional-libsystemd-dependency.patch new file mode 100644 index 0000000..5694cef --- /dev/null +++ b/0006-meson-Add-optional-libsystemd-dependency.patch @@ -0,0 +1,370 @@ +From 63c529fc5a8db1a82fd6b988aa44e3e6a1e417bf Mon Sep 17 00:00:00 2001 +From: Nishal Kulkarni +Date: Thu, 5 Aug 2021 19:37:28 +0530 +Subject: [PATCH 6/6] meson: Add optional libsystemd dependency + +To utilize the API provided by libsystemd it would be better to +create a separate HAVE_LIBSYSTEMD configuration option instead of +having to rely on HAVE_NATIVE_BACKEND. + +For now this will be utilized for getting the control group of a +MetaWindow. + +Part-of: +--- + config.h.meson | 3 +++ + meson.build | 5 ++++- + meson_options.txt | 6 ++++++ + src/meson.build | 6 ++++++ + 4 files changed, 19 insertions(+), 1 deletion(-) + +diff --git a/config.h.meson b/config.h.meson +index 26e13b9ca..b7ca736df 100644 +--- a/config.h.meson ++++ b/config.h.meson +@@ -7,60 +7,63 @@ + /* Version number of package */ + #mesondefine PACKAGE_VERSION + + /* Search path for plugins */ + #mesondefine MUTTER_PLUGIN_DIR + + /* */ + #mesondefine MUTTER_LOCALEDIR + + /* */ + #mesondefine MUTTER_LIBEXECDIR + + /* */ + #mesondefine MUTTER_PKGDATADIR + + /* Defined if EGL support is enabled */ + #mesondefine HAVE_EGL + + /* Defined if EGLDevice support is enabled */ + #mesondefine HAVE_EGL_DEVICE + + /* Defined if EGLStream support is enabled */ + #mesondefine HAVE_WAYLAND_EGLSTREAM + + /* Building with gudev for device type detection */ + #mesondefine HAVE_LIBGUDEV + + /* Building with libwacom for advanced tablet management */ + #mesondefine HAVE_LIBWACOM + ++/* Building with libsystemd */ ++#mesondefine HAVE_LIBSYSTEMD ++ + /* Define if you want to enable the native (KMS) backend based on systemd */ + #mesondefine HAVE_NATIVE_BACKEND + + /* Define if you want to enable Wayland support */ + #mesondefine HAVE_WAYLAND + + /* Defined if screen cast and remote desktop support is enabled */ + #mesondefine HAVE_REMOTE_DESKTOP + + /* Building with SM support */ + #mesondefine HAVE_SM + + /* Building with startup notification support */ + #mesondefine HAVE_STARTUP_NOTIFICATION + + /* Building with Sysprof profiling support */ + #mesondefine HAVE_PROFILER + + /* Path to Xwayland executable */ + #mesondefine XWAYLAND_PATH + + /* Xwayland applications allowed to issue keyboard grabs */ + #mesondefine XWAYLAND_GRAB_DEFAULT_ACCESS_RULES + + /* XKB base prefix */ + #mesondefine XKB_BASE + + /* Whether exists and it defines prctl() */ + #mesondefine HAVE_SYS_PRCTL + +diff --git a/meson.build b/meson.build +index 39ad5bcd1..613aa6779 100644 +--- a/meson.build ++++ b/meson.build +@@ -161,67 +161,69 @@ if have_gles2 + if not have_egl + error('GLESv2 support requires EGL to be enabled') + endif + endif + + have_wayland = get_option('wayland') + if have_wayland + wayland_server_dep = dependency('wayland-server', version: wayland_server_req) + wayland_client_dep = dependency('wayland-client', version: wayland_server_req) + wayland_protocols_dep = dependency('wayland-protocols', + version: wayland_protocols_req) + wayland_egl_dep = dependency('wayland-egl') + + if not have_egl + error('Wayland support requires EGL to be enabled') + endif + endif + + have_libgudev = get_option('udev') + if have_libgudev + libudev_dep = dependency('libudev', version: udev_req) + gudev_dep = dependency('gudev-1.0', version: gudev_req) + udev_dep = dependency('udev') + + udev_dir = get_option('udev_dir') + if udev_dir == '' + udev_dir = udev_dep.get_pkgconfig_variable('udevdir') + endif + endif + ++have_libsystemd = get_option('systemd') ++libsystemd_dep = dependency('libsystemd', required: have_libsystemd) ++ + have_native_backend = get_option('native_backend') + if have_native_backend + libdrm_dep = dependency('libdrm') + libgbm_dep = dependency('gbm', version: gbm_req) + libinput_dep = dependency('libinput', version: libinput_req) + +- libsystemd_dep = dependency('libsystemd', required: false) + if libsystemd_dep.found() + logind_provider_dep = libsystemd_dep + else + logind_provider_dep = dependency('libelogind') + endif + + if not have_egl + error('The native backend requires EGL to be enabled') + endif + + if not have_gles2 + error('The native backend requires GLESv2 to be enabled') + endif + + if not have_libgudev + error('The native backend requires udev to be enabled') + endif + endif + + have_egl_device = get_option('egl_device') + + have_wayland_eglstream = get_option('wayland_eglstream') + if have_wayland_eglstream + wayland_eglstream_protocols_dep = dependency('wayland-eglstream-protocols') + dl_dep = cc.find_library('dl', required: true) + + if not have_wayland + error('Wayland EGLStream support requires Wayland to be enabled') + endif + endif +@@ -359,60 +361,61 @@ if buildtype != 'plain' + '-Werror=array-bounds', + '-Werror=write-strings', + '-Werror=address', + '-Werror=int-to-pointer-cast', + '-Werror=pointer-to-int-cast', + '-Werror=empty-body', + '-Werror=write-strings', + ] + supported_warnings = cc.get_supported_arguments(all_warnings) + add_project_arguments(supported_warnings, language: 'c') + endif + + if get_option('debug') + debug_c_args = [ + '-DG_ENABLE_DEBUG', + '-fno-omit-frame-pointer' + ] + supported_debug_c_args = cc.get_supported_arguments(debug_c_args) + add_project_arguments(supported_debug_c_args, language: 'c') + endif + + cc.compiles('void main (void) { __builtin_ffsl (0); __builtin_popcountl (0); }') + + cdata = configuration_data() + cdata.set_quoted('GETTEXT_PACKAGE', gettext_package) + cdata.set_quoted('VERSION', meson.project_version()) + cdata.set_quoted('PACKAGE_VERSION', meson.project_version()) + + cdata.set('HAVE_EGL', have_egl) + cdata.set('HAVE_WAYLAND', have_wayland) ++cdata.set('HAVE_LIBSYSTEMD', have_libsystemd) + cdata.set('HAVE_NATIVE_BACKEND', have_native_backend) + cdata.set('HAVE_REMOTE_DESKTOP', have_remote_desktop) + cdata.set('HAVE_EGL_DEVICE', have_egl_device) + cdata.set('HAVE_WAYLAND_EGLSTREAM', have_wayland_eglstream) + cdata.set('HAVE_LIBGUDEV', have_libgudev) + cdata.set('HAVE_LIBWACOM', have_libwacom) + cdata.set('HAVE_SM', have_sm) + cdata.set('HAVE_STARTUP_NOTIFICATION', have_startup_notification) + cdata.set('HAVE_INTROSPECTION', have_introspection) + cdata.set('HAVE_PROFILER', have_profiler) + + xkb_base = xkeyboard_config_dep.get_pkgconfig_variable('xkb_base') + cdata.set_quoted('XKB_BASE', xkb_base) + + if cc.has_header_symbol('sys/prctl.h', 'prctl') + cdata.set('HAVE_SYS_PRCTL', 1) + endif + + have_xwayland_initfd = false + have_xwayland_listenfd = false + if have_wayland + xwayland_dep = dependency('xwayland', required: false) + + xwayland_path = get_option('xwayland_path') + if xwayland_path == '' + if xwayland_dep.found() + xwayland_path = xwayland_dep.get_pkgconfig_variable('xwayland') + else + xwayland_path = find_program('Xwayland').path() + endif +diff --git a/meson_options.txt b/meson_options.txt +index 61d9cb48d..6609e4332 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -12,60 +12,66 @@ option('opengl_libname', + + option('gles2_libname', + type: 'string', + value: 'libGLESv2.so.2', + description: 'GLESv2 library file name' + ) + + option('gles2', + type: 'boolean', + value: true, + description: 'Enable GLES2 support' + ) + + option('egl', + type: 'boolean', + value: true, + description: 'Enable EGL support' + ) + option('glx', + type: 'boolean', + value: true, + description: 'Enable GLX support' + ) + + option('wayland', + type: 'boolean', + value: true, + description: 'Enable Wayland support' + ) + ++option('systemd', ++ type: 'boolean', ++ value: true, ++ description: 'Enable systemd support' ++) ++ + option('native_backend', + type: 'boolean', + value: true, + description: 'Enable the native backend' + ) + + option('remote_desktop', + type: 'boolean', + value: true, + description: 'Enable remote desktop and screen cast support' + ) + + option('egl_device', + type: 'boolean', + value: false, + description: 'Enable EGLDevice and EGLStream renderer support' + ) + + option('wayland_eglstream', + type: 'boolean', + value: false, + description: 'Enable Wayland EGLStream support client support' + ) + + option('udev', + type: 'boolean', + value: true, + description: 'Enable udev support when using the X11 backend' + ) + +diff --git a/src/meson.build b/src/meson.build +index 47633498e..9ac6afa1a 100644 +--- a/src/meson.build ++++ b/src/meson.build +@@ -91,60 +91,66 @@ if have_x11 + xinerama_dep, + xext_dep, + ice_dep, + xcomposite_dep, + xcursor_dep, + xdamage_dep, + xkbfile_dep, + xkeyboard_config_dep, + xkbcommon_x11_dep, + xrender_dep, + x11_xcb_dep, + xcb_randr_dep, + xcb_res_dep, + xau_dep, + xtst_dep, + ] + + if have_sm + mutter_pkg_private_deps += [ + sm_dep, + ] + endif + endif + + if have_wayland + mutter_pkg_deps += [ + wayland_server_dep, + ] + endif + ++if have_libsystemd ++ mutter_pkg_private_deps += [ ++ libsystemd_dep, ++ ] ++endif ++ + if have_native_backend + mutter_pkg_private_deps += [ + libdrm_dep, + libinput_dep, + gudev_dep, + libgbm_dep, + logind_provider_dep, + libudev_dep, + xkbcommon_dep, + ] + endif + + if have_wayland_eglstream + mutter_lib_deps += [ + dl_dep, + ] + mutter_pkg_private_deps += [ + wayland_eglstream_protocols_dep, + ] + endif + + mutter_deps = [ + mutter_pkg_deps, + mutter_pkg_private_deps, + mutter_lib_deps, + ] + + mutter_c_args = [ + '-DCLUTTER_ENABLE_COMPOSITOR_API', + '-DCOGL_ENABLE_EXPERIMENTAL_API', +-- +2.44.0 + diff --git a/mutter.spec b/mutter.spec index 75a5ff9..5c5abce 100644 --- a/mutter.spec +++ b/mutter.spec @@ -10,7 +10,7 @@ Name: mutter Version: 40.9 -Release: 17%{?dist} +Release: 18%{?dist} Summary: Window and compositing manager based on Clutter License: GPLv2+ @@ -120,6 +120,15 @@ Patch48: 0001-backends-Disambiguate-output-mapped-to-tablet-with-c.patch Patch49: 0001-kms-impl-device-Add-addfb2_modifiers-to-MetaKmsDevic.patch Patch50: 0002-kms-device-Disable-modifiers-when-DRM_CAP_ADDFB2_MOD.patch +# Focus stealing prevention fixes +# Resolves https://issues.redhat.com/browse/RHEL-29537 +Patch51: 0001-window-Don-t-switch-workspaces-if-users-from-forged-.patch +Patch52: 0002-core-events-Count-shell-interactions-has-user-intera.patch +Patch53: 0003-core-window-Split-cgroup-out-to-separate-struct.patch +Patch54: 0004-window-Track-workspace-per-cgroup.patch +Patch55: 0005-core-display-Avoid-placement-heuristcs-for-apps-that.patch +Patch56: 0006-meson-Add-optional-libsystemd-dependency.patch + BuildRequires: chrpath BuildRequires: pango-devel BuildRequires: startup-notification-devel @@ -267,6 +276,11 @@ desktop-file-validate %{buildroot}/%{_datadir}/applications/%{name}.desktop %{_datadir}/mutter-%{mutter_api_version}/tests %changelog +* Mon Jul 29 2024 Ray Strode - 40.9-18 +- Don't allow applications on other workspaces to steal + focus, unless their timestamps are pristine + Resolves: RHEL-29537 + * Thu Jul 04 2024 José Expósito - 40.9-17 - Fix Wayland session with Virtio driver Resolves: RHEL-45198