summaryrefslogtreecommitdiff
path: root/src/x11/meta-x11-stack.c
diff options
context:
space:
mode:
authorCarlos Garnacho <carlosg@gnome.org>2019-05-07 12:59:50 +0200
committerJonas Ã…dahl <jadahl@gmail.com>2019-05-24 15:30:31 +0000
commitef074ea510e7f3d10fa99d56121a2bdcc13f6f39 (patch)
treee0e339a60ada8c7f9a9a19dc70caf3a5eac3c741 /src/x11/meta-x11-stack.c
parent39bac6eabd5f8658339b823c8bbc62b381d6504c (diff)
downloadmutter-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
Diffstat (limited to 'src/x11/meta-x11-stack.c')
-rw-r--r--src/x11/meta-x11-stack.c413
1 files changed, 413 insertions, 0 deletions
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);
+}