diff options
author | Carlos Garnacho <carlosg@gnome.org> | 2019-05-07 12:59:50 +0200 |
---|---|---|
committer | Jonas Ã…dahl <jadahl@gmail.com> | 2019-05-24 15:30:31 +0000 |
commit | ef074ea510e7f3d10fa99d56121a2bdcc13f6f39 (patch) | |
tree | e0e339a60ada8c7f9a9a19dc70caf3a5eac3c741 | |
parent | 39bac6eabd5f8658339b823c8bbc62b381d6504c (diff) | |
download | mutter-ef074ea510e7f3d10fa99d56121a2bdcc13f6f39.tar.gz |
x11: Add MetaX11Stack object
This object takes care of the X11 representation of the window stack,
namely the _NET_CLIENT_LIST and _NET_CLIENT_LIST_STACKING root window
properties.
This code has been lifted from src/core/stack.c into src/x11 as it's
dependent on the X11 display availability. This also leaves MetaStack
squeaky clean of x11 specifics.
https://gitlab.gnome.org/GNOME/mutter/merge_requests/420
-rw-r--r-- | src/core/stack.c | 257 | ||||
-rw-r--r-- | src/core/stack.h | 26 | ||||
-rw-r--r-- | src/meson.build | 2 | ||||
-rw-r--r-- | src/x11/meta-x11-display-private.h | 2 | ||||
-rw-r--r-- | src/x11/meta-x11-display.c | 3 | ||||
-rw-r--r-- | src/x11/meta-x11-stack-private.h | 33 | ||||
-rw-r--r-- | src/x11/meta-x11-stack.c | 413 |
7 files changed, 458 insertions, 278 deletions
diff --git a/src/core/stack.c b/src/core/stack.c index 55701d5bf..5a2f83301 100644 --- a/src/core/stack.c +++ b/src/core/stack.c @@ -29,18 +29,13 @@ #include "core/stack.h" -#include <X11/Xatom.h> - #include "backends/meta-logical-monitor.h" #include "core/frame.h" #include "core/meta-workspace-manager-private.h" #include "core/window-private.h" #include "meta/group.h" -#include "meta/meta-x11-errors.h" #include "meta/prefs.h" #include "meta/workspace.h" -#include "x11/group-private.h" -#include "x11/meta-x11-display-private.h" #define WINDOW_HAS_TRANSIENT_TYPE(w) \ (w->type == META_WINDOW_DIALOG || \ @@ -52,11 +47,8 @@ #define WINDOW_TRANSIENT_FOR_WHOLE_GROUP(w) \ (WINDOW_HAS_TRANSIENT_TYPE (w) && w->transient_for == NULL) -static void stack_sync_to_xserver (MetaStack *stack); static void meta_window_set_stack_position_no_sync (MetaWindow *window, int position); -static void stack_do_window_deletions (MetaStack *stack); -static void stack_do_window_additions (MetaStack *stack); static void stack_do_relayer (MetaStack *stack); static void stack_do_constrain (MetaStack *stack); static void stack_do_resort (MetaStack *stack); @@ -85,7 +77,6 @@ G_DEFINE_TYPE (MetaStack, meta_stack, G_TYPE_OBJECT) static void meta_stack_init (MetaStack *stack) { - stack->xwindows = g_array_new (FALSE, FALSE, sizeof (Window)); } static void @@ -93,11 +84,7 @@ meta_stack_finalize (GObject *object) { MetaStack *stack = META_STACK (object); - g_array_free (stack->xwindows, TRUE); - g_list_free (stack->sorted); - g_list_free (stack->added); - g_list_free (stack->removed); G_OBJECT_CLASS (meta_stack_parent_class)->finalize (object); } @@ -210,7 +197,11 @@ meta_stack_add (MetaStack *stack, if (meta_window_is_in_stack (window)) meta_bug ("Window %s had stack position already\n", window->desc); - stack->added = g_list_prepend (stack->added, window); + stack->sorted = g_list_prepend (stack->sorted, window); + stack->need_resort = TRUE; /* may not be needed as we add to top */ + stack->need_constrain = TRUE; + stack->need_relayer = TRUE; + g_signal_emit (stack, signals[WINDOW_ADDED], 0, window); window->stack_position = stack->n_positions; @@ -219,7 +210,6 @@ meta_stack_add (MetaStack *stack, "Window %s has stack_position initialized to %d\n", window->desc, window->stack_position); - stack_sync_to_xserver (stack); meta_stack_changed (stack); meta_stack_update_window_tile_matches (stack, workspace_manager->active_workspace); } @@ -240,27 +230,10 @@ meta_stack_remove (MetaStack *stack, window->stack_position = -1; stack->n_positions -= 1; - /* We don't know if it's been moved from "added" to "stack" yet */ - stack->added = g_list_remove (stack->added, window); stack->sorted = g_list_remove (stack->sorted, window); g_signal_emit (stack, signals[WINDOW_REMOVED], 0, window); - /* stack->removed is only used to update stack->xwindows */ - if (window->client_type == META_WINDOW_CLIENT_TYPE_X11) - { - /* Remember the window ID to remove it from the stack array. - * The macro is safe to use: Window is guaranteed to be 32 bits, and - * GUINT_TO_POINTER says it only works on 32 bits. - */ - stack->removed = g_list_prepend (stack->removed, - GUINT_TO_POINTER (window->xwindow)); - if (window->frame) - stack->removed = g_list_prepend (stack->removed, - GUINT_TO_POINTER (window->frame->xwindow)); - } - - stack_sync_to_xserver (stack); meta_stack_changed (stack); meta_stack_update_window_tile_matches (stack, workspace_manager->active_workspace); } @@ -272,7 +245,6 @@ meta_stack_update_layer (MetaStack *stack, MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; stack->need_relayer = TRUE; - stack_sync_to_xserver (stack); meta_stack_changed (stack); meta_stack_update_window_tile_matches (stack, workspace_manager->active_workspace); } @@ -284,7 +256,6 @@ meta_stack_update_transient (MetaStack *stack, MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; stack->need_constrain = TRUE; - stack_sync_to_xserver (stack); meta_stack_changed (stack); meta_stack_update_window_tile_matches (stack, workspace_manager->active_workspace); } @@ -315,7 +286,6 @@ meta_stack_raise (MetaStack *stack, meta_window_set_stack_position_no_sync (window, max_stack_position); - stack_sync_to_xserver (stack); meta_stack_changed (stack); meta_stack_update_window_tile_matches (stack, workspace_manager->active_workspace); } @@ -345,7 +315,6 @@ meta_stack_lower (MetaStack *stack, meta_window_set_stack_position_no_sync (window, min_stack_position); - stack_sync_to_xserver (stack); meta_stack_changed (stack); meta_stack_update_window_tile_matches (stack, workspace_manager->active_workspace); } @@ -362,7 +331,6 @@ meta_stack_thaw (MetaStack *stack) g_return_if_fail (stack->freeze_count > 0); stack->freeze_count -= 1; - stack_sync_to_xserver (stack); meta_stack_changed (stack); meta_stack_update_window_tile_matches (stack, NULL); } @@ -880,99 +848,6 @@ apply_constraints (Constraint **constraints, } /** - * stack_do_window_deletions: - * - * Go through "deleted" and take the matching windows - * out of "windows". - */ -static void -stack_do_window_deletions (MetaStack *stack) -{ - /* Do removals before adds, with paranoid idea that we might re-add - * the same window IDs. - */ - GList *tmp; - int i; - - tmp = stack->removed; - while (tmp != NULL) - { - Window xwindow; - xwindow = GPOINTER_TO_UINT (tmp->data); - - /* We go from the end figuring removals are more - * likely to be recent. - */ - i = stack->xwindows->len; - while (i > 0) - { - --i; - - /* there's no guarantee we'll actually find windows to - * remove, e.g. the same xwindow could have been - * added/removed before we ever synced, and we put - * both the window->xwindow and window->frame->xwindow - * in the removal list. - */ - if (xwindow == g_array_index (stack->xwindows, Window, i)) - { - g_array_remove_index (stack->xwindows, i); - goto next; - } - } - - next: - tmp = tmp->next; - } - - g_list_free (stack->removed); - stack->removed = NULL; -} - -static void -stack_do_window_additions (MetaStack *stack) -{ - GList *tmp; - gint n_added; - - n_added = g_list_length (stack->added); - if (n_added > 0) - { - meta_topic (META_DEBUG_STACK, - "Adding %d windows to sorted list\n", - n_added); - - /* stack->added has the most recent additions at the - * front of the list, so we need to reverse it - */ - stack->added = g_list_reverse (stack->added); - - tmp = stack->added; - while (tmp != NULL) - { - MetaWindow *w; - - w = tmp->data; - - if (w->client_type == META_WINDOW_CLIENT_TYPE_X11) - g_array_append_val (stack->xwindows, w->xwindow); - - /* add to the main list */ - stack->sorted = g_list_prepend (stack->sorted, w); - - tmp = tmp->next; - } - - stack->need_resort = TRUE; /* may not be needed as we add to top */ - stack->need_constrain = TRUE; - stack->need_relayer = TRUE; - } - - g_list_free (stack->added); - stack->added = NULL; -} - -/** * stack_do_relayer: * * Update the layers that windows are in @@ -1089,131 +964,11 @@ stack_do_resort (MetaStack *stack) static void stack_ensure_sorted (MetaStack *stack) { - stack_do_window_deletions (stack); - stack_do_window_additions (stack); stack_do_relayer (stack); stack_do_constrain (stack); stack_do_resort (stack); } -/** - * stack_sync_to_server: - * - * Order the windows on the X server to be the same as in our structure. - * We do this using XRestackWindows if we don't know the previous order, - * or XConfigureWindow on a few particular windows if we do and can figure - * out the minimum set of changes. After that, we set __NET_CLIENT_LIST - * and __NET_CLIENT_LIST_STACKING. - * - * FIXME: Now that we have a good view of the stacking order on the server - * with MetaStackTracker it should be possible to do a simpler and better - * job of computing the minimal set of stacking requests needed. - */ -static void -stack_sync_to_xserver (MetaStack *stack) -{ - GArray *x11_stacked; - GArray *all_root_children_stacked; /* wayland OR x11 */ - GList *tmp; - GArray *hidden_stack_ids; - - /* Bail out if frozen */ - if (stack->freeze_count > 0) - return; - - meta_topic (META_DEBUG_STACK, "Syncing window stack to server\n"); - - stack_ensure_sorted (stack); - - /* Create stacked xwindow arrays, in bottom-to-top order - */ - x11_stacked = g_array_new (FALSE, FALSE, sizeof (Window)); - - all_root_children_stacked = g_array_new (FALSE, FALSE, sizeof (guint64)); - hidden_stack_ids = g_array_new (FALSE, FALSE, sizeof (guint64)); - - meta_topic (META_DEBUG_STACK, "Bottom to top: "); - meta_push_no_msg_prefix (); - - for (tmp = g_list_last(stack->sorted); tmp != NULL; tmp = tmp->prev) - { - MetaWindow *w = tmp->data; - guint64 top_level_window; - guint64 stack_id; - - if (w->unmanaging) - continue; - - meta_topic (META_DEBUG_STACK, "%u:%d - %s ", - w->layer, w->stack_position, w->desc); - - if (w->client_type == META_WINDOW_CLIENT_TYPE_X11) - g_array_append_val (x11_stacked, w->xwindow); - - if (w->frame) - top_level_window = w->frame->xwindow; - else - top_level_window = w->xwindow; - - if (w->client_type == META_WINDOW_CLIENT_TYPE_X11) - stack_id = top_level_window; - else - stack_id = w->stamp; - - /* We don't restack hidden windows along with the rest, though they are - * reflected in the _NET hints. Hidden windows all get pushed below - * the screens fullscreen guard_window. */ - if (w->hidden) - { - g_array_append_val (hidden_stack_ids, stack_id); - continue; - } - - g_array_append_val (all_root_children_stacked, stack_id); - } - - meta_topic (META_DEBUG_STACK, "\n"); - meta_pop_no_msg_prefix (); - - /* The screen guard window sits above all hidden windows and acts as - * a barrier to input reaching these windows. */ - guint64 guard_window_id = stack->display->x11_display->guard_window; - g_array_append_val (hidden_stack_ids, guard_window_id); - - /* Sync to server */ - - meta_topic (META_DEBUG_STACK, "Restacking %u windows\n", - all_root_children_stacked->len); - - meta_stack_tracker_restack_managed (stack->display->stack_tracker, - (guint64 *)all_root_children_stacked->data, - all_root_children_stacked->len); - meta_stack_tracker_restack_at_bottom (stack->display->stack_tracker, - (guint64 *)hidden_stack_ids->data, - hidden_stack_ids->len); - - /* Sync _NET_CLIENT_LIST and _NET_CLIENT_LIST_STACKING */ - - XChangeProperty (stack->display->x11_display->xdisplay, - stack->display->x11_display->xroot, - stack->display->x11_display->atom__NET_CLIENT_LIST, - XA_WINDOW, - 32, PropModeReplace, - (unsigned char *)stack->xwindows->data, - stack->xwindows->len); - XChangeProperty (stack->display->x11_display->xdisplay, - stack->display->x11_display->xroot, - stack->display->x11_display->atom__NET_CLIENT_LIST_STACKING, - XA_WINDOW, - 32, PropModeReplace, - (unsigned char *)x11_stacked->data, - x11_stacked->len); - - g_array_free (x11_stacked, TRUE); - g_array_free (hidden_stack_ids, TRUE); - g_array_free (all_root_children_stacked, TRUE); -} - MetaWindow* meta_stack_get_top (MetaStack *stack) { @@ -1523,7 +1278,6 @@ meta_stack_set_positions (MetaStack *stack, meta_topic (META_DEBUG_STACK, "Reset the stack positions of (nearly) all windows\n"); - stack_sync_to_xserver (stack); meta_stack_changed (stack); meta_stack_update_window_tile_matches (stack, NULL); } @@ -1589,7 +1343,6 @@ meta_window_set_stack_position (MetaWindow *window, MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; meta_window_set_stack_position_no_sync (window, position); - stack_sync_to_xserver (window->display->stack); meta_stack_changed (window->display->stack); meta_stack_update_window_tile_matches (window->display->stack, workspace_manager->active_workspace); diff --git a/src/core/stack.h b/src/core/stack.h index c407a2ba8..00d1cfe54 100644 --- a/src/core/stack.h +++ b/src/core/stack.h @@ -56,36 +56,10 @@ struct _MetaStack /** The MetaDisplay containing this stack. */ MetaDisplay *display; - /** - * A sequence of all the Windows (X handles, not MetaWindows) of the windows - * we manage, sorted in order. Suitable to be passed into _NET_CLIENT_LIST. - */ - GArray *xwindows; - /** The MetaWindows of the windows we manage, sorted in order. */ GList *sorted; /** - * MetaWindows waiting to be added to the "sorted" and "windows" list, after - * being added by meta_stack_add() and before being assimilated by - * stack_ensure_sorted(). - * - * The order of the elements in this list is not important; what is important - * is the stack_position element of each window. - */ - GList *added; - - /** - * Windows (X handles, not MetaWindows) waiting to be removed from the - * "windows" list, after being removed by meta_stack_remove() and before - * being assimilated by stack_ensure_sorted(). (We already removed them - * from the "sorted" list.) - * - * The order of the elements in this list is not important. - */ - GList *removed; - - /** * If this is zero, the local stack oughtn't to be brought up to date with * the X server's stack, because it is in the middle of being updated. * If it is positive, the local stack is said to be "frozen", and will need diff --git a/src/meson.build b/src/meson.build index 0a1262fe9..5007b5cab 100644 --- a/src/meson.build +++ b/src/meson.build @@ -392,6 +392,8 @@ mutter_sources = [ 'x11/meta-x11-selection-input-stream-private.h', 'x11/meta-x11-selection-output-stream.c', 'x11/meta-x11-selection-output-stream-private.h', + 'x11/meta-x11-stack.c', + 'x11/meta-x11-stack-private.h', 'x11/mutter-Xatomtype.h', 'x11/session.c', 'x11/session.h', diff --git a/src/x11/meta-x11-display-private.h b/src/x11/meta-x11-display-private.h index c7af182d9..d19cdd688 100644 --- a/src/x11/meta-x11-display-private.h +++ b/src/x11/meta-x11-display-private.h @@ -35,6 +35,7 @@ #include "meta/types.h" #include "meta/meta-x11-display.h" #include "meta-startup-notification-x11.h" +#include "meta-x11-stack-private.h" #include "ui/ui.h" typedef struct _MetaGroupPropHooks MetaGroupPropHooks; @@ -167,6 +168,7 @@ struct _MetaX11Display #define META_X11_DISPLAY_HAS_XINPUT_23(x11_display) ((x11_display)->have_xinput_23) MetaX11StartupNotification *startup_notification; + MetaX11Stack *x11_stack; }; MetaX11Display *meta_x11_display_new (MetaDisplay *display, GError **error); diff --git a/src/x11/meta-x11-display.c b/src/x11/meta-x11-display.c index 2aece19a8..98c4e3e43 100644 --- a/src/x11/meta-x11-display.c +++ b/src/x11/meta-x11-display.c @@ -108,6 +108,8 @@ meta_x11_display_dispose (GObject *object) meta_x11_selection_shutdown (x11_display); + g_clear_object (&x11_display->x11_stack); + if (x11_display->ui) { meta_ui_free (x11_display->ui); @@ -1270,6 +1272,7 @@ meta_x11_display_new (MetaDisplay *display, GError **error) set_desktop_geometry_hint (x11_display); x11_display->ui = meta_ui_new (x11_display); + x11_display->x11_stack = meta_x11_stack_new (x11_display); x11_display->keys_grabbed = FALSE; meta_x11_display_grab_keys (x11_display); diff --git a/src/x11/meta-x11-stack-private.h b/src/x11/meta-x11-stack-private.h new file mode 100644 index 000000000..a00b8e743 --- /dev/null +++ b/src/x11/meta-x11-stack-private.h @@ -0,0 +1,33 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* + * Copyright (C) 2019 Red Hat, Inc + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef META_X11_STACK_H +#define META_X11_STACK_H + +#include <glib-object.h> + +#include "meta/types.h" + +#define META_TYPE_X11_STACK (meta_x11_stack_get_type ()) +G_DECLARE_FINAL_TYPE (MetaX11Stack, meta_x11_stack, META, X11_STACK, GObject) + +typedef struct _MetaX11Stack MetaX11Stack; + +MetaX11Stack * meta_x11_stack_new (MetaX11Display *x11_display); + +#endif /* META_X11_STACK_H */ diff --git a/src/x11/meta-x11-stack.c b/src/x11/meta-x11-stack.c new file mode 100644 index 000000000..fa08fc4c5 --- /dev/null +++ b/src/x11/meta-x11-stack.c @@ -0,0 +1,413 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* + * Copyright (C) 2019 Red Hat, Inc + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include "core/frame.h" +#include "core/stack.h" +#include "core/window-private.h" +#include "x11/meta-x11-display-private.h" +#include "x11/meta-x11-stack-private.h" + +struct _MetaX11Stack +{ + GObject parent; + MetaX11Display *x11_display; + + /* + * A sequence of all the Windows (X handles, not MetaWindows) of the windows + * we manage, sorted in order. Suitable to be passed into _NET_CLIENT_LIST. + */ + GArray *xwindows; + + /* + * MetaWindows waiting to be added to the xwindows list, after + * being added to the MetaStack. + * + * The order of the elements in this list is not important; what is important + * is the stack_position element of each window. + */ + GList *added; + + /* + * Windows (X handles, not MetaWindows) waiting to be removed from the + * xwindows list, after being removed from the MetaStack. + * + * The order of the elements in this list is not important. + */ + GList *removed; +}; + +enum +{ + PROP_DISPLAY = 1, + N_PROPS +}; + +static GParamSpec *pspecs[N_PROPS] = { 0 }; + +G_DEFINE_TYPE (MetaX11Stack, meta_x11_stack, G_TYPE_OBJECT) + +static void +meta_x11_stack_init (MetaX11Stack *x11_stack) +{ + x11_stack->xwindows = g_array_new (FALSE, FALSE, sizeof (Window)); +} + +static void +meta_x11_stack_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + MetaX11Stack *x11_stack = META_X11_STACK (object); + + switch (prop_id) + { + case PROP_DISPLAY: + x11_stack->x11_display = g_value_get_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +meta_x11_stack_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + MetaX11Stack *x11_stack = META_X11_STACK (object); + + switch (prop_id) + { + case PROP_DISPLAY: + g_value_set_object (value, x11_stack->x11_display); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +stack_window_added_cb (MetaStack *stack, + MetaWindow *window, + MetaX11Stack *x11_stack) +{ + if (window->client_type != META_WINDOW_CLIENT_TYPE_X11) + return; + + x11_stack->added = g_list_prepend (x11_stack->added, window); +} + +static void +stack_window_removed_cb (MetaStack *stack, + MetaWindow *window, + MetaX11Stack *x11_stack) +{ + if (window->client_type != META_WINDOW_CLIENT_TYPE_X11) + return; + + x11_stack->added = g_list_remove (x11_stack->added, window); + + x11_stack->removed = g_list_prepend (x11_stack->removed, + GUINT_TO_POINTER (window->xwindow)); + if (window->frame) + { + x11_stack->removed = g_list_prepend (x11_stack->removed, + GUINT_TO_POINTER (window->frame->xwindow)); + } +} + +/** + * stack_do_window_deletions: + * + * Go through "deleted" and take the matching windows + * out of "windows". + */ +static void +x11_stack_do_window_deletions (MetaX11Stack *x11_stack) +{ + GList *tmp; + int i; + + tmp = x11_stack->removed; + while (tmp != NULL) + { + Window xwindow; + xwindow = GPOINTER_TO_UINT (tmp->data); + + /* We go from the end figuring removals are more + * likely to be recent. + */ + i = x11_stack->xwindows->len; + while (i > 0) + { + --i; + + /* there's no guarantee we'll actually find windows to + * remove, e.g. the same xwindow could have been + * added/removed before we ever synced, and we put + * both the window->xwindow and window->frame->xwindow + * in the removal list. + */ + if (xwindow == g_array_index (x11_stack->xwindows, Window, i)) + { + g_array_remove_index (x11_stack->xwindows, i); + goto next; + } + } + + next: + tmp = tmp->next; + } + + g_clear_pointer (&x11_stack->removed, g_list_free); +} + +static void +x11_stack_do_window_additions (MetaX11Stack *x11_stack) +{ + GList *tmp; + gint n_added; + + n_added = g_list_length (x11_stack->added); + if (n_added > 0) + { + meta_topic (META_DEBUG_STACK, + "Adding %d windows to sorted list\n", + n_added); + + /* stack->added has the most recent additions at the + * front of the list, so we need to reverse it + */ + x11_stack->added = g_list_reverse (x11_stack->added); + + tmp = x11_stack->added; + while (tmp != NULL) + { + MetaWindow *w; + + w = tmp->data; + g_array_append_val (x11_stack->xwindows, w->xwindow); + tmp = tmp->next; + } + } + + g_clear_pointer (&x11_stack->added, g_list_free); +} + +/** + * x11_stack_sync_to_server: + * + * Order the windows on the X server to be the same as in our structure. + * We do this using XRestackWindows if we don't know the previous order, + * or XConfigureWindow on a few particular windows if we do and can figure + * out the minimum set of changes. After that, we set __NET_CLIENT_LIST + * and __NET_CLIENT_LIST_STACKING. + * + * FIXME: Now that we have a good view of the stacking order on the server + * with MetaStackTracker it should be possible to do a simpler and better + * job of computing the minimal set of stacking requests needed. + */ +static void +x11_stack_sync_to_xserver (MetaX11Stack *x11_stack) +{ + MetaX11Display *x11_display = x11_stack->x11_display; + MetaStack *stack = x11_display->display->stack; + GArray *x11_stacked; + GArray *all_root_children_stacked; /* wayland OR x11 */ + GList *tmp; + GArray *hidden_stack_ids; + uint64_t guard_window_id; + GList *sorted; + + meta_topic (META_DEBUG_STACK, "Syncing window stack to server\n"); + + /* Create stacked xwindow arrays, in bottom-to-top order + */ + x11_stacked = g_array_new (FALSE, FALSE, sizeof (Window)); + + all_root_children_stacked = g_array_new (FALSE, FALSE, sizeof (guint64)); + hidden_stack_ids = g_array_new (FALSE, FALSE, sizeof (guint64)); + + meta_topic (META_DEBUG_STACK, "Bottom to top: "); + meta_push_no_msg_prefix (); + + sorted = meta_stack_list_windows (stack, NULL); + + for (tmp = sorted; tmp; tmp = tmp->next) + { + MetaWindow *w = tmp->data; + guint64 top_level_window; + guint64 stack_id; + + if (w->unmanaging) + continue; + + meta_topic (META_DEBUG_STACK, "%u:%d - %s ", + w->layer, w->stack_position, w->desc); + + if (w->client_type == META_WINDOW_CLIENT_TYPE_X11) + g_array_append_val (x11_stacked, w->xwindow); + + if (w->frame) + top_level_window = w->frame->xwindow; + else + top_level_window = w->xwindow; + + if (w->client_type == META_WINDOW_CLIENT_TYPE_X11) + stack_id = top_level_window; + else + stack_id = w->stamp; + + /* We don't restack hidden windows along with the rest, though they are + * reflected in the _NET hints. Hidden windows all get pushed below + * the screens fullscreen guard_window. */ + if (w->hidden) + { + g_array_append_val (hidden_stack_ids, stack_id); + continue; + } + + g_array_append_val (all_root_children_stacked, stack_id); + } + + meta_topic (META_DEBUG_STACK, "\n"); + meta_pop_no_msg_prefix (); + + /* The screen guard window sits above all hidden windows and acts as + * a barrier to input reaching these windows. */ + guard_window_id = x11_stack->x11_display->guard_window; + g_array_append_val (hidden_stack_ids, guard_window_id); + + /* Sync to server */ + + meta_topic (META_DEBUG_STACK, "Restacking %u windows\n", + all_root_children_stacked->len); + + meta_stack_tracker_restack_managed (x11_display->display->stack_tracker, + (guint64 *)all_root_children_stacked->data, + all_root_children_stacked->len); + meta_stack_tracker_restack_at_bottom (x11_display->display->stack_tracker, + (guint64 *)hidden_stack_ids->data, + hidden_stack_ids->len); + + /* Sync _NET_CLIENT_LIST and _NET_CLIENT_LIST_STACKING */ + + XChangeProperty (x11_stack->x11_display->xdisplay, + x11_stack->x11_display->xroot, + x11_stack->x11_display->atom__NET_CLIENT_LIST, + XA_WINDOW, + 32, PropModeReplace, + (unsigned char *) x11_stack->xwindows->data, + x11_stack->xwindows->len); + XChangeProperty (x11_stack->x11_display->xdisplay, + x11_stack->x11_display->xroot, + x11_stack->x11_display->atom__NET_CLIENT_LIST_STACKING, + XA_WINDOW, + 32, PropModeReplace, + (unsigned char *) x11_stacked->data, + x11_stacked->len); + + g_array_free (x11_stacked, TRUE); + g_array_free (hidden_stack_ids, TRUE); + g_array_free (all_root_children_stacked, TRUE); + g_list_free (sorted); +} + +static void +stack_changed_cb (MetaX11Stack *x11_stack) +{ + /* Do removals before adds, with paranoid idea that we might re-add + * the same window IDs. + */ + x11_stack_do_window_deletions (x11_stack); + x11_stack_do_window_additions (x11_stack); + x11_stack_sync_to_xserver (x11_stack); +} + +static void +meta_x11_stack_constructed (GObject *object) +{ + MetaX11Stack *x11_stack = META_X11_STACK (object); + MetaX11Display *x11_display = x11_stack->x11_display; + + G_OBJECT_CLASS (meta_x11_stack_parent_class)->constructed (object); + + g_signal_connect (x11_display->display->stack, + "window-added", + G_CALLBACK (stack_window_added_cb), + x11_stack); + g_signal_connect (x11_display->display->stack, + "window-removed", + G_CALLBACK (stack_window_removed_cb), + x11_stack); + g_signal_connect_swapped (x11_display->display->stack, + "changed", + G_CALLBACK (stack_changed_cb), + x11_stack); +} + +static void +meta_x11_stack_finalize (GObject *object) +{ + MetaX11Stack *x11_stack = META_X11_STACK (object); + MetaX11Display *x11_display = x11_stack->x11_display; + + if (x11_display->display && x11_display->display->stack) + { + g_signal_handlers_disconnect_by_data (x11_display->display->stack, + x11_stack); + } + + g_array_free (x11_stack->xwindows, TRUE); + g_list_free (x11_stack->added); + g_list_free (x11_stack->removed); + + G_OBJECT_CLASS (meta_x11_stack_parent_class)->finalize (object); +} + +static void +meta_x11_stack_class_init (MetaX11StackClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = meta_x11_stack_set_property; + object_class->get_property = meta_x11_stack_get_property; + object_class->constructed = meta_x11_stack_constructed; + object_class->finalize = meta_x11_stack_finalize; + + pspecs[PROP_DISPLAY] = + g_param_spec_object ("display", + "Display", + "Display", + META_TYPE_X11_DISPLAY, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + + g_object_class_install_properties (object_class, N_PROPS, pspecs); +} + +MetaX11Stack * +meta_x11_stack_new (MetaX11Display *x11_display) +{ + return g_object_new (META_TYPE_X11_STACK, + "display", x11_display, + NULL); +} |