Blob Blame History Raw
From d627d8f100ecd6abfafcd207975f5e9e89aba8c5 Mon Sep 17 00:00:00 2001
From: Michael Webster <miketwebster@gmail.com>
Date: Fri, 23 Jan 2015 13:12:35 -0500
Subject: [PATCH] Improved support for client-side-decorated windows:

- Fixed keeping titlebar onscreen
- Fixed edge-resistance thresholds when dragging
- Fixed visible gap when dragging window against an edge to tile
- Fixed pointer/window relationship when pulling a window free from
  a tiled or maximized state.

* This does not address issues when dragging taller windows free from
  lower tile positions, where muffin wants to make sure an unmaximized
  window unmaximizes itself fully onscreen, thereby forcing the pointer
  from over the titlebar.  This was pre-existing for all types of windows.

Improvements are effective in both Debian (Gtk 3.14) and Mint 17.1 (3.10).
---
 src/core/constraints.c     | 154 ++++++++++++++++++++++++++++++---------------
 src/core/edge-resistance.c |   9 +++
 src/core/place.c           |  24 +++----
 src/core/window-private.h  |   4 ++
 src/core/window.c          |  29 +++++++--
 5 files changed, 153 insertions(+), 67 deletions(-)

diff --git a/src/core/constraints.c b/src/core/constraints.c
index 4a2e427..8be8311 100644
--- a/src/core/constraints.c
+++ b/src/core/constraints.c
@@ -204,9 +204,11 @@ static void place_window_if_needed       (MetaWindow     *window,
                                           ConstraintInfo *info);
 static void update_onscreen_requirements (MetaWindow     *window,
                                           ConstraintInfo *info);
-static void extend_by_frame              (MetaRectangle           *rect,
+static void extend_by_frame              (MetaWindow *window,
+                                          MetaRectangle           *rect,
                                           const MetaFrameBorders  *borders);
-static void unextend_by_frame            (MetaRectangle           *rect,
+static void unextend_by_frame            (MetaWindow *window,
+                                          MetaRectangle           *rect,
                                           const MetaFrameBorders  *borders);
 
 typedef gboolean (* ConstraintFunc) (MetaWindow         *window,
@@ -672,7 +674,7 @@ update_onscreen_requirements (MetaWindow     *window,
   /* The require onscreen/on-single-monitor and titlebar_visible
    * stuff is relative to the outer window, not the inner
    */
-  extend_by_frame (&info->current, info->borders);
+  extend_by_frame (window, &info->current, info->borders);
 
   /* Update whether we want future constraint runs to require the
    * window to be on fully onscreen.
@@ -703,45 +705,72 @@ update_onscreen_requirements (MetaWindow     *window,
   /* Update whether we want future constraint runs to require the
    * titlebar to be visible.
    */
-  if (window->frame && window->decorated)
-    {
-      MetaRectangle titlebar_rect;
-
-      titlebar_rect = info->current;
-      titlebar_rect.height = info->borders->visible.top;
-      old = window->require_titlebar_visible;
-      window->require_titlebar_visible =
-        meta_rectangle_overlaps_with_region (info->usable_screen_region,
-                                             &titlebar_rect);
-      if (old ^ window->require_titlebar_visible)
-        meta_topic (META_DEBUG_GEOMETRY,
-                    "require_titlebar_visible for %s toggled to %s\n",
-                    window->desc,
-                    window->require_titlebar_visible ? "TRUE" : "FALSE");
-    }
+
+  MetaRectangle titlebar_rect;
+
+  meta_window_get_titlebar_rect (window, &titlebar_rect);
+
+  titlebar_rect.x += info->current.x;
+  titlebar_rect.y += info->current.y;
+
+  old = window->require_titlebar_visible;
+  window->require_titlebar_visible =
+    meta_rectangle_overlaps_with_region (info->usable_screen_region,
+                                         &titlebar_rect);
+
+  if (old ^ window->require_titlebar_visible)
+    meta_topic (META_DEBUG_GEOMETRY,
+                "require_titlebar_visible for %s toggled to %s\n",
+                window->desc,
+                window->require_titlebar_visible ? "TRUE" : "FALSE");
+
 
   /* Don't forget to restore the position of the window */
-  unextend_by_frame (&info->current, info->borders);
+  unextend_by_frame (window, &info->current, info->borders);
 }
 
 static void
-extend_by_frame (MetaRectangle           *rect,
-                 const MetaFrameBorders *borders)
+extend_by_frame (MetaWindow              *window,
+                 MetaRectangle           *rect,
+                 const MetaFrameBorders  *borders)
 {
-  rect->x -= borders->visible.left;
-  rect->y -= borders->visible.top;
-  rect->width  += borders->visible.left + borders->visible.right;
-  rect->height += borders->visible.top + borders->visible.bottom;
+  if (window->frame)
+    {
+      rect->x -= borders->visible.left;
+      rect->y -= borders->visible.top;
+      rect->width  += borders->visible.left + borders->visible.right;
+      rect->height += borders->visible.top + borders->visible.bottom;
+    }
+  else if (meta_window_is_client_decorated (window))
+    {
+      const GtkBorder *extents = &window->custom_frame_extents;
+      rect->x += extents->left;
+      rect->y += extents->top;
+      rect->width -= extents->left + extents->right;
+      rect->height -= extents->top + extents->bottom;
+    }
 }
 
 static void
-unextend_by_frame (MetaRectangle           *rect,
-                   const MetaFrameBorders *borders)
+unextend_by_frame (MetaWindow              *window,
+                   MetaRectangle           *rect,
+                   const MetaFrameBorders  *borders)
 {
-  rect->x += borders->visible.left;
-  rect->y += borders->visible.top;
-  rect->width  -= borders->visible.left + borders->visible.right;
-  rect->height -= borders->visible.top + borders->visible.bottom;
+  if (window->frame)
+    {
+      rect->x += borders->visible.left;
+      rect->y += borders->visible.top;
+      rect->width  -= borders->visible.left + borders->visible.right;
+      rect->height -= borders->visible.top + borders->visible.bottom;
+    }
+  else if (meta_window_is_client_decorated (window))
+    {
+      const GtkBorder *extents = &window->custom_frame_extents;
+      rect->x -= extents->left;
+      rect->y -= extents->top;
+      rect->width += extents->left + extents->right;
+      rect->height += extents->top + extents->bottom;
+    }
 }
 
 static gboolean
@@ -853,7 +882,7 @@ constrain_maximization (MetaWindow         *window,
         }
 
         target_size = info->current;
-        extend_by_frame (&target_size, info->borders);
+        extend_by_frame (window, &target_size, info->borders);
         meta_rectangle_expand_to_snapped_borders (&target_size,
                                                   &info->entire_monitor,
                                                    active_workspace_struts,
@@ -862,7 +891,7 @@ constrain_maximization (MetaWindow         *window,
         g_slist_free (snapped_windows_as_struts);
       } else {
           target_size = info->current;
-          extend_by_frame (&target_size, info->borders);
+          extend_by_frame (window, &target_size, info->borders);
           meta_rectangle_expand_to_avoiding_struts (&target_size,
                                                     &info->entire_monitor,
                                                     direction,
@@ -870,7 +899,7 @@ constrain_maximization (MetaWindow         *window,
       }
    }
   /* Now make target_size = maximized size of client window */
-  unextend_by_frame (&target_size, info->borders);
+  unextend_by_frame (window, &target_size, info->borders);
 
   /* Check min size constraints; max size constraints are ignored for maximized
    * windows, as per bug 327543.
@@ -1025,7 +1054,7 @@ constrain_tiling (MetaWindow         *window,
       }
   }
 
-  unextend_by_frame (&target_size, info->borders);
+  unextend_by_frame (window, &target_size, info->borders);
 
   /* Check min size constraints; max size constraints are ignored as for
    * maximized windows.
@@ -1376,7 +1405,7 @@ do_screen_and_monitor_relative_constraints (
   /* Determine whether constraint applies; exit if it doesn't */
   how_far_it_can_be_smushed = info->current;
   meta_window_get_size_limits (window, info->borders, TRUE, &min_size, &max_size);
-  extend_by_frame (&info->current, info->borders);
+  extend_by_frame (window, &info->current, info->borders);
 
   if (info->action_type != ACTION_MOVE)
     {
@@ -1396,7 +1425,7 @@ do_screen_and_monitor_relative_constraints (
                                         &info->current);
   if (exit_early || constraint_satisfied || check_only)
     {
-      unextend_by_frame (&info->current, info->borders);
+      unextend_by_frame (window, &info->current, info->borders);
       return constraint_satisfied;
     }
 
@@ -1415,12 +1444,20 @@ do_screen_and_monitor_relative_constraints (
                                    info->fixed_directions,
                                    &info->current);
   else
-    /* For everything else, shove the rectangle into the relevant region */
-    meta_rectangle_shove_into_region (region_spanning_rectangles,
-                                      info->fixed_directions,
-                                      &info->current);
+    {
+      /* For everything else, shove the rectangle into the relevant region */
+      if (meta_window_is_client_decorated (window))
+        extend_by_frame (window, &info->current, info->borders);
+
+      meta_rectangle_shove_into_region (region_spanning_rectangles,
+                                        info->fixed_directions,
+                                        &info->current);
+
+      if (meta_window_is_client_decorated (window))
+        unextend_by_frame (window, &info->current, info->borders);
+    }
 
-  unextend_by_frame (&info->current, info->borders);
+  unextend_by_frame (window, &info->current, info->borders);
   return TRUE;
 }
 
@@ -1442,7 +1479,7 @@ constrain_to_single_monitor (MetaWindow         *window,
       window->type == META_WINDOW_DOCK      ||
       window->screen->n_monitor_infos == 1  ||
       !window->require_on_single_monitor    ||
-      !window->frame                        ||
+      (!window->frame && !meta_window_is_client_decorated (window)) ||
       info->is_user_action)
     return TRUE;
 
@@ -1491,6 +1528,7 @@ constrain_titlebar_visible (MetaWindow         *window,
   int bottom_amount;
   int horiz_amount_offscreen, vert_amount_offscreen;
   int horiz_amount_onscreen,  vert_amount_onscreen;
+  int scale = meta_prefs_get_ui_scale ();
 
   if (priority > PRIORITY_TITLEBAR_VISIBLE)
     return TRUE;
@@ -1509,7 +1547,6 @@ constrain_titlebar_visible (MetaWindow         *window,
       window->type == META_WINDOW_DOCK    ||
       window->fullscreen                  ||
       !window->require_titlebar_visible   ||
-      !window->decorated                  ||
       unconstrained_user_action)
     return TRUE;
 
@@ -1522,8 +1559,8 @@ constrain_titlebar_visible (MetaWindow         *window,
    */
   horiz_amount_onscreen = info->current.width  / 4;
   vert_amount_onscreen  = info->current.height / 4;
-  horiz_amount_onscreen = CLAMP (horiz_amount_onscreen, 10, 75);
-  vert_amount_onscreen  = CLAMP (vert_amount_onscreen,  10, 75);
+  horiz_amount_onscreen = CLAMP (horiz_amount_onscreen, 10 * scale, 75 * scale);
+  vert_amount_onscreen  = CLAMP (vert_amount_onscreen,  10 * scale, 75 * scale);
   horiz_amount_offscreen = info->current.width - horiz_amount_onscreen;
   vert_amount_offscreen  = info->current.height - vert_amount_onscreen;
   horiz_amount_offscreen = MAX (horiz_amount_offscreen, 0);
@@ -1536,6 +1573,15 @@ constrain_titlebar_visible (MetaWindow         *window,
       bottom_amount = info->current.height + info->borders->visible.bottom;
       vert_amount_onscreen = info->borders->visible.top;
     }
+  else if (meta_window_is_client_decorated (window))
+    {
+      extend_by_frame (window, &info->current, info->borders);
+      extend_by_frame (window, &info->current, info->borders);
+      vert_amount_onscreen = CSD_TITLEBAR_HEIGHT * scale; /* Hardcoded for now, we don't get this from Gtk */
+      bottom_amount = vert_amount_offscreen = MAX ((info->current.height - vert_amount_onscreen), 0);
+      unextend_by_frame (window, &info->current, info->borders);
+      unextend_by_frame (window, &info->current, info->borders);
+    }
   else
     bottom_amount = vert_amount_offscreen;
 
@@ -1575,6 +1621,7 @@ constrain_partially_onscreen (MetaWindow         *window,
   int top_amount, bottom_amount;
   int horiz_amount_offscreen, vert_amount_offscreen;
   int horiz_amount_onscreen,  vert_amount_onscreen;
+  int scale = meta_prefs_get_ui_scale ();
 
   if (priority > PRIORITY_PARTIALLY_VISIBLE_ON_WORKAREA)
     return TRUE;
@@ -1596,8 +1643,8 @@ constrain_partially_onscreen (MetaWindow         *window,
    */
   horiz_amount_onscreen = info->current.width  / 4;
   vert_amount_onscreen  = info->current.height / 4;
-  horiz_amount_onscreen = CLAMP (horiz_amount_onscreen, 10, 75);
-  vert_amount_onscreen  = CLAMP (vert_amount_onscreen,  10, 75);
+  horiz_amount_onscreen = CLAMP (horiz_amount_onscreen, 10 * scale, 75 * scale);
+  vert_amount_onscreen  = CLAMP (vert_amount_onscreen,  10 * scale, 75 * scale);
   horiz_amount_offscreen = info->current.width - horiz_amount_onscreen;
   vert_amount_offscreen  = info->current.height - vert_amount_onscreen;
   horiz_amount_offscreen = MAX (horiz_amount_offscreen, 0);
@@ -1611,6 +1658,15 @@ constrain_partially_onscreen (MetaWindow         *window,
       bottom_amount = info->current.height + info->borders->visible.bottom;
       vert_amount_onscreen = info->borders->visible.top;
     }
+  else if (meta_window_is_client_decorated (window))
+    {
+      extend_by_frame (window, &info->current, info->borders);
+      extend_by_frame (window, &info->current, info->borders);
+      vert_amount_onscreen = CSD_TITLEBAR_HEIGHT * scale; /* Hardcoded for now, we don't get this from Gtk */
+      bottom_amount = vert_amount_offscreen = MAX ((info->current.height - vert_amount_onscreen), 0);
+      unextend_by_frame (window, &info->current, info->borders);
+      unextend_by_frame (window, &info->current, info->borders);
+    }
   else
     bottom_amount = vert_amount_offscreen;
 
diff --git a/src/core/edge-resistance.c b/src/core/edge-resistance.c
index 0a0e6c9..b9adea6 100644
--- a/src/core/edge-resistance.c
+++ b/src/core/edge-resistance.c
@@ -1157,6 +1157,15 @@ meta_window_edge_resistance_for_move (MetaWindow  *window,
 
   meta_window_get_outer_rect (window, &old_outer);
 
+  if (meta_window_is_client_decorated (window))
+    {
+      const GtkBorder *extents = &window->custom_frame_extents;
+      old_outer.x += extents->left;
+      old_outer.y += extents->top;
+      old_outer.width -= extents->left + extents->right;
+      old_outer.height -= extents->top + extents->bottom;
+    }
+
   proposed_outer = old_outer;
   proposed_outer.x += (*new_x - old_x);
   proposed_outer.y += (*new_y - old_y);
diff --git a/src/core/place.c b/src/core/place.c
index 676e1ee..37da70f 100644
--- a/src/core/place.c
+++ b/src/core/place.c
@@ -143,6 +143,7 @@ find_next_cascade (MetaWindow *window,
   GList *tmp;
   GList *sorted;
   int cascade_x, cascade_y;
+  MetaRectangle titlebar_rect;
   int x_threshold, y_threshold;
   int window_width, window_height;
   int cascade_stage;
@@ -162,17 +163,11 @@ find_next_cascade (MetaWindow *window,
    * manually cascade.
    */
 #define CASCADE_FUZZ 15
-  if (borders)
-    {
-      x_threshold = MAX (borders->visible.left, CASCADE_FUZZ);
-      y_threshold = MAX (borders->visible.top, CASCADE_FUZZ);
-    }
-  else
-    {
-      x_threshold = CASCADE_FUZZ;
-      y_threshold = CASCADE_FUZZ;
-    }
-  
+
+  meta_window_get_titlebar_rect (window, &titlebar_rect);
+  x_threshold = MAX (titlebar_rect.x, CASCADE_FUZZ);
+  y_threshold = MAX (titlebar_rect.y, CASCADE_FUZZ);
+
   /* Find furthest-SE origin of all workspaces.
    * cascade_x, cascade_y are the target position
    * of NW corner of window frame.
@@ -217,9 +212,10 @@ find_next_cascade (MetaWindow *window,
            * point. The new window frame should go at the origin
            * of the client window we're stacking above.
            */
-          meta_window_get_position (w, &wx, &wy);
-          cascade_x = wx;
-          cascade_y = wy;
+          meta_window_get_titlebar_rect (w, &titlebar_rect);
+          /* Cascade the window evenly by the titlebar height; this isn't a typo. */
+          cascade_x = wx + titlebar_rect.height;
+          cascade_y = wy + titlebar_rect.height;
           
           /* If we go off the screen, start over with a new cascade */
 	  if (((cascade_x + window_width) >
diff --git a/src/core/window-private.h b/src/core/window-private.h
index 3b20ef4..392fc91 100644
--- a/src/core/window-private.h
+++ b/src/core/window-private.h
@@ -94,6 +94,7 @@ enum {
 #define NUMBER_OF_QUEUES 3
 
 #define HUD_WIDTH 24
+#define CSD_TITLEBAR_HEIGHT 48
 
 struct _MetaWindow
 {
@@ -711,6 +712,9 @@ void meta_window_get_tile_threshold_area_for_mode        (MetaWindow    *window,
                                                           MetaRectangle *tile_area,
                                                           gint           zone_width);
 
+void meta_window_get_titlebar_rect (MetaWindow *window,
+                                    MetaRectangle *titlebar_rect);
+
 gboolean meta_window_same_application (MetaWindow *window,
                                        MetaWindow *other_window);
 
diff --git a/src/core/window.c b/src/core/window.c
index fada2eb..5621ab1 100644
--- a/src/core/window.c
+++ b/src/core/window.c
@@ -5805,6 +5805,28 @@ meta_window_get_tile_side (MetaWindow *window)
     return side;
 }
 
+void
+meta_window_get_titlebar_rect (MetaWindow *window,
+                               MetaRectangle *rect)
+{
+  meta_window_get_outer_rect (window, rect);
+
+  /* The returned rectangle is relative to the frame rect. */
+  rect->x = 0;
+  rect->y = 0;
+
+  if (window->frame)
+    {
+      rect->height = window->frame->child_y;
+    }
+  else
+    {
+      /* Pick an arbitrary height for a titlebar. We might want to
+       * eventually have CSD windows expose their borders to us. */
+      rect->height = CSD_TITLEBAR_HEIGHT * meta_prefs_get_ui_scale ();
+    }
+}
+
 const char*
 meta_window_get_startup_id (MetaWindow *window)
 {
@@ -9184,17 +9206,16 @@ update_move (MetaWindow  *window,
 
       display->grab_initial_window_pos.x =
         x - window->saved_rect.width * prop;
-      display->grab_initial_window_pos.y = y;
 
       if (window->frame)
         {
-          display->grab_initial_window_pos.y += window->frame->child_y / 2;
+          display->grab_initial_window_pos.y = y + (window->frame->child_y / 2);
+          display->grab_anchor_root_x = x;
+          display->grab_anchor_root_y = y;
         }
 
       window->saved_rect.x = display->grab_initial_window_pos.x;
       window->saved_rect.y = display->grab_initial_window_pos.y;
-      display->grab_anchor_root_x = x;
-      display->grab_anchor_root_y = y;
 
       meta_window_unmaximize (window,
                               META_MAXIMIZE_HORIZONTAL |
-- 
2.1.0