From e3d59832c56dbc6acb4301836bc54c467889d515 Mon Sep 17 00:00:00 2001
From: Vasilis Liaskovitis <vliaskovitis@suse.com>
Date: Fri, 29 Sep 2017 16:57:22 +0200
Subject: [PATCH] x11/window: Implement _NET_RESTACK_WINDOW and
 XConfigureRequestEvent sibling

Implement _NET_RESTACK_WINDOW, based on metacity commit 0b5a50c8.

Also respect "above" field (sibling) of XConfigureRequestEvent. When it is
set, perform a stack operation relative to that sibling.

https://bugzilla.gnome.org/show_bug.cgi?id=786363
https://bugzilla.gnome.org/show_bug.cgi?id=786365

Index: mutter-3.20.3/src/core/window-private.h
===================================================================
--- mutter-3.20.3.orig/src/core/window-private.h
+++ mutter-3.20.3/src/core/window-private.h
@@ -619,6 +619,9 @@ void meta_window_frame_size_changed (Met
 void meta_window_stack_just_below (MetaWindow *window,
                                    MetaWindow *below_this_one);
 
+void meta_window_stack_just_above (MetaWindow *window,
+                                   MetaWindow *above_this_one);
+
 void meta_window_set_user_time (MetaWindow *window,
                                 guint32     timestamp);
 
Index: mutter-3.20.3/src/core/window.c
===================================================================
--- mutter-3.20.3.orig/src/core/window.c
+++ mutter-3.20.3/src/core/window.c
@@ -6529,6 +6529,30 @@ meta_window_stack_just_below (MetaWindow
     }
 }
 
+void
+meta_window_stack_just_above (MetaWindow *window,
+                              MetaWindow *above_this_one)
+{
+  g_return_if_fail (window         != NULL);
+  g_return_if_fail (above_this_one != NULL);
+
+  if (window->stack_position < above_this_one->stack_position)
+    {
+      meta_topic (META_DEBUG_STACK,
+                  "Setting stack position of window %s to %d (making it above window %s).\n",
+                  window->desc,
+                  above_this_one->stack_position,
+                  above_this_one->desc);
+      meta_window_set_stack_position (window, above_this_one->stack_position);
+    }
+  else
+    {
+      meta_topic (META_DEBUG_STACK,
+                  "Window %s  was already above window %s.\n",
+                  window->desc, above_this_one->desc);
+    }
+}
+
 /**
  * meta_window_get_user_time:
  * @window: a #MetaWindow
Index: mutter-3.20.3/src/x11/atomnames.h
===================================================================
--- mutter-3.20.3.orig/src/x11/atomnames.h
+++ mutter-3.20.3/src/x11/atomnames.h
@@ -176,6 +176,7 @@ item(_NET_WM_OPAQUE_REGION)
 item(_NET_WM_FRAME_DRAWN)
 item(_NET_WM_FRAME_TIMINGS)
 item(_NET_WM_WINDOW_OPACITY)
+item(_NET_RESTACK_WINDOW)
 
 /* eof atomnames.h */
 
Index: mutter-3.20.3/src/x11/window-x11.c
===================================================================
--- mutter-3.20.3.orig/src/x11/window-x11.c
+++ mutter-3.20.3/src/x11/window-x11.c
@@ -2050,6 +2050,32 @@ meta_window_move_resize_request (MetaWin
     }
 }
 
+static void
+restack_window (MetaWindow *window,
+                MetaWindow *sibling,
+                int         direction)
+{
+ switch (direction)
+   {
+   case Above:
+     if (sibling)
+       meta_window_stack_just_above (window, sibling);
+     else
+       meta_window_raise (window);
+     break;
+   case Below:
+     if (sibling)
+       meta_window_stack_just_below (window, sibling);
+     else
+       meta_window_lower (window);
+     break;
+   case TopIf:
+   case BottomIf:
+   case Opposite:
+     break;
+   }
+}
+
 gboolean
 meta_window_x11_configure_request (MetaWindow *window,
                                    XEvent     *event)
@@ -2083,10 +2109,7 @@ meta_window_x11_configure_request (MetaW
    * the stack looks).
    *
    * I'm pretty sure no interesting client uses TopIf, BottomIf, or
-   * Opposite anyway, so the only possible missing thing is
-   * Above/Below with a sibling set. For now we just pretend there's
-   * never a sibling set and always do the full raise/lower instead of
-   * the raise-just-above/below-sibling.
+   * Opposite anyway.
    */
   if (event->xconfigurerequest.value_mask & CWStackMode)
     {
@@ -2118,19 +2141,23 @@ meta_window_x11_configure_request (MetaW
         }
       else
         {
-          switch (event->xconfigurerequest.detail)
+          MetaWindow *sibling = NULL;
+          /* Handle Above/Below with a sibling set */
+          if (event->xconfigurerequest.above != None)
             {
-            case Above:
-              meta_window_raise (window);
-              break;
-            case Below:
-              meta_window_lower (window);
-              break;
-            case TopIf:
-            case BottomIf:
-            case Opposite:
-              break;
+              MetaDisplay *display;
+
+              display = meta_window_get_display (window);
+              sibling = meta_display_lookup_x_window (display,
+                                                      event->xconfigurerequest.above);
+              if (sibling == NULL)
+                return TRUE;
+
+              meta_topic (META_DEBUG_STACK,
+                      "xconfigure stacking request from window %s sibling %s stackmode %d\n",
+                      window->desc, sibling->desc, event->xconfigurerequest.detail);
             }
+          restack_window (window, sibling, event->xconfigurerequest.detail);
         }
     }
 
@@ -2203,6 +2230,30 @@ query_pressed_buttons (MetaWindow *windo
   return button;
 }
 
+static void
+handle_net_restack_window (MetaDisplay *display,
+                           XEvent      *event)
+{
+  MetaWindow *window, *sibling = NULL;
+
+  /* Ignore if this does not come from a pager, see the WM spec
+   */
+  if (event->xclient.data.l[0] != 2)
+    return;
+
+  window = meta_display_lookup_x_window (display,
+                                         event->xclient.window);
+
+  if (window)
+    {
+      if (event->xclient.data.l[1])
+        sibling = meta_display_lookup_x_window (display,
+                                                event->xclient.data.l[1]);
+
+      restack_window (window, sibling, event->xclient.data.l[2]);
+    }
+}
+
 gboolean
 meta_window_x11_client_message (MetaWindow *window,
                                 XEvent     *event)
@@ -2682,6 +2733,11 @@ meta_window_x11_client_message (MetaWind
 
       meta_window_show_menu (window, META_WINDOW_MENU_WM, x, y);
     }
+  else if (event->xclient.message_type ==
+           display->atom__NET_RESTACK_WINDOW)
+    {
+      handle_net_restack_window (display, event);
+    }
 
   return FALSE;
 }
