[mutter] Revert "window: Remove old tiling code"



commit 46eb682c83187577edef8210fb49717c2a49f689
Author: Florian Müllner <fmuellner gnome org>
Date:   Sun Jan 10 15:16:09 2016 +0100

    Revert "window: Remove old tiling code"
    
    This reverts commit 8bded7d497f493cf7c3c5bb0ff5a353e34981215.

 src/compositor/meta-window-actor.c |    7 +
 src/core/constraints.c             |   68 ++++++++-
 src/core/display-private.h         |    9 +
 src/core/display.c                 |    6 +
 src/core/frame.c                   |    6 +
 src/core/keybindings.c             |   64 ++++++---
 src/core/screen.c                  |   40 +++++
 src/core/stack.c                   |   30 ++++
 src/core/stack.h                   |    2 +
 src/core/window-private.h          |   30 ++++-
 src/core/window.c                  |  297 ++++++++++++++++++++++++++++++++++--
 src/meta/window.h                  |    2 +
 12 files changed, 530 insertions(+), 31 deletions(-)
---
diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c
index 1734ff7..58a2d81 100644
--- a/src/compositor/meta-window-actor.c
+++ b/src/compositor/meta-window-actor.c
@@ -818,6 +818,13 @@ meta_window_actor_has_shadow (MetaWindowActor *self)
     return FALSE;
 
   /*
+   * If we have two snap-tiled windows, we don't want the shadow to obstruct
+   * the other window.
+   */
+  if (meta_window_get_tile_match (priv->window))
+    return FALSE;
+
+  /*
    * Always put a shadow around windows with a frame - This should override
    * the restriction about not putting a shadow around ARGB windows.
    */
diff --git a/src/core/constraints.c b/src/core/constraints.c
index 9247ce6..7b23797 100644
--- a/src/core/constraints.c
+++ b/src/core/constraints.c
@@ -152,6 +152,10 @@ static gboolean constrain_maximization       (MetaWindow         *window,
                                               ConstraintInfo     *info,
                                               ConstraintPriority  priority,
                                               gboolean            check_only);
+static gboolean constrain_tiling             (MetaWindow         *window,
+                                              ConstraintInfo     *info,
+                                              ConstraintPriority  priority,
+                                              gboolean            check_only);
 static gboolean constrain_fullscreen         (MetaWindow         *window,
                                               ConstraintInfo     *info,
                                               ConstraintPriority  priority,
@@ -209,6 +213,7 @@ typedef struct {
 static const Constraint all_constraints[] = {
   {constrain_modal_dialog,       "constrain_modal_dialog"},
   {constrain_maximization,       "constrain_maximization"},
+  {constrain_tiling,             "constrain_tiling"},
   {constrain_fullscreen,         "constrain_fullscreen"},
   {constrain_size_increments,    "constrain_size_increments"},
   {constrain_size_limits,        "constrain_size_limits"},
@@ -696,11 +701,16 @@ constrain_maximization (MetaWindow         *window,
     return TRUE;
 
   /* Determine whether constraint applies; exit if it doesn't */
-  if (!window->maximized_horizontally && !window->maximized_vertically)
+  if ((!window->maximized_horizontally && !window->maximized_vertically) ||
+      META_WINDOW_TILED_SIDE_BY_SIDE (window))
     return TRUE;
 
   /* Calculate target_size = maximized size of (window + frame) */
-  if (META_WINDOW_MAXIMIZED (window))
+  if (META_WINDOW_TILED_MAXIMIZED (window))
+    {
+      meta_window_get_current_tile_area (window, &target_size);
+    }
+  else if (META_WINDOW_MAXIMIZED (window))
     {
       target_size = info->work_area_monitor;
     }
@@ -764,6 +774,58 @@ constrain_maximization (MetaWindow         *window,
 }
 
 static gboolean
+constrain_tiling (MetaWindow         *window,
+                  ConstraintInfo     *info,
+                  ConstraintPriority  priority,
+                  gboolean            check_only)
+{
+  MetaRectangle target_size;
+  MetaRectangle min_size, max_size;
+  gboolean hminbad, vminbad;
+  gboolean horiz_equal, vert_equal;
+  gboolean constraint_already_satisfied;
+
+  if (priority > PRIORITY_TILING)
+    return TRUE;
+
+  /* Determine whether constraint applies; exit if it doesn't */
+  if (!META_WINDOW_TILED_SIDE_BY_SIDE (window))
+    return TRUE;
+
+  /* Calculate target_size - as the tile previews need this as well, we
+   * use an external function for the actual calculation
+   */
+  meta_window_get_current_tile_area (window, &target_size);
+
+  /* Check min size constraints; max size constraints are ignored as for
+   * maximized windows.
+   */
+  get_size_limits (window, &min_size, &max_size);
+  hminbad = target_size.width < min_size.width;
+  vminbad = target_size.height < min_size.height;
+  if (hminbad || vminbad)
+    return TRUE;
+
+  /* Determine whether constraint is already satisfied; exit if it is */
+  horiz_equal = target_size.x      == info->current.x &&
+                target_size.width  == info->current.width;
+  vert_equal  = target_size.y      == info->current.y &&
+                target_size.height == info->current.height;
+  constraint_already_satisfied = horiz_equal && vert_equal;
+  if (check_only || constraint_already_satisfied)
+    return constraint_already_satisfied;
+
+  /*** Enforce constraint ***/
+  info->current.x      = target_size.x;
+  info->current.width  = target_size.width;
+  info->current.y      = target_size.y;
+  info->current.height = target_size.height;
+
+  return TRUE;
+}
+
+
+static gboolean
 constrain_fullscreen (MetaWindow         *window,
                       ConstraintInfo     *info,
                       ConstraintPriority  priority,
@@ -815,6 +877,7 @@ constrain_size_increments (MetaWindow         *window,
 
   /* Determine whether constraint applies; exit if it doesn't */
   if (META_WINDOW_MAXIMIZED (window) || window->fullscreen ||
+      META_WINDOW_TILED_SIDE_BY_SIDE (window) ||
       info->action_type == ACTION_MOVE)
     return TRUE;
 
@@ -957,6 +1020,7 @@ constrain_aspect_ratio (MetaWindow         *window,
   constraints_are_inconsistent = minr > maxr;
   if (constraints_are_inconsistent ||
       META_WINDOW_MAXIMIZED (window) || window->fullscreen ||
+      META_WINDOW_TILED_SIDE_BY_SIDE (window) ||
       info->action_type == ACTION_MOVE)
     return TRUE;
 
diff --git a/src/core/display-private.h b/src/core/display-private.h
index a90ade2..29a3ccf 100644
--- a/src/core/display-private.h
+++ b/src/core/display-private.h
@@ -74,6 +74,13 @@ typedef enum {
 #define N_IGNORED_CROSSING_SERIALS  10
 
 typedef enum {
+  META_TILE_NONE,
+  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,
@@ -203,6 +210,8 @@ struct _MetaDisplay
   int         grab_anchor_root_x;
   int         grab_anchor_root_y;
   MetaRectangle grab_anchor_window_pos;
+  MetaTileMode  grab_tile_mode;
+  int           grab_tile_monitor_number;
   int         grab_latest_motion_x;
   int         grab_latest_motion_y;
   guint       grab_have_pointer : 1;
diff --git a/src/core/display.c b/src/core/display.c
index 062a0c3..dbfbc68 100644
--- a/src/core/display.c
+++ b/src/core/display.c
@@ -663,6 +663,8 @@ meta_display_open (void)
 
   display->grab_op = META_GRAB_OP_NONE;
   display->grab_window = NULL;
+  display->grab_tile_mode = META_TILE_NONE;
+  display->grab_tile_monitor_number = -1;
 
   display->grab_edge_resistance_data = NULL;
 
@@ -1909,6 +1911,8 @@ meta_display_begin_grab_op (MetaDisplay *display,
   display->grab_op = op;
   display->grab_window = grab_window;
   display->grab_button = button;
+  display->grab_tile_mode = grab_window->tile_mode;
+  display->grab_tile_monitor_number = grab_window->tile_monitor_number;
   display->grab_anchor_root_x = root_x;
   display->grab_anchor_root_y = root_y;
   display->grab_latest_motion_x = root_x;
@@ -2006,6 +2010,8 @@ meta_display_end_grab_op (MetaDisplay *display,
 
   display->event_route = META_EVENT_ROUTE_NORMAL;
   display->grab_window = NULL;
+  display->grab_tile_mode = META_TILE_NONE;
+  display->grab_tile_monitor_number = -1;
 
   meta_display_update_cursor (display);
 
diff --git a/src/core/frame.c b/src/core/frame.c
index c4be91a..9335fb6 100644
--- a/src/core/frame.c
+++ b/src/core/frame.c
@@ -274,6 +274,12 @@ meta_frame_get_flags (MetaFrame *frame)
   if (META_WINDOW_MAXIMIZED (frame->window))
     flags |= META_FRAME_MAXIMIZED;
 
+  if (META_WINDOW_TILED_LEFT (frame->window))
+    flags |= META_FRAME_TILED_LEFT;
+
+  if (META_WINDOW_TILED_RIGHT (frame->window))
+    flags |= META_FRAME_TILED_RIGHT;
+
   if (frame->window->fullscreen)
     flags |= META_FRAME_FULLSCREEN;
 
diff --git a/src/core/keybindings.c b/src/core/keybindings.c
index adeb9bd..ddbab70 100644
--- a/src/core/keybindings.c
+++ b/src/core/keybindings.c
@@ -2001,13 +2001,23 @@ process_mouse_move_resize_grab (MetaDisplay     *display,
 
   if (event->keyval == CLUTTER_KEY_Escape)
     {
+      /* Hide the tiling preview if necessary */
+      if (window->tile_mode != META_TILE_NONE)
+        meta_screen_hide_tile_preview (screen);
+
+      /* Restore the original tile mode */
+      window->tile_mode = display->grab_tile_mode;
+      window->tile_monitor_number = display->grab_tile_monitor_number;
+
       /* End move or resize and restore to original state.  If the
        * window was a maximized window that had been "shaken loose" we
        * need to remaximize it.  In normal cases, we need to do a
        * moveresize now to get the position back to the original.
        */
-      if (window->shaken_loose)
+      if (window->shaken_loose || window->tile_mode == META_TILE_MAXIMIZED)
         meta_window_maximize (window, META_MAXIMIZE_BOTH);
+      else if (window->tile_mode != META_TILE_NONE)
+        meta_window_tile (window);
       else
         meta_window_move_resize_frame (display->grab_window,
                                        TRUE,
@@ -2929,23 +2939,41 @@ handle_toggle_above       (MetaDisplay     *display,
 }
 
 static void
-handle_toggle_tiled_left (MetaDisplay     *display,
-                          MetaScreen      *screen,
-                          MetaWindow      *window,
-                          ClutterKeyEvent *event,
-                          MetaKeyBinding  *binding,
-                          gpointer         dummy)
+handle_toggle_tiled (MetaDisplay     *display,
+                     MetaScreen      *screen,
+                     MetaWindow      *window,
+                     ClutterKeyEvent *event,
+                     MetaKeyBinding  *binding,
+                     gpointer         dummy)
 {
-}
+  MetaTileMode mode = binding->handler->data;
 
-static void
-handle_toggle_tiled_right (MetaDisplay     *display,
-                           MetaScreen      *screen,
-                           MetaWindow      *window,
-                           ClutterKeyEvent *event,
-                           MetaKeyBinding  *binding,
-                           gpointer         dummy)
-{
+  if ((META_WINDOW_TILED_LEFT (window) && mode == META_TILE_LEFT) ||
+      (META_WINDOW_TILED_RIGHT (window) && mode == META_TILE_RIGHT))
+    {
+      window->tile_monitor_number = window->saved_maximize ? window->monitor->number
+        : -1;
+      window->tile_mode = window->saved_maximize ? META_TILE_MAXIMIZED
+        : META_TILE_NONE;
+
+      if (window->saved_maximize)
+        meta_window_maximize (window, META_MAXIMIZE_BOTH);
+      else
+        meta_window_unmaximize (window, META_MAXIMIZE_BOTH);
+    }
+  else if (meta_window_can_tile_side_by_side (window))
+    {
+      window->tile_monitor_number = window->monitor->number;
+      window->tile_mode = mode;
+      /* Maximization constraints beat tiling constraints, so if the window
+       * is maximized, tiling won't have any effect unless we unmaximize it
+       * horizontally first; rather than calling meta_window_unmaximize(),
+       * we just set the flag and rely on meta_window_tile() syncing it to
+       * save an additional roundtrip.
+       */
+      window->maximized_horizontally = FALSE;
+      meta_window_tile (window);
+    }
 }
 
 static void
@@ -3678,14 +3706,14 @@ init_builtin_key_bindings (MetaDisplay *display)
                           mutter_keybindings,
                           META_KEY_BINDING_PER_WINDOW,
                           META_KEYBINDING_ACTION_TOGGLE_TILED_LEFT,
-                          handle_toggle_tiled_left, 0);
+                          handle_toggle_tiled, META_TILE_LEFT);
 
   add_builtin_keybinding (display,
                           "toggle-tiled-right",
                           mutter_keybindings,
                           META_KEY_BINDING_PER_WINDOW,
                           META_KEYBINDING_ACTION_TOGGLE_TILED_RIGHT,
-                          handle_toggle_tiled_right, 0);
+                          handle_toggle_tiled, META_TILE_RIGHT);
 
   add_builtin_keybinding (display,
                           "toggle-above",
diff --git a/src/core/screen.c b/src/core/screen.c
index 14193d4..19f7db5 100644
--- a/src/core/screen.c
+++ b/src/core/screen.c
@@ -1320,6 +1320,46 @@ meta_screen_set_cursor (MetaScreen *screen,
 static gboolean
 meta_screen_update_tile_preview_timeout (gpointer data)
 {
+  MetaScreen *screen = data;
+  MetaWindow *window = screen->display->grab_window;
+  gboolean needs_preview = FALSE;
+
+  screen->tile_preview_timeout_id = 0;
+
+  if (window)
+    {
+      switch (window->tile_mode)
+        {
+          case META_TILE_LEFT:
+          case META_TILE_RIGHT:
+              if (!META_WINDOW_TILED_SIDE_BY_SIDE (window))
+                needs_preview = TRUE;
+              break;
+
+          case META_TILE_MAXIMIZED:
+              if (!META_WINDOW_MAXIMIZED (window))
+                needs_preview = TRUE;
+              break;
+
+          default:
+              needs_preview = FALSE;
+              break;
+        }
+    }
+
+  if (needs_preview)
+    {
+      MetaRectangle tile_rect;
+      int monitor;
+
+      monitor = meta_window_get_current_tile_monitor_number (window);
+      meta_window_get_current_tile_area (window, &tile_rect);
+      meta_compositor_show_tile_preview (screen->display->compositor,
+                                         window, &tile_rect, monitor);
+    }
+  else
+    meta_compositor_hide_tile_preview (screen->display->compositor);
+
   return FALSE;
 }
 
diff --git a/src/core/stack.c b/src/core/stack.c
index d8e4eb7..a3879f7 100644
--- a/src/core/stack.c
+++ b/src/core/stack.c
@@ -117,6 +117,7 @@ meta_stack_add (MetaStack  *stack,
               window->desc, window->stack_position);
 
   stack_sync_to_xserver (stack);
+  meta_stack_update_window_tile_matches (stack, window->screen->active_workspace);
 }
 
 void
@@ -152,6 +153,7 @@ meta_stack_remove (MetaStack  *stack,
                                      GUINT_TO_POINTER (window->frame->xwindow));
 
   stack_sync_to_xserver (stack);
+  meta_stack_update_window_tile_matches (stack, window->screen->active_workspace);
 }
 
 void
@@ -161,6 +163,7 @@ meta_stack_update_layer (MetaStack  *stack,
   stack->need_relayer = TRUE;
 
   stack_sync_to_xserver (stack);
+  meta_stack_update_window_tile_matches (stack, window->screen->active_workspace);
 }
 
 void
@@ -170,6 +173,7 @@ meta_stack_update_transient (MetaStack  *stack,
   stack->need_constrain = TRUE;
 
   stack_sync_to_xserver (stack);
+  meta_stack_update_window_tile_matches (stack, window->screen->active_workspace);
 }
 
 /* raise/lower within a layer */
@@ -198,6 +202,7 @@ meta_stack_raise (MetaStack  *stack,
   meta_window_set_stack_position_no_sync (window, max_stack_position);
 
   stack_sync_to_xserver (stack);
+  meta_stack_update_window_tile_matches (stack, window->screen->active_workspace);
 }
 
 void
@@ -225,6 +230,7 @@ meta_stack_lower (MetaStack  *stack,
   meta_window_set_stack_position_no_sync (window, min_stack_position);
 
   stack_sync_to_xserver (stack);
+  meta_stack_update_window_tile_matches (stack, window->screen->active_workspace);
 }
 
 void
@@ -240,6 +246,27 @@ meta_stack_thaw (MetaStack *stack)
 
   stack->freeze_count -= 1;
   stack_sync_to_xserver (stack);
+  meta_stack_update_window_tile_matches (stack, NULL);
+}
+
+void
+meta_stack_update_window_tile_matches (MetaStack     *stack,
+                                       MetaWorkspace *workspace)
+{
+  GList *windows, *tmp;
+
+  if (stack->freeze_count > 0)
+    return;
+
+  windows = meta_stack_list_windows (stack, workspace);
+  tmp = windows;
+  while (tmp)
+    {
+      meta_window_compute_tile_match ((MetaWindow *) tmp->data);
+      tmp = tmp->next;
+    }
+
+  g_list_free (windows);
 }
 
 static gboolean
@@ -1421,6 +1448,7 @@ meta_stack_set_positions (MetaStack *stack,
               "Reset the stack positions of (nearly) all windows\n");
 
   stack_sync_to_xserver (stack);
+  meta_stack_update_window_tile_matches (stack, NULL);
 }
 
 void
@@ -1483,4 +1511,6 @@ meta_window_set_stack_position (MetaWindow *window,
 {
   meta_window_set_stack_position_no_sync (window, position);
   stack_sync_to_xserver (window->screen->stack);
+  meta_stack_update_window_tile_matches (window->screen->stack,
+                                         window->screen->active_workspace);
 }
diff --git a/src/core/stack.h b/src/core/stack.h
index b3596cc..e82be0a 100644
--- a/src/core/stack.h
+++ b/src/core/stack.h
@@ -413,4 +413,6 @@ GList* meta_stack_get_positions (MetaStack *stack);
 void   meta_stack_set_positions (MetaStack *stack,
                                  GList     *windows);
 
+void meta_stack_update_window_tile_matches (MetaStack     *stack,
+                                            MetaWorkspace *workspace);
 #endif
diff --git a/src/core/window-private.h b/src/core/window-private.h
index d76aed8..eb89b4c 100644
--- a/src/core/window-private.h
+++ b/src/core/window-private.h
@@ -157,6 +157,14 @@ struct _MetaWindow
   guint maximize_vertically_after_placement : 1;
   guint minimize_after_placement : 1;
 
+  /* The current or requested tile mode. If maximized_vertically is true,
+   * this is the current mode. If not, it is the mode which will be
+   * requested after the window grab is released */
+  guint tile_mode : 2;
+  /* 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;
   int preferred_output_winsys_id;
 
   /* Whether we're shaded */
@@ -433,6 +441,9 @@ struct _MetaWindow
   /* Focused window that is (directly or indirectly) attached to this one */
   MetaWindow *attached_focus_window;
 
+  /* The currently complementary tiled window, if any */
+  MetaWindow *tile_match;
+
   /* Bypass compositor hints */
   guint bypass_compositor;
 };
@@ -481,8 +492,17 @@ struct _MetaWindowClass
                                         (w)->maximized_vertically)
 #define META_WINDOW_MAXIMIZED_VERTICALLY(w)    ((w)->maximized_vertically)
 #define META_WINDOW_MAXIMIZED_HORIZONTALLY(w)  ((w)->maximized_horizontally)
+#define META_WINDOW_TILED_SIDE_BY_SIDE(w)      ((w)->maximized_vertically && \
+                                                !(w)->maximized_horizontally && \
+                                                 (w)->tile_mode != META_TILE_NONE)
+#define META_WINDOW_TILED_LEFT(w)     (META_WINDOW_TILED_SIDE_BY_SIDE(w) && \
+                                       (w)->tile_mode == META_TILE_LEFT)
+#define META_WINDOW_TILED_RIGHT(w)    (META_WINDOW_TILED_SIDE_BY_SIDE(w) && \
+                                       (w)->tile_mode == META_TILE_RIGHT)
+#define META_WINDOW_TILED_MAXIMIZED(w)(META_WINDOW_MAXIMIZED(w) && \
+                                       (w)->tile_mode == META_TILE_MAXIMIZED)
 #define META_WINDOW_ALLOWS_MOVE(w)     ((w)->has_move_func && !(w)->fullscreen)
-#define META_WINDOW_ALLOWS_RESIZE_EXCEPT_HINTS(w)   ((w)->has_resize_func && !META_WINDOW_MAXIMIZED (w) && 
!(w)->fullscreen && !(w)->shaded)
+#define META_WINDOW_ALLOWS_RESIZE_EXCEPT_HINTS(w)   ((w)->has_resize_func && !META_WINDOW_MAXIMIZED (w) && 
!META_WINDOW_TILED_SIDE_BY_SIDE(w) && !(w)->fullscreen && !(w)->shaded)
 #define META_WINDOW_ALLOWS_RESIZE(w)   (META_WINDOW_ALLOWS_RESIZE_EXCEPT_HINTS (w) &&                \
                                         (((w)->size_hints.min_width < (w)->size_hints.max_width) ||  \
                                          ((w)->size_hints.min_height < (w)->size_hints.max_height)))
@@ -502,6 +522,7 @@ void        meta_window_unmanage           (MetaWindow  *window,
                                             guint32      timestamp);
 void        meta_window_queue              (MetaWindow  *window,
                                             guint queuebits);
+void        meta_window_tile               (MetaWindow        *window);
 void        meta_window_maximize_internal  (MetaWindow        *window,
                                             MetaMaximizeFlags  directions,
                                             MetaRectangle     *saved_rect);
@@ -563,6 +584,11 @@ gboolean meta_window_handle_mouse_grab_op_event  (MetaWindow         *window,
 
 GList* meta_window_get_workspaces (MetaWindow *window);
 
+int meta_window_get_current_tile_monitor_number (MetaWindow *window);
+void meta_window_get_current_tile_area         (MetaWindow    *window,
+                                                MetaRectangle *tile_area);
+
+
 gboolean meta_window_same_application (MetaWindow *window,
                                        MetaWindow *other_window);
 
@@ -602,6 +628,8 @@ void meta_window_on_all_workspaces_changed (MetaWindow *window);
 gboolean meta_window_should_attach_to_parent (MetaWindow *window);
 gboolean meta_window_can_tile_side_by_side   (MetaWindow *window);
 
+void meta_window_compute_tile_match (MetaWindow *window);
+
 gboolean meta_window_updates_are_frozen (MetaWindow *window);
 
 void meta_window_set_title                (MetaWindow *window,
diff --git a/src/core/window.c b/src/core/window.c
index 3c458b0..30e7b9a 100644
--- a/src/core/window.c
+++ b/src/core/window.c
@@ -898,6 +898,8 @@ _meta_window_shared_new (MetaDisplay         *display,
   window->require_titlebar_visible = TRUE;
   window->on_all_workspaces = FALSE;
   window->on_all_workspaces_requested = FALSE;
+  window->tile_mode = META_TILE_NONE;
+  window->tile_monitor_number = -1;
   window->shaded = FALSE;
   window->initially_iconic = FALSE;
   window->minimized = FALSE;
@@ -994,6 +996,8 @@ _meta_window_shared_new (MetaDisplay         *display,
                                                               window);
   window->preferred_output_winsys_id = window->monitor->winsys_id;
 
+  window->tile_match = NULL;
+
   /* Assign this #MetaWindow a sequence number which can be used
    * for sorting.
    */
@@ -2609,7 +2613,7 @@ ensure_size_hints_satisfied (MetaRectangle    *rect,
 static void
 meta_window_save_rect (MetaWindow *window)
 {
-  if (!(META_WINDOW_MAXIMIZED (window) || window->fullscreen))
+  if (!(META_WINDOW_MAXIMIZED (window) || META_WINDOW_TILED_SIDE_BY_SIDE (window) || window->fullscreen))
     {
       /* save size/pos as appropriate args for move_resize */
       if (!window->maximized_horizontally)
@@ -2648,6 +2652,9 @@ meta_window_maximize_internal (MetaWindow        *window,
   else
     meta_window_save_rect (window);
 
+  if (maximize_horizontally && maximize_vertically)
+    window->saved_maximize = TRUE;
+
   window->maximized_horizontally =
     window->maximized_horizontally || maximize_horizontally;
   window->maximized_vertically =
@@ -2669,6 +2676,7 @@ void
 meta_window_maximize (MetaWindow        *window,
                       MetaMaximizeFlags  directions)
 {
+  MetaRectangle *saved_rect = NULL;
   gboolean maximize_horizontally, maximize_vertically;
 
   g_return_if_fail (!window->override_redirect);
@@ -2707,7 +2715,17 @@ meta_window_maximize (MetaWindow        *window,
          return;
        }
 
-      meta_window_maximize_internal (window, directions, NULL);
+      if (window->tile_mode != META_TILE_NONE)
+        {
+          saved_rect = &window->saved_rect;
+
+          window->maximized_vertically = FALSE;
+          window->tile_mode = META_TILE_NONE;
+        }
+
+      meta_window_maximize_internal (window,
+                                     directions,
+                                     saved_rect);
 
       MetaRectangle old_frame_rect, old_buffer_rect, new_rect;
 
@@ -2881,6 +2899,29 @@ meta_window_requested_dont_bypass_compositor (MetaWindow *window)
   return window->bypass_compositor == _NET_WM_BYPASS_COMPOSITOR_HINT_OFF;
 }
 
+void
+meta_window_tile (MetaWindow *window)
+{
+  MetaMaximizeFlags directions;
+
+  /* Don't do anything if no tiling is requested */
+  if (window->tile_mode == META_TILE_NONE)
+    return;
+
+  if (window->tile_mode == META_TILE_MAXIMIZED)
+    directions = META_MAXIMIZE_BOTH;
+  else
+    directions = META_MAXIMIZE_VERTICAL;
+
+  meta_window_maximize_internal (window, directions, NULL);
+  meta_screen_update_tile_preview (window->screen, FALSE);
+
+  meta_window_move_resize_now (window);
+
+  if (window->frame)
+    meta_frame_queue_draw (window->frame);
+}
+
 static gboolean
 meta_window_can_tile_maximized (MetaWindow *window)
 {
@@ -2958,6 +2999,9 @@ meta_window_unmaximize (MetaWindow        *window,
   unmaximize_vertically   = directions & META_MAXIMIZE_VERTICAL;
   g_assert (unmaximize_horizontally || unmaximize_vertically);
 
+  if (unmaximize_horizontally && unmaximize_vertically)
+    window->saved_maximize = FALSE;
+
   /* Only do something if the window isn't already maximized in the
    * given direction(s).
    */
@@ -3511,6 +3555,9 @@ meta_window_update_for_monitors_changed (MetaWindow *window)
   if (!new)
     new = &window->screen->monitor_infos[window->screen->primary_monitor_index];
 
+  if (window->tile_mode != META_TILE_NONE)
+    window->tile_monitor_number = new->number;
+
   /* This will eventually reach meta_window_update_monitor that
    * will send leave/enter-monitor events. The old != new monitor
    * check will always fail (due to the new monitor_infos set) so
@@ -3692,6 +3739,9 @@ meta_window_move_resize_internal (MetaWindow          *window,
     }
 
   meta_window_foreach_transient (window, maybe_move_attached_dialog, NULL);
+
+  meta_stack_update_window_tile_matches (window->screen->stack,
+                                         window->screen->active_workspace);
 }
 
 /**
@@ -3796,6 +3846,9 @@ meta_window_move_to_monitor (MetaWindow  *window,
                                          monitor,
                                          &new_area);
 
+  if (window->tile_mode != META_TILE_NONE)
+    window->tile_monitor_number = monitor;
+
   meta_window_move_between_rects (window, &old_area, &new_area);
   window->preferred_output_winsys_id = window->monitor->winsys_id;
 
@@ -5512,12 +5565,65 @@ update_move (MetaWindow  *window,
   shake_threshold = meta_prefs_get_drag_threshold () *
     DRAG_THRESHOLD_TO_SHAKE_THRESHOLD_FACTOR;
 
+  if (snap)
+    {
+      /* We don't want to tile while snapping. Also, clear any previous tile
+         request. */
+      window->tile_mode = META_TILE_NONE;
+      window->tile_monitor_number = -1;
+    }
+  else if (meta_prefs_get_edge_tiling () &&
+           !META_WINDOW_MAXIMIZED (window) &&
+           !META_WINDOW_TILED_SIDE_BY_SIDE (window))
+    {
+      const MetaMonitorInfo *monitor;
+      MetaRectangle work_area;
+
+      /* For side-by-side tiling we are interested in the inside vertical
+       * edges of the work area of the monitor where the pointer is located,
+       * and in the outside top edge for maximized tiling.
+       *
+       * For maximized tiling we use the outside edge instead of the
+       * inside edge, because we don't want to force users to maximize
+       * windows they are placing near the top of their screens.
+       *
+       * The "current" idea of meta_window_get_work_area_current_monitor() and
+       * meta_screen_get_current_monitor() is slightly different: the former
+       * refers to the monitor which contains the largest part of the window,
+       * the latter to the one where the pointer is located.
+       */
+      monitor = meta_screen_get_current_monitor_info_for_pos (window->screen, x, y);
+      meta_window_get_work_area_for_monitor (window,
+                                             monitor->number,
+                                             &work_area);
+
+      /* Check if the cursor is in a position which triggers tiling
+       * and set tile_mode accordingly.
+       */
+      if (meta_window_can_tile_side_by_side (window) &&
+          x >= monitor->rect.x && x < (work_area.x + shake_threshold))
+        window->tile_mode = META_TILE_LEFT;
+      else if (meta_window_can_tile_side_by_side (window) &&
+               x >= work_area.x + work_area.width - shake_threshold &&
+               x < (monitor->rect.x + monitor->rect.width))
+        window->tile_mode = META_TILE_RIGHT;
+      else if (meta_window_can_tile_maximized (window) &&
+               y >= monitor->rect.y && y <= work_area.y)
+        window->tile_mode = META_TILE_MAXIMIZED;
+      else
+        window->tile_mode = META_TILE_NONE;
+
+      if (window->tile_mode != META_TILE_NONE)
+        window->tile_monitor_number = monitor->number;
+    }
+
   /* shake loose (unmaximize) maximized or tiled window if dragged beyond
    * the threshold in the Y direction. Tiled windows can also be pulled
    * loose via X motion.
    */
 
-  if ((META_WINDOW_MAXIMIZED (window) && ABS (dy) >= shake_threshold))
+  if ((META_WINDOW_MAXIMIZED (window) && ABS (dy) >= shake_threshold) ||
+      (META_WINDOW_TILED_SIDE_BY_SIDE (window) && (MAX (ABS (dx), ABS (dy)) >= shake_threshold)))
     {
       double prop;
 
@@ -5526,6 +5632,7 @@ update_move (MetaWindow  *window,
        * is enabled, as top edge tiling can be used in that case
        */
       window->shaken_loose = !meta_prefs_get_edge_tiling ();
+      window->tile_mode = META_TILE_NONE;
 
       /* move the unmaximized window to the cursor */
       prop =
@@ -5554,12 +5661,14 @@ update_move (MetaWindow  *window,
   /* remaximize window on another monitor if window has been shaken
    * loose or it is still maximized (then move straight)
    */
-  else if (window->shaken_loose || META_WINDOW_MAXIMIZED (window))
+  else if ((window->shaken_loose || META_WINDOW_MAXIMIZED (window)) &&
+           window->tile_mode != META_TILE_LEFT && window->tile_mode != META_TILE_RIGHT)
     {
       const MetaMonitorInfo *wmonitor;
       MetaRectangle work_area;
       int monitor;
 
+      window->tile_mode = META_TILE_NONE;
       wmonitor = window->monitor;
 
       for (monitor = 0; monitor < window->screen->n_monitor_infos; monitor++)
@@ -5604,10 +5713,17 @@ update_move (MetaWindow  *window,
         }
     }
 
+  /* Delay showing the tile preview slightly to make it more unlikely to
+   * trigger it unwittingly, e.g. when shaking loose the window or moving
+   * it to another monitor.
+   */
+  meta_screen_update_tile_preview (window->screen,
+                                   window->tile_mode != META_TILE_NONE);
+
   meta_window_get_frame_rect (window, &old);
 
-  /* Don't allow movement in the maximized directions */
-  if (window->maximized_horizontally)
+  /* Don't allow movement in the maximized directions or while tiled */
+  if (window->maximized_horizontally || META_WINDOW_TILED_SIDE_BY_SIDE (window))
     new_x = old.x;
   if (window->maximized_vertically)
     new_y = old.y;
@@ -5768,6 +5884,23 @@ update_resize (MetaWindow *window,
     g_get_current_time (&window->display->grab_last_moveresize_time);
 }
 
+static void
+update_tile_mode (MetaWindow *window)
+{
+  switch (window->tile_mode)
+    {
+      case META_TILE_LEFT:
+      case META_TILE_RIGHT:
+          if (!META_WINDOW_TILED_SIDE_BY_SIDE (window))
+              window->tile_mode = META_TILE_NONE;
+          break;
+      case META_TILE_MAXIMIZED:
+          if (!META_WINDOW_MAXIMIZED (window))
+              window->tile_mode = META_TILE_NONE;
+          break;
+    }
+}
+
 void
 meta_window_update_resize (MetaWindow *window,
                            gboolean    snap,
@@ -5798,9 +5931,12 @@ end_grab_op (MetaWindow *window,
     {
       if (meta_grab_op_is_moving (window->display->grab_op))
         {
-          update_move (window,
-                       modifiers & CLUTTER_SHIFT_MASK,
-                       x, y);
+          if (window->tile_mode != META_TILE_NONE)
+            meta_window_tile (window);
+          else
+            update_move (window,
+                         modifiers & CLUTTER_SHIFT_MASK,
+                         x, y);
         }
       else if (meta_grab_op_is_resizing (window->display->grab_op))
         {
@@ -5808,6 +5944,15 @@ end_grab_op (MetaWindow *window,
                          modifiers & CLUTTER_SHIFT_MASK,
                          x, y,
                          TRUE);
+
+          /* If a tiled window has been dragged free with a
+           * mouse resize without snapping back to the tiled
+           * state, it will end up with an inconsistent tile
+           * mode on mouse release; cleaning the mode earlier
+           * would break the ability to snap back to the tiled
+           * state, so we wait until mouse release.
+           */
+          update_tile_mode (window);
         }
     }
   meta_display_end_grab_op (window->display, clutter_event_get_time (event));
@@ -5992,6 +6137,40 @@ meta_window_get_work_area_all_monitors (MetaWindow    *window,
               window->desc, area->x, area->y, area->width, area->height);
 }
 
+int
+meta_window_get_current_tile_monitor_number (MetaWindow *window)
+{
+  int tile_monitor_number = window->tile_monitor_number;
+
+  if (tile_monitor_number < 0)
+    {
+      meta_warning ("%s called with an invalid monitor number; using 0 instead\n", G_STRFUNC);
+      tile_monitor_number = 0;
+    }
+
+  return tile_monitor_number;
+}
+
+void
+meta_window_get_current_tile_area (MetaWindow    *window,
+                                   MetaRectangle *tile_area)
+{
+  int tile_monitor_number;
+
+  g_return_if_fail (window->tile_mode != META_TILE_NONE);
+
+  tile_monitor_number = meta_window_get_current_tile_monitor_number (window);
+
+  meta_window_get_work_area_for_monitor (window, tile_monitor_number, tile_area);
+
+  if (window->tile_mode == META_TILE_LEFT  ||
+      window->tile_mode == META_TILE_RIGHT)
+    tile_area->width /= 2;
+
+  if (window->tile_mode == META_TILE_RIGHT)
+    tile_area->x += tile_area->width;
+}
+
 gboolean
 meta_window_same_application (MetaWindow *window,
                               MetaWindow *other_window)
@@ -6995,7 +7174,8 @@ meta_window_get_frame_type (MetaWindow *window)
       return META_FRAME_TYPE_LAST;
     }
   else if (window->border_only ||
-           (window->hide_titlebar_when_maximized && META_WINDOW_MAXIMIZED (window)))
+           (window->hide_titlebar_when_maximized && META_WINDOW_MAXIMIZED (window)) ||
+           (window->hide_titlebar_when_maximized && META_WINDOW_TILED_SIDE_BY_SIDE (window)))
     {
       /* override base frame type */
       return META_FRAME_TYPE_BORDER;
@@ -7044,6 +7224,103 @@ meta_window_is_attached_dialog (MetaWindow *window)
   return window->attached;
 }
 
+/**
+ * meta_window_get_tile_match:
+ * @window: a #MetaWindow
+ *
+ * Returns the matching tiled window on the same monitor as @window. This is
+ * the topmost tiled window in a complementary tile mode that is:
+ *
+ *  - on the same monitor;
+ *  - on the same workspace;
+ *  - spanning the remaining monitor width;
+ *  - there is no 3rd window stacked between both tiled windows that's
+ *    partially visible in the common edge.
+ *
+ * Return value: (transfer none) (nullable): the matching tiled window or
+ * %NULL if it doesn't exist.
+ */
+MetaWindow *
+meta_window_get_tile_match (MetaWindow *window)
+{
+  return window->tile_match;
+}
+
+void
+meta_window_compute_tile_match (MetaWindow *window)
+{
+  MetaWindow *match;
+  MetaStack *stack;
+  MetaTileMode match_tile_mode = META_TILE_NONE;
+
+  window->tile_match = NULL;
+
+  if (window->shaded || window->minimized)
+    return;
+
+  if (META_WINDOW_TILED_LEFT (window))
+    match_tile_mode = META_TILE_RIGHT;
+  else if (META_WINDOW_TILED_RIGHT (window))
+    match_tile_mode = META_TILE_LEFT;
+  else
+    return;
+
+  stack = window->screen->stack;
+
+  for (match = meta_stack_get_top (stack);
+       match;
+       match = meta_stack_get_below (stack, match, FALSE))
+    {
+      if (!match->shaded &&
+          !match->minimized &&
+          match->tile_mode == match_tile_mode &&
+          match->monitor == window->monitor &&
+          meta_window_get_workspace (match) == meta_window_get_workspace (window))
+        break;
+    }
+
+  if (match)
+    {
+      MetaWindow *above, *bottommost, *topmost;
+      MetaRectangle above_rect, bottommost_rect, topmost_rect;
+
+      if (meta_stack_windows_cmp (window->screen->stack, match, window) > 0)
+        {
+          topmost = match;
+          bottommost = window;
+        }
+      else
+        {
+          topmost = window;
+          bottommost = match;
+        }
+
+      meta_window_get_frame_rect (bottommost, &bottommost_rect);
+      meta_window_get_frame_rect (topmost, &topmost_rect);
+      /*
+       * If there's a window stacked in between which is partially visible
+       * behind the topmost tile we don't consider the tiles to match.
+       */
+      for (above = meta_stack_get_above (stack, bottommost, FALSE);
+           above && above != topmost;
+           above = meta_stack_get_above (stack, above, FALSE))
+        {
+          if (above->minimized ||
+              above->monitor != window->monitor ||
+              meta_window_get_workspace (above) != meta_window_get_workspace (window))
+            continue;
+
+          meta_window_get_frame_rect (above, &above_rect);
+
+          if (meta_rectangle_overlap (&above_rect, &bottommost_rect) &&
+              meta_rectangle_overlap (&above_rect, &topmost_rect))
+            return;
+        }
+
+      window->tile_match = match;
+    }
+}
+
 void
 meta_window_set_title (MetaWindow *window,
                        const char *title)
diff --git a/src/meta/window.h b/src/meta/window.h
index a3a4b76..60c9997 100644
--- a/src/meta/window.h
+++ b/src/meta/window.h
@@ -214,6 +214,8 @@ MetaFrameType meta_window_get_frame_type (MetaWindow *window);
 
 cairo_region_t *meta_window_get_frame_bounds (MetaWindow *window);
 
+MetaWindow *meta_window_get_tile_match (MetaWindow *window);
+
 void        meta_window_make_fullscreen    (MetaWindow  *window);
 void        meta_window_unmake_fullscreen  (MetaWindow  *window);
 void        meta_window_make_above         (MetaWindow  *window);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]