[mutter/wip/tiling-2: 3/4] window: Add new tiling code



commit 4e5e6aa7f85ffa017b34a596c41d4af1ee4babd8
Author: Jasper St. Pierre <jstpierre mecheye net>
Date:   Tue Jun 30 18:10:58 2015 -0700

    window: Add new tiling code
    
    The new tiling code, instead of based around "tiling states", is instead
    based around constrained edges. This allows us to have windows that have
    three constrained edges, but keep one free-floating, e.g. a window tiled
    to the left has the left, top, and bottom edges constrained, but the
    right edge can be left resizable.
    
    This system also is easily extended to support corner tiling. We also,
    using the new "size state" system, also keep normal, tiled, and
    maximized sizes independently, allowing the maximize button to bounce
    between maximized and tiled states without reverting to normal in
    between. Dragging from the top will always restore the normal state,
    though.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=751857

 src/core/constraints.c    |   46 +++++++-
 src/core/keybindings.c    |    2 +
 src/core/screen-private.h |   17 ++-
 src/core/screen.c         |   54 +++++++---
 src/core/window-private.h |    8 +-
 src/core/window.c         |  279 +++++++++++++++++++++++++++++++++++++++------
 src/meta/common.h         |   20 ++++
 src/meta/window.h         |    4 +
 8 files changed, 375 insertions(+), 55 deletions(-)
---
diff --git a/src/core/constraints.c b/src/core/constraints.c
index 9247ce6..e0e3490 100644
--- a/src/core/constraints.c
+++ b/src/core/constraints.c
@@ -97,7 +97,7 @@ typedef enum
   PRIORITY_ENTIRELY_VISIBLE_ON_WORKAREA = 1,
   PRIORITY_SIZE_HINTS_INCREMENTS = 1,
   PRIORITY_MAXIMIZATION = 2,
-  PRIORITY_TILING = 2,
+  PRIORITY_CONSTRAINED_EDGES = 2,
   PRIORITY_FULLSCREEN = 2,
   PRIORITY_SIZE_HINTS_LIMITS = 3,
   PRIORITY_TITLEBAR_VISIBLE = 4,
@@ -152,6 +152,10 @@ static gboolean constrain_maximization       (MetaWindow         *window,
                                               ConstraintInfo     *info,
                                               ConstraintPriority  priority,
                                               gboolean            check_only);
+static gboolean constrain_constrained_edges  (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_constrained_edges,  "constrain_constrained_edges"},
   {constrain_fullscreen,         "constrain_fullscreen"},
   {constrain_size_increments,    "constrain_size_increments"},
   {constrain_size_limits,        "constrain_size_limits"},
@@ -799,6 +804,45 @@ constrain_fullscreen (MetaWindow         *window,
 }
 
 static gboolean
+constrain_constrained_edges (MetaWindow         *window,
+                             ConstraintInfo     *info,
+                             ConstraintPriority  priority,
+                             gboolean            check_only)
+{
+  MetaRectangle monitor, new_rectangle;
+  gboolean constraint_already_satisfied;
+
+  if (priority > PRIORITY_CONSTRAINED_EDGES)
+    return TRUE;
+
+  /* Determine whether constraint applies; exit if it doesn't */
+  if (!window->constrained_edges)
+    return TRUE;
+
+  new_rectangle = info->current;
+  monitor = info->work_area_monitor;
+
+  if (window->constrained_edges & META_DIRECTION_LEFT)
+    new_rectangle.x = monitor.x;
+  if (window->constrained_edges & META_DIRECTION_RIGHT)
+    new_rectangle.x = monitor.x + monitor.width - new_rectangle.width;
+  if (window->constrained_edges & META_DIRECTION_TOP)
+    new_rectangle.y = monitor.y;
+  if (window->constrained_edges & META_DIRECTION_BOTTOM)
+    new_rectangle.y = monitor.y + monitor.height - new_rectangle.height;
+
+  constraint_already_satisfied =
+    meta_rectangle_equal (&info->current, &new_rectangle);
+
+  if (check_only || constraint_already_satisfied)
+    return constraint_already_satisfied;
+
+  /*** Enforce constraint ***/
+  info->current = new_rectangle;
+  return TRUE;
+}
+
+static gboolean
 constrain_size_increments (MetaWindow         *window,
                            ConstraintInfo     *info,
                            ConstraintPriority  priority,
diff --git a/src/core/keybindings.c b/src/core/keybindings.c
index adeb9bd..f54d8da 100644
--- a/src/core/keybindings.c
+++ b/src/core/keybindings.c
@@ -2936,6 +2936,7 @@ handle_toggle_tiled_left (MetaDisplay     *display,
                           MetaKeyBinding  *binding,
                           gpointer         dummy)
 {
+  meta_window_toggle_tile (window, META_TILE_ZONE_W);
 }
 
 static void
@@ -2946,6 +2947,7 @@ handle_toggle_tiled_right (MetaDisplay     *display,
                            MetaKeyBinding  *binding,
                            gpointer         dummy)
 {
+  meta_window_toggle_tile (window, META_TILE_ZONE_E);
 }
 
 static void
diff --git a/src/core/screen-private.h b/src/core/screen-private.h
index d0fc318..25acc5b 100644
--- a/src/core/screen-private.h
+++ b/src/core/screen-private.h
@@ -57,7 +57,13 @@ struct _MetaScreen
   MetaRectangle rect;  /* Size of screen; rect.x & rect.y are always 0 */
   MetaUI *ui;
 
-  guint tile_preview_timeout_id;
+  struct {
+    gboolean exists;
+    guint timeout_id;
+    MetaWindow *window;
+    MetaRectangle area;
+    int monitor;
+  } tile_preview;
 
   MetaWorkspace *active_workspace;
 
@@ -132,9 +138,12 @@ void          meta_screen_foreach_window      (MetaScreen                 *scree
 
 void          meta_screen_update_cursor       (MetaScreen                 *screen);
 
-void          meta_screen_update_tile_preview          (MetaScreen    *screen,
-                                                        gboolean       delay);
-void          meta_screen_hide_tile_preview            (MetaScreen    *screen);
+void          meta_screen_update_tile_preview (MetaScreen    *screen,
+                                               MetaWindow    *window,
+                                               MetaRectangle  area,
+                                               int            monitor,
+                                               gboolean       delay);
+void          meta_screen_hide_tile_preview   (MetaScreen *screen);
 
 MetaWindow*   meta_screen_get_mouse_window     (MetaScreen                 *screen,
                                                 MetaWindow                 *not_this_one);
diff --git a/src/core/screen.c b/src/core/screen.c
index 4103b91..2d1867b 100644
--- a/src/core/screen.c
+++ b/src/core/screen.c
@@ -718,8 +718,6 @@ meta_screen_new (MetaDisplay *display,
   screen->ui = meta_ui_new (screen->display->xdisplay,
                             screen->xscreen);
 
-  screen->tile_preview_timeout_id = 0;
-
   screen->stack = meta_stack_new (screen);
   screen->stack_tracker = meta_stack_tracker_new (screen);
 
@@ -807,8 +805,8 @@ meta_screen_free (MetaScreen *screen,
 
   g_free (screen->monitor_infos);
 
-  if (screen->tile_preview_timeout_id)
-    g_source_remove (screen->tile_preview_timeout_id);
+  if (screen->tile_preview.timeout_id)
+    g_source_remove (screen->tile_preview.timeout_id);
 
   g_free (screen->screen_name);
 
@@ -1287,43 +1285,69 @@ meta_screen_set_cursor (MetaScreen *screen,
 static gboolean
 meta_screen_update_tile_preview_timeout (gpointer data)
 {
+  MetaScreen *screen = data;
+
+  screen->tile_preview.timeout_id = 0;
+
+  if (screen->tile_preview.exists)
+    {
+      meta_compositor_show_tile_preview (screen->display->compositor,
+                                         screen->tile_preview.window,
+                                         &screen->tile_preview.area,
+                                         screen->tile_preview.monitor);
+    }
+  else
+    {
+      meta_compositor_hide_tile_preview (screen->display->compositor);
+    }
+
   return FALSE;
 }
 
 #define TILE_PREVIEW_TIMEOUT_MS 200
 
 void
-meta_screen_update_tile_preview (MetaScreen *screen,
-                                 gboolean    delay)
+meta_screen_update_tile_preview (MetaScreen    *screen,
+                                 MetaWindow    *window,
+                                 MetaRectangle  area,
+                                 int            monitor,
+                                 gboolean       delay)
 {
+  screen->tile_preview.exists = TRUE;
+  screen->tile_preview.window = window;
+  screen->tile_preview.area = area;
+  screen->tile_preview.monitor = monitor;
+
   if (delay)
     {
-      if (screen->tile_preview_timeout_id > 0)
+      if (screen->tile_preview.timeout_id > 0)
         return;
 
-      screen->tile_preview_timeout_id =
+      screen->tile_preview.timeout_id =
         g_timeout_add (TILE_PREVIEW_TIMEOUT_MS,
                        meta_screen_update_tile_preview_timeout,
                        screen);
-      g_source_set_name_by_id (screen->tile_preview_timeout_id,
+      g_source_set_name_by_id (screen->tile_preview.timeout_id,
                                "[mutter] meta_screen_update_tile_preview_timeout");
     }
   else
     {
-      if (screen->tile_preview_timeout_id > 0)
-        g_source_remove (screen->tile_preview_timeout_id);
+      if (screen->tile_preview.timeout_id > 0)
+        g_source_remove (screen->tile_preview.timeout_id);
 
-      meta_screen_update_tile_preview_timeout ((gpointer)screen);
+      meta_screen_update_tile_preview_timeout ((gpointer) screen);
     }
 }
 
 void
 meta_screen_hide_tile_preview (MetaScreen *screen)
 {
-  if (screen->tile_preview_timeout_id > 0)
-    g_source_remove (screen->tile_preview_timeout_id);
+  screen->tile_preview.exists = FALSE;
+
+  if (screen->tile_preview.timeout_id > 0)
+    g_source_remove (screen->tile_preview.timeout_id);
 
-  meta_compositor_hide_tile_preview (screen->display->compositor);
+  meta_screen_update_tile_preview_timeout ((gpointer) screen);
 }
 
 MetaWindow*
diff --git a/src/core/window-private.h b/src/core/window-private.h
index 7a2e5d8..8afa5bd 100644
--- a/src/core/window-private.h
+++ b/src/core/window-private.h
@@ -450,8 +450,10 @@ struct _MetaWindow
   /* This is where we store data while in another state. The information
    * above in MetaWindow is always the current state of the window. */
   struct {
-    MetaWindowSizeState normal, maximized;
+    MetaWindowSizeState normal, tiled, maximized;
   } size_states;
+
+  MetaDirection constrained_edges;
 };
 
 struct _MetaWindowClass
@@ -617,7 +619,6 @@ void meta_window_update_for_monitors_changed (MetaWindow *window);
 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);
 
 gboolean meta_window_updates_are_frozen (MetaWindow *window);
 
@@ -684,4 +685,7 @@ gboolean meta_window_has_pointer (MetaWindow *window);
 
 void meta_window_emit_size_changed (MetaWindow *window);
 
+void meta_window_toggle_tile (MetaWindow   *window,
+                              MetaTileZone  tile_zone);
+
 #endif
diff --git a/src/core/window.c b/src/core/window.c
index 429b079..c4f96c5 100644
--- a/src/core/window.c
+++ b/src/core/window.c
@@ -2609,6 +2609,8 @@ get_current_size_state (MetaWindow *window)
 {
   if (META_WINDOW_MAXIMIZED (window))
     return &window->size_states.maximized;
+  else if (window->constrained_edges != 0)
+    return &window->size_states.tiled;
   else
     return &window->size_states.normal;
 }
@@ -2880,35 +2882,48 @@ meta_window_requested_dont_bypass_compositor (MetaWindow *window)
   return window->bypass_compositor == _NET_WM_BYPASS_COMPOSITOR_HINT_OFF;
 }
 
-static gboolean
-meta_window_can_tile_maximized (MetaWindow *window)
-{
-  return window->has_maximize_func;
-}
-
-gboolean
-meta_window_can_tile_side_by_side (MetaWindow *window)
+static void
+get_tile_zone_area (MetaWindow    *window,
+                    MetaTileZone   tile_zone,
+                    MetaRectangle *rect_p)
 {
-  int monitor;
-  MetaRectangle tile_area;
-  MetaRectangle client_rect;
-
-  if (!meta_window_can_tile_maximized (window))
-    return FALSE;
+  MetaRectangle work_area;
+  meta_window_get_work_area_for_monitor (window, window->monitor->number, &work_area);
 
-  monitor = meta_screen_get_current_monitor (window->screen);
-  meta_window_get_work_area_for_monitor (window, monitor, &tile_area);
+  MetaRectangle rect = {
+    .x = work_area.x,
+    .y = work_area.y,
+  };
 
-  /* Do not allow tiling in portrait orientation */
-  if (tile_area.height > tile_area.width)
-    return FALSE;
-
-  tile_area.width /= 2;
+  switch (tile_zone & META_DIRECTION_HORIZONTAL)
+    {
+    case META_DIRECTION_HORIZONTAL:
+      rect.width = work_area.width;
+      break;
+    case META_DIRECTION_LEFT:
+      rect.width = work_area.width / 2;
+      break;
+    case META_DIRECTION_RIGHT:
+      rect.x += work_area.width / 2;
+      rect.width = work_area.width / 2;
+      break;
+    }
 
-  meta_window_frame_rect_to_client_rect (window, &tile_area, &client_rect);
+  switch (tile_zone & META_DIRECTION_VERTICAL)
+    {
+    case META_DIRECTION_VERTICAL:
+      rect.height = work_area.height;
+      break;
+    case META_DIRECTION_TOP:
+      rect.height = work_area.height / 2;
+      break;
+    case META_DIRECTION_BOTTOM:
+      rect.y += work_area.height / 2;
+      rect.height = work_area.height / 2;
+      break;
+    }
 
-  return client_rect.width >= window->size_hints.min_width &&
-         client_rect.height >= window->size_hints.min_height;
+  *rect_p = rect;
 }
 
 static void
@@ -3022,7 +3037,10 @@ meta_window_unmaximize (MetaWindow        *window,
   g_assert (unmaximize_horizontally || unmaximize_vertically);
 
   MetaWindowSizeState *size_state;
-  size_state = &window->size_states.normal;
+  if (window->constrained_edges != 0)
+    size_state = &window->size_states.tiled;
+  else
+    size_state = &window->size_states.normal;
 
   /* Special-case unmaximizing both directions to restoring the
    * last state. This makes the unmaximize buttons go back to the
@@ -3085,6 +3103,12 @@ meta_window_unmaximize (MetaWindow        *window,
 
       new_size_state.rect = target_rect;
 
+      /* Clear any constrained edges when unmaximizing */
+      if (unmaximize_horizontally)
+        window->constrained_edges &= ~META_DIRECTION_HORIZONTAL;
+      if (unmaximize_vertically)
+        window->constrained_edges &= ~META_DIRECTION_VERTICAL;
+
       meta_window_unmaximize_internal (window, &new_size_state);
     }
 }
@@ -5485,6 +5509,119 @@ update_move_timeout (gpointer data)
   return FALSE;
 }
 
+typedef enum {
+  META_RECT_EDGE_W = 1 << 0, /* west */
+  META_RECT_EDGE_E = 1 << 1, /* east */
+  META_RECT_EDGE_H = 1 << 2, /* horizontal (middle) */
+  META_RECT_EDGE_N = 1 << 3, /* north */
+  META_RECT_EDGE_S = 1 << 4, /* south */
+  META_RECT_EDGE_V = 1 << 5, /* vertical (middle) */
+} MetaRectEdges;
+
+typedef struct {
+  int x1, y1, x2, y2;
+} Box;
+
+static MetaRectEdges
+get_rect_edges (Box outer, Box inner, int x, int y)
+{
+  MetaDirection edges = 0;
+
+  if (x >= outer.x1 && x < inner.x1)
+    edges |= META_RECT_EDGE_W;
+  else if (x >= inner.x2 && x < outer.x2)
+    edges |= META_RECT_EDGE_E;
+  else if (x >= inner.x1 && x < inner.x2)
+    edges |= META_RECT_EDGE_H;
+
+  if (y >= outer.y1 && y < inner.y1)
+    edges |= META_RECT_EDGE_N;
+  else if (y >= inner.y2 && y < outer.y2)
+    edges |= META_RECT_EDGE_S;
+  else if (y >= inner.y1 && y < inner.y2)
+    edges |= META_RECT_EDGE_V;
+
+  return edges;
+}
+
+static inline Box
+box_from_rect (MetaRectangle rect)
+{
+  Box box = { .x1 = rect.x, .y1 = rect.y, .x2 = rect.x + rect.width, .y2 = rect.y + rect.height };
+  return box;
+}
+
+static inline int
+get_drag_shake_threshold (void)
+{
+  /* Originally for detaching maximized windows, but we use this
+   * for the zones at the sides of the monitor where trigger tiling
+   * because it's about the right size
+   */
+#define DRAG_THRESHOLD_TO_SHAKE_THRESHOLD_FACTOR 6
+  return meta_prefs_get_drag_threshold () * DRAG_THRESHOLD_TO_SHAKE_THRESHOLD_FACTOR;
+}
+
+static MetaTileZone
+get_tile_zone_for_pointer (MetaWindow *window,
+                           int x, int y)
+{
+  if (!window->has_maximize_func)
+    return 0;
+
+  const MetaMonitorInfo *monitor;
+  MetaRectangle work_area;
+  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);
+
+  MetaRectangle client_rect;
+  meta_window_frame_rect_to_client_rect (window, &work_area, &client_rect);
+
+  gboolean can_tile_horz = ((client_rect.width / 2) >= window->size_hints.min_width);
+  gboolean can_tile_vert = ((client_rect.height / 2) >= window->size_hints.min_height);
+  gboolean can_max_horz = (client_rect.width >= window->size_hints.min_width);
+  gboolean can_max_vert = (client_rect.height >= window->size_hints.min_height);
+
+  int shake_threshold = get_drag_shake_threshold ();
+  Box outer = box_from_rect (monitor->rect);
+  /* fudge the work area with the shake threshold */
+  Box inner = box_from_rect (work_area);
+  inner.x1 = MAX (inner.x1, shake_threshold);
+  inner.x2 = MIN (outer.x2 - shake_threshold, inner.x2);
+  inner.y1 = MAX (inner.y1, shake_threshold);
+  inner.y2 = MIN (outer.y2 - shake_threshold, inner.y2);
+
+  MetaRectEdges edges = get_rect_edges (outer, inner, x, y);
+
+  /* Simple cases: outside of a monitor, or in the inner rectangle
+   * entirely aren't tile zones. */
+  if (edges == 0 ||
+      edges == (META_RECT_EDGE_H | META_RECT_EDGE_V))
+    return 0;
+
+  /* Special case: the top border is maximization */
+  if (edges == (META_RECT_EDGE_N | META_RECT_EDGE_H))
+    return META_TILE_ZONE_MAXIMIZED;
+
+  MetaTileZone tile_zone = 0;
+
+  if ((edges & META_RECT_EDGE_W) && can_tile_horz)
+    tile_zone |= META_DIRECTION_LEFT;
+  else if ((edges & META_RECT_EDGE_E) && can_tile_horz)
+    tile_zone |= META_DIRECTION_RIGHT;
+  else if ((edges & META_RECT_EDGE_H) && can_max_horz)
+    tile_zone |= META_DIRECTION_HORIZONTAL;
+
+  if ((edges & META_RECT_EDGE_N) && can_tile_vert)
+    tile_zone |= META_DIRECTION_TOP;
+  else if ((edges & META_RECT_EDGE_S) && can_tile_vert)
+    tile_zone |= META_DIRECTION_BOTTOM;
+  else if ((edges & META_RECT_EDGE_V) && can_max_vert)
+    tile_zone |= META_DIRECTION_VERTICAL;
+
+  return tile_zone;
+}
+
 static void
 update_move (MetaWindow  *window,
              gboolean     snap,
@@ -5521,20 +5658,36 @@ update_move (MetaWindow  *window,
   if (dx == 0 && dy == 0)
     return;
 
-  /* Originally for detaching maximized windows, but we use this
-   * for the zones at the sides of the monitor where trigger tiling
-   * because it's about the right size
-   */
-#define DRAG_THRESHOLD_TO_SHAKE_THRESHOLD_FACTOR 6
-  shake_threshold = meta_prefs_get_drag_threshold () *
-    DRAG_THRESHOLD_TO_SHAKE_THRESHOLD_FACTOR;
+  shake_threshold = get_drag_shake_threshold ();
 
   /* 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.
    */
+  int shake_x_threshold = G_MAXINT;
+
+  if ((window->constrained_edges & META_DIRECTION_HORIZONTAL) &&
+      (window->constrained_edges & META_DIRECTION_HORIZONTAL) != META_DIRECTION_HORIZONTAL)
+    shake_x_threshold = shake_threshold;
 
-  if ((META_WINDOW_MAXIMIZED (window) && ABS (dy) >= shake_threshold))
+  if (meta_prefs_get_edge_tiling ())
+    {
+      MetaTileZone tile_zone = get_tile_zone_for_pointer (window, x, y);
+      if (tile_zone != 0)
+        {
+          MetaRectangle tile_area;
+          get_tile_zone_area (window, tile_zone, &tile_area);
+          const MetaMonitorInfo *monitor = meta_screen_get_current_monitor_info_for_pos (window->screen, x, 
y);
+          meta_screen_update_tile_preview (window->screen, window, tile_area, monitor->number, TRUE);
+        }
+      else
+        {
+          meta_screen_hide_tile_preview (window->screen);
+        }
+    }
+
+  if ((META_WINDOW_MAXIMIZED (window) || window->constrained_edges) &&
+      (ABS (dy) >= shake_threshold || ABS (dx) >= shake_x_threshold))
     {
       double prop;
 
@@ -5564,6 +5717,8 @@ update_move (MetaWindow  *window,
       window->size_states.normal.rect.x = display->grab_initial_window_pos.x;
       window->size_states.normal.rect.y = display->grab_initial_window_pos.y;
 
+      window->constrained_edges = 0;
+
       meta_window_unmaximize_internal (window, &window->size_states.normal);
       return;
     }
@@ -5818,6 +5973,10 @@ end_grab_op (MetaWindow *window,
           update_move (window,
                        modifiers & CLUTTER_SHIFT_MASK,
                        x, y);
+
+          MetaTileZone tile_zone = get_tile_zone_for_pointer (window, x, y);
+          if (tile_zone != 0)
+            meta_window_tile (window, tile_zone);
         }
       else if (meta_grab_op_is_resizing (window->display->grab_op))
         {
@@ -5827,6 +5986,9 @@ end_grab_op (MetaWindow *window,
                          TRUE);
         }
     }
+
+  meta_screen_hide_tile_preview (window->screen);
+
   meta_display_end_grab_op (window->display, clutter_event_get_time (event));
 }
 
@@ -7664,3 +7826,54 @@ meta_window_emit_size_changed (MetaWindow *window)
 {
   g_signal_emit (window, window_signals[SIZE_CHANGED], 0);
 }
+
+void
+meta_window_tile (MetaWindow   *window,
+                  MetaTileZone  tile_zone)
+{
+  /* Special case: Tile maximizing is the same as maximizing. */
+  if (tile_zone == META_TILE_ZONE_MAXIMIZED)
+    {
+      meta_window_maximize (window, META_MAXIMIZE_BOTH);
+      return;
+    }
+
+  MetaWindowSizeState *size_state = get_current_size_state (window);
+  save_size_state (window, size_state, &window->rect);
+
+  window->constrained_edges = tile_zone;
+
+  if ((window->constrained_edges & META_DIRECTION_VERTICAL) == META_DIRECTION_VERTICAL)
+    window->maximized_vertically = TRUE;
+  if ((window->constrained_edges & META_DIRECTION_HORIZONTAL) == META_DIRECTION_HORIZONTAL)
+    window->maximized_horizontally = TRUE;
+
+  meta_window_recalc_features (window);
+  set_net_wm_state (window);
+
+  MetaRectangle target_rect;
+  get_tile_zone_area (window, tile_zone, &target_rect);
+
+  meta_window_move_resize_internal (window,
+                                    META_MOVE_RESIZE_MOVE_ACTION | META_MOVE_RESIZE_RESIZE_ACTION | 
META_MOVE_RESIZE_STATE_CHANGED,
+                                    NorthWestGravity,
+                                    target_rect);
+}
+
+void
+meta_window_untile (MetaWindow *window)
+{
+  window->constrained_edges = 0;
+}
+
+void
+meta_window_toggle_tile (MetaWindow   *window,
+                         MetaTileZone  tile_zone)
+{
+  /* Toggle to a specific toggle point. So if we're in NW and toggle-tile
+   * to W, then tile to W. If we then toggle-tile to W again, we untile. */
+  if (window->constrained_edges == tile_zone)
+    meta_window_untile (window);
+  else
+    meta_window_tile (window, tile_zone);
+}
diff --git a/src/meta/common.h b/src/meta/common.h
index 181c3d6..4bcc67b 100644
--- a/src/meta/common.h
+++ b/src/meta/common.h
@@ -317,6 +317,26 @@ typedef enum
   META_DIRECTION_VERTICAL   = META_DIRECTION_UP   | META_DIRECTION_DOWN,
 } MetaDirection;
 
+/* Tile zones are specified in terms of their constrained edges.
+ * So, "top left corner" is top and left, which should be obvious,
+ * but "left side of the screen" is top, bottom, and left
+ */
+typedef enum {
+  META_TILE_ZONE_MAXIMIZED_HORZ = META_DIRECTION_HORIZONTAL,
+  META_TILE_ZONE_MAXIMIZED_VERT = META_DIRECTION_VERTICAL,
+  META_TILE_ZONE_MAXIMIZED      = META_TILE_ZONE_MAXIMIZED_HORZ | META_TILE_ZONE_MAXIMIZED_VERT,
+
+  META_TILE_ZONE_W = META_TILE_ZONE_MAXIMIZED_VERT | META_DIRECTION_LEFT,
+  META_TILE_ZONE_E = META_TILE_ZONE_MAXIMIZED_VERT | META_DIRECTION_RIGHT,
+  META_TILE_ZONE_N = META_TILE_ZONE_MAXIMIZED_HORZ | META_DIRECTION_TOP,
+  META_TILE_ZONE_S = META_TILE_ZONE_MAXIMIZED_HORZ | META_DIRECTION_BOTTOM,
+
+  META_TILE_ZONE_NW = META_DIRECTION_TOP | META_DIRECTION_LEFT,
+  META_TILE_ZONE_NE = META_DIRECTION_TOP | META_DIRECTION_RIGHT,
+  META_TILE_ZONE_SW = META_DIRECTION_BOTTOM | META_DIRECTION_LEFT,
+  META_TILE_ZONE_SE = META_DIRECTION_BOTTOM | META_DIRECTION_RIGHT,
+} MetaTileZone;
+
 /**
  * MetaMotionDirection:
  * @META_MOTION_UP: Upwards motion
diff --git a/src/meta/window.h b/src/meta/window.h
index a3a4b76..6a40217 100644
--- a/src/meta/window.h
+++ b/src/meta/window.h
@@ -258,4 +258,8 @@ gboolean meta_window_is_client_decorated (MetaWindow *window);
 gboolean meta_window_titlebar_is_onscreen    (MetaWindow *window);
 void     meta_window_shove_titlebar_onscreen (MetaWindow *window);
 
+void     meta_window_tile (MetaWindow   *window,
+                           MetaTileZone  tile_zone);
+void     meta_window_untile (MetaWindow *window);
+
 #endif


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