raveit65 3243dae
From a87157176ca6e01c8c4047999ee584f00b63c11e Mon Sep 17 00:00:00 2001
raveit65 3243dae
From: Stefano Karapetsas <stefano@karapetsas.com>
raveit65 3243dae
Date: Fri, 31 May 2013 14:22:39 +0000
raveit65 3243dae
Subject: Implement side-by-side tiling
raveit65 3243dae
raveit65 3243dae
Patch by Florian Müllner for Metacity
raveit65 3243dae
https://bugzilla.gnome.org/show_bug.cgi?id=607694
raveit65 3243dae
raveit65 3243dae
When dragging a window over a screen edge and dropping it there,
raveit65 3243dae
maximize it vertically and scale it horizontally to cover the
raveit65 3243dae
corresponding half of the current monitor.
raveit65 3243dae
raveit65 3243dae
Whenever a "hot area" which triggers this behavior is entered, an
raveit65 3243dae
indication of window's target size is displayed after a short delay
raveit65 3243dae
to avoid distraction when moving a window between monitors.
raveit65 3243dae
---
raveit65 3243dae
diff --git a/src/Makefile.am b/src/Makefile.am
raveit65 3243dae
index a7b0123..9326361 100644
raveit65 3243dae
--- a/src/Makefile.am
raveit65 3243dae
+++ b/src/Makefile.am
raveit65 3243dae
@@ -98,6 +98,8 @@ marco_SOURCES = \
raveit65 3243dae
 	include/resizepopup.h \
raveit65 3243dae
 	ui/tabpopup.c \
raveit65 3243dae
 	include/tabpopup.h \
raveit65 3243dae
+	ui/tile-preview.c \
raveit65 3243dae
+	include/tile-preview.h \
raveit65 3243dae
 	ui/theme-parser.c \
raveit65 3243dae
 	ui/theme-parser.h \
raveit65 3243dae
 	ui/theme.c \
raveit65 3243dae
diff --git a/src/core/constraints.c b/src/core/constraints.c
raveit65 3243dae
index 16d9b10..a79f858 100644
raveit65 3243dae
--- a/src/core/constraints.c
raveit65 3243dae
+++ b/src/core/constraints.c
raveit65 3243dae
@@ -98,6 +98,7 @@ typedef enum
raveit65 3243dae
   PRIORITY_ENTIRELY_VISIBLE_ON_WORKAREA = 1,
raveit65 3243dae
   PRIORITY_SIZE_HINTS_INCREMENTS = 1,
raveit65 3243dae
   PRIORITY_MAXIMIZATION = 2,
raveit65 3243dae
+  PRIORITY_TILING = 2,
raveit65 3243dae
   PRIORITY_FULLSCREEN = 2,
raveit65 3243dae
   PRIORITY_SIZE_HINTS_LIMITS = 3,
raveit65 3243dae
   PRIORITY_TITLEBAR_VISIBLE = 4,
raveit65 3243dae
@@ -145,6 +146,10 @@ static gboolean constrain_maximization       (MetaWindow         *window,
raveit65 3243dae
                                               ConstraintInfo     *info,
raveit65 3243dae
                                               ConstraintPriority  priority,
raveit65 3243dae
                                               gboolean            check_only);
raveit65 3243dae
+static gboolean constrain_tiling             (MetaWindow         *window,
raveit65 3243dae
+                                              ConstraintInfo     *info,
raveit65 3243dae
+                                              ConstraintPriority  priority,
raveit65 3243dae
+                                              gboolean            check_only);
raveit65 3243dae
 static gboolean constrain_fullscreen         (MetaWindow         *window,
raveit65 3243dae
                                               ConstraintInfo     *info,
raveit65 3243dae
                                               ConstraintPriority  priority,
raveit65 3243dae
@@ -211,6 +216,7 @@ typedef struct {
raveit65 3243dae
 
raveit65 3243dae
 static const Constraint all_constraints[] = {
raveit65 3243dae
   {constrain_maximization,       "constrain_maximization"},
raveit65 3243dae
+  {constrain_tiling,             "constrain_tiling"},
raveit65 3243dae
   {constrain_fullscreen,         "constrain_fullscreen"},
raveit65 3243dae
   {constrain_size_increments,    "constrain_size_increments"},
raveit65 3243dae
   {constrain_size_limits,        "constrain_size_limits"},
raveit65 3243dae
@@ -731,7 +737,8 @@ constrain_maximization (MetaWindow         *window,
raveit65 3243dae
     return TRUE;
raveit65 3243dae
 
raveit65 3243dae
   /* Determine whether constraint applies; exit if it doesn't */
raveit65 3243dae
-  if (!window->maximized_horizontally && !window->maximized_vertically)
raveit65 3243dae
+  if ((!window->maximized_horizontally && !window->maximized_vertically) ||
raveit65 3243dae
+      META_WINDOW_TILED (window))
raveit65 3243dae
     return TRUE;
raveit65 3243dae
 
raveit65 3243dae
   /* Calculate target_size = maximized size of (window + frame) */
raveit65 3243dae
@@ -800,6 +807,58 @@ constrain_maximization (MetaWindow         *window,
raveit65 3243dae
 }
raveit65 3243dae
 
raveit65 3243dae
 static gboolean
raveit65 3243dae
+constrain_tiling (MetaWindow         *window,
raveit65 3243dae
+                  ConstraintInfo     *info,
raveit65 3243dae
+                  ConstraintPriority  priority,
raveit65 3243dae
+                  gboolean            check_only)
raveit65 3243dae
+{
raveit65 3243dae
+  MetaRectangle target_size;
raveit65 3243dae
+  MetaRectangle min_size, max_size;
raveit65 3243dae
+  gboolean hminbad, vminbad;
raveit65 3243dae
+  gboolean horiz_equal, vert_equal;
raveit65 3243dae
+  gboolean constraint_already_satisfied;
raveit65 3243dae
+
raveit65 3243dae
+  if (priority > PRIORITY_TILING)
raveit65 3243dae
+    return TRUE;
raveit65 3243dae
+
raveit65 3243dae
+  /* Determine whether constraint applies; exit if it doesn't */
raveit65 3243dae
+  if (!META_WINDOW_TILED (window))
raveit65 3243dae
+    return TRUE;
raveit65 3243dae
+
raveit65 3243dae
+  /* Calculate target_size - as the tile previews need this as well, we
raveit65 3243dae
+   * use an external function for the actual calculation
raveit65 3243dae
+   */
raveit65 3243dae
+  meta_window_get_current_tile_area (window, &target_size);
raveit65 3243dae
+  unextend_by_frame (&target_size, info->fgeom);
raveit65 3243dae
+
raveit65 3243dae
+  /* Check min size constraints; max size constraints are ignored as for
raveit65 3243dae
+   * maximized windows.
raveit65 3243dae
+   */
raveit65 3243dae
+  get_size_limits (window, info->fgeom, FALSE, &min_size, &max_size);
raveit65 3243dae
+  hminbad = target_size.width < min_size.width;
raveit65 3243dae
+  vminbad = target_size.height < min_size.height;
raveit65 3243dae
+  if (hminbad || vminbad)
raveit65 3243dae
+    return TRUE;
raveit65 3243dae
+
raveit65 3243dae
+  /* Determine whether constraint is already satisfied; exit if it is */
raveit65 3243dae
+  horiz_equal = target_size.x      == info->current.x &&
raveit65 3243dae
+                target_size.width  == info->current.width;
raveit65 3243dae
+  vert_equal  = target_size.y      == info->current.y &&
raveit65 3243dae
+                target_size.height == info->current.height;
raveit65 3243dae
+  constraint_already_satisfied = horiz_equal && vert_equal;
raveit65 3243dae
+  if (check_only || constraint_already_satisfied)
raveit65 3243dae
+    return constraint_already_satisfied;
raveit65 3243dae
+
raveit65 3243dae
+  /*** Enforce constraint ***/
raveit65 3243dae
+  info->current.x      = target_size.x;
raveit65 3243dae
+  info->current.width  = target_size.width;
raveit65 3243dae
+  info->current.y      = target_size.y;
raveit65 3243dae
+  info->current.height = target_size.height;
raveit65 3243dae
+
raveit65 3243dae
+  return TRUE;
raveit65 3243dae
+}
raveit65 3243dae
+
raveit65 3243dae
+static gboolean
raveit65 3243dae
 constrain_fullscreen (MetaWindow         *window,
raveit65 3243dae
                       ConstraintInfo     *info,
raveit65 3243dae
                       ConstraintPriority  priority,
raveit65 3243dae
@@ -850,7 +909,7 @@ constrain_size_increments (MetaWindow         *window,
raveit65 3243dae
 
raveit65 3243dae
   /* Determine whether constraint applies; exit if it doesn't */
raveit65 3243dae
   if (META_WINDOW_MAXIMIZED (window) || window->fullscreen || 
raveit65 3243dae
-      info->action_type == ACTION_MOVE)
raveit65 3243dae
+      META_WINDOW_TILED (window) || info->action_type == ACTION_MOVE)
raveit65 3243dae
     return TRUE;
raveit65 3243dae
 
raveit65 3243dae
   /* Determine whether constraint is already satisfied; exit if it is */
raveit65 3243dae
@@ -981,7 +1040,7 @@ constrain_aspect_ratio (MetaWindow         *window,
raveit65 3243dae
   constraints_are_inconsistent = minr > maxr;
raveit65 3243dae
   if (constraints_are_inconsistent ||
raveit65 3243dae
       META_WINDOW_MAXIMIZED (window) || window->fullscreen || 
raveit65 3243dae
-      info->action_type == ACTION_MOVE)
raveit65 3243dae
+      META_WINDOW_TILED (window) || info->action_type == ACTION_MOVE)
raveit65 3243dae
     return TRUE;
raveit65 3243dae
 
raveit65 3243dae
   /* Determine whether constraint is already satisfied; exit if it is.  We
raveit65 3243dae
diff --git a/src/core/core.c b/src/core/core.c
raveit65 3243dae
index 76e5548..c8fa02b 100644
raveit65 3243dae
--- a/src/core/core.c
raveit65 3243dae
+++ b/src/core/core.c
raveit65 3243dae
@@ -28,6 +28,7 @@
raveit65 3243dae
 #include "frame-private.h"
raveit65 3243dae
 #include "workspace.h"
raveit65 3243dae
 #include "prefs.h"
raveit65 3243dae
+#include "errors.h"
raveit65 3243dae
 
raveit65 3243dae
 /* Looks up the MetaWindow representing the frame of the given X window.
raveit65 3243dae
  * Used as a helper function by a bunch of the functions below.
raveit65 3243dae
@@ -297,6 +298,35 @@ meta_core_user_lower_and_unfocus (Display *xdisplay,
raveit65 3243dae
 }
raveit65 3243dae
 
raveit65 3243dae
 void
raveit65 3243dae
+meta_core_lower_beneath_focus_window (Display *xdisplay,
raveit65 3243dae
+                                      Window   xwindow,
raveit65 3243dae
+                                      guint32  timestamp)
raveit65 3243dae
+{
raveit65 3243dae
+  XWindowChanges changes;
raveit65 3243dae
+  MetaDisplay *display;
raveit65 3243dae
+  MetaScreen *screen;
raveit65 3243dae
+  MetaWindow *focus_window;
raveit65 3243dae
+
raveit65 3243dae
+  display = meta_display_for_x_display (xdisplay);
raveit65 3243dae
+  screen = meta_display_screen_for_xwindow (display, xwindow);
raveit65 3243dae
+  focus_window = meta_stack_get_top (screen->stack);
raveit65 3243dae
+
raveit65 3243dae
+  if (focus_window == NULL)
raveit65 3243dae
+    return;
raveit65 3243dae
+
raveit65 3243dae
+  changes.stack_mode = Below;
raveit65 3243dae
+  changes.sibling = focus_window->frame ? focus_window->frame->xwindow
raveit65 3243dae
+                                        : focus_window->xwindow;
raveit65 3243dae
+
raveit65 3243dae
+  meta_error_trap_push (display);
raveit65 3243dae
+  XConfigureWindow (xdisplay,
raveit65 3243dae
+                    xwindow,
raveit65 3243dae
+                    CWSibling | CWStackMode,
raveit65 3243dae
+                    &changes);
raveit65 3243dae
+  meta_error_trap_pop (display, FALSE);
raveit65 3243dae
+}
raveit65 3243dae
+
raveit65 3243dae
+void
raveit65 3243dae
 meta_core_user_focus (Display *xdisplay,
raveit65 3243dae
                       Window   frame_xwindow,
raveit65 3243dae
                       guint32  timestamp)
raveit65 3243dae
diff --git a/src/core/prefs.c b/src/core/prefs.c
raveit65 3243dae
index 116a9bb..5f46b55 100644
raveit65 3243dae
--- a/src/core/prefs.c
raveit65 3243dae
+++ b/src/core/prefs.c
raveit65 3243dae
@@ -117,6 +117,7 @@ static gboolean compositing_fast_alt_tab = FALSE;
raveit65 3243dae
 static gboolean resize_with_right_button = FALSE;
raveit65 3243dae
 static gboolean center_new_windows = FALSE;
raveit65 3243dae
 static gboolean force_fullscreen = TRUE;
raveit65 3243dae
+static gboolean side_by_side_tiling = FALSE;
raveit65 3243dae
 
raveit65 3243dae
 static MetaVisualBellType visual_bell_type = META_VISUAL_BELL_FULLSCREEN_FLASH;
raveit65 3243dae
 static MetaButtonLayout button_layout;
raveit65 3243dae
@@ -401,6 +402,12 @@ static MetaBoolPreference preferences_bool[] =
raveit65 3243dae
       &center_new_windows,
raveit65 3243dae
       FALSE,
raveit65 3243dae
     },
raveit65 3243dae
+    { "side-by-side-tiling",
raveit65 3243dae
+      KEY_GENERAL_SCHEMA,
raveit65 3243dae
+      META_PREF_SIDE_BY_SIDE_TILING,
raveit65 3243dae
+      &side_by_side_tiling,
raveit65 3243dae
+      FALSE,
raveit65 3243dae
+    },
raveit65 3243dae
     { NULL, NULL, 0, NULL, FALSE },
raveit65 3243dae
   };
raveit65 3243dae
 
raveit65 3243dae
@@ -1545,6 +1552,9 @@ meta_preference_to_string (MetaPreference pref)
raveit65 3243dae
 
raveit65 3243dae
     case META_PREF_FORCE_FULLSCREEN:
raveit65 3243dae
       return "FORCE_FULLSCREEN";
raveit65 3243dae
+
raveit65 3243dae
+    case META_PREF_SIDE_BY_SIDE_TILING:
raveit65 3243dae
+      return "SIDE_BY_SIDE_TILING";
raveit65 3243dae
     }
raveit65 3243dae
 
raveit65 3243dae
   return "(unknown)";
raveit65 3243dae
@@ -2202,6 +2212,12 @@ meta_prefs_get_center_new_windows (void)
raveit65 3243dae
     return center_new_windows;
raveit65 3243dae
 }
raveit65 3243dae
 
raveit65 3243dae
+gboolean
raveit65 3243dae
+meta_prefs_get_side_by_side_tiling ()
raveit65 3243dae
+{
raveit65 3243dae
+  return side_by_side_tiling;
raveit65 3243dae
+}
raveit65 3243dae
+
raveit65 3243dae
 guint
raveit65 3243dae
 meta_prefs_get_mouse_button_resize (void)
raveit65 3243dae
 {
raveit65 3243dae
diff --git a/src/core/screen-private.h b/src/core/screen-private.h
raveit65 3243dae
index 77ea457..8eb02d0 100644
raveit65 3243dae
--- a/src/core/screen-private.h
raveit65 3243dae
+++ b/src/core/screen-private.h
raveit65 3243dae
@@ -79,6 +79,9 @@ struct _MetaScreen
raveit65 3243dae
   MetaRectangle rect;  /* Size of screen; rect.x & rect.y are always 0 */
raveit65 3243dae
   MetaUI *ui;
raveit65 3243dae
   MetaTabPopup *tab_popup;
raveit65 3243dae
+  MetaTilePreview *tile_preview;
raveit65 3243dae
+  
raveit65 3243dae
+  guint tile_preview_timeout_id;
raveit65 3243dae
   
raveit65 3243dae
   MetaWorkspace *active_workspace;
raveit65 3243dae
 
raveit65 3243dae
@@ -160,6 +163,8 @@ void          meta_screen_ensure_tab_popup    (MetaScreen                 *scree
raveit65 3243dae
                                                MetaTabList                 list_type,
raveit65 3243dae
                                                MetaTabShowType             show_type);
raveit65 3243dae
 void          meta_screen_ensure_workspace_popup (MetaScreen *screen);
raveit65 3243dae
+void          meta_screen_tile_preview_update          (MetaScreen    *screen,
raveit65 3243dae
+                                                        gboolean       delay);
raveit65 3243dae
 
raveit65 3243dae
 MetaWindow*   meta_screen_get_mouse_window     (MetaScreen                 *screen,
raveit65 3243dae
                                                 MetaWindow                 *not_this_one);
raveit65 3243dae
diff --git a/src/core/screen.c b/src/core/screen.c
raveit65 3243dae
index e8fce40..eefe58f 100644
raveit65 3243dae
--- a/src/core/screen.c
raveit65 3243dae
+++ b/src/core/screen.c
raveit65 3243dae
@@ -582,7 +582,10 @@ meta_screen_new (MetaDisplay *display,
raveit65 3243dae
                             screen->xscreen);
raveit65 3243dae
 
raveit65 3243dae
   screen->tab_popup = NULL;
raveit65 3243dae
-  
raveit65 3243dae
+  screen->tile_preview = NULL;
raveit65 3243dae
+
raveit65 3243dae
+  screen->tile_preview_timeout_id = 0;
raveit65 3243dae
+
raveit65 3243dae
   screen->stack = meta_stack_new (screen);
raveit65 3243dae
 
raveit65 3243dae
   meta_prefs_add_listener (prefs_changed_callback, screen);
raveit65 3243dae
@@ -691,7 +694,13 @@ meta_screen_free (MetaScreen *screen,
raveit65 3243dae
   
raveit65 3243dae
   if (screen->xinerama_infos)
raveit65 3243dae
     g_free (screen->xinerama_infos);
raveit65 3243dae
-  
raveit65 3243dae
+
raveit65 3243dae
+  if (screen->tile_preview_timeout_id)
raveit65 3243dae
+    g_source_remove (screen->tile_preview_timeout_id);
raveit65 3243dae
+
raveit65 3243dae
+  if (screen->tile_preview)
raveit65 3243dae
+    meta_tile_preview_free (screen->tile_preview);
raveit65 3243dae
+
raveit65 3243dae
   g_free (screen->screen_name);
raveit65 3243dae
   g_free (screen);
raveit65 3243dae
 
raveit65 3243dae
@@ -1451,6 +1460,59 @@ meta_screen_ensure_workspace_popup (MetaScreen *screen)
raveit65 3243dae
   /* don't show tab popup, since proper space isn't selected yet */
raveit65 3243dae
 }
raveit65 3243dae
 
raveit65 3243dae
+static gboolean
raveit65 3243dae
+meta_screen_tile_preview_update_timeout (gpointer data)
raveit65 3243dae
+{
raveit65 3243dae
+  MetaScreen *screen = data;
raveit65 3243dae
+  MetaWindow *window = screen->display->grab_window;
raveit65 3243dae
+  gboolean composited = screen->display->compositor != NULL;
raveit65 3243dae
+
raveit65 3243dae
+  screen->tile_preview_timeout_id = 0;
raveit65 3243dae
+
raveit65 3243dae
+  if (!screen->tile_preview)
raveit65 3243dae
+    screen->tile_preview = meta_tile_preview_new (screen->number,
raveit65 3243dae
+                                                  composited);
raveit65 3243dae
+
raveit65 3243dae
+  if (window
raveit65 3243dae
+      && !META_WINDOW_TILED (window)
raveit65 3243dae
+      && window->tile_mode != META_TILE_NONE)
raveit65 3243dae
+    {
raveit65 3243dae
+      MetaRectangle tile_rect;
raveit65 3243dae
+
raveit65 3243dae
+      meta_window_get_current_tile_area (window, &tile_rect);
raveit65 3243dae
+      meta_tile_preview_show (screen->tile_preview, &tile_rect);
raveit65 3243dae
+    }
raveit65 3243dae
+  else
raveit65 3243dae
+    meta_tile_preview_hide (screen->tile_preview);
raveit65 3243dae
+
raveit65 3243dae
+  return FALSE;
raveit65 3243dae
+}
raveit65 3243dae
+
raveit65 3243dae
+#define TILE_PREVIEW_TIMEOUT_MS 200
raveit65 3243dae
+
raveit65 3243dae
+void
raveit65 3243dae
+meta_screen_tile_preview_update (MetaScreen *screen,
raveit65 3243dae
+                                 gboolean    delay)
raveit65 3243dae
+{
raveit65 3243dae
+  if (delay)
raveit65 3243dae
+    {
raveit65 3243dae
+      if (screen->tile_preview_timeout_id > 0)
raveit65 3243dae
+        return;
raveit65 3243dae
+
raveit65 3243dae
+      screen->tile_preview_timeout_id =
raveit65 3243dae
+        g_timeout_add (TILE_PREVIEW_TIMEOUT_MS,
raveit65 3243dae
+                       meta_screen_tile_preview_update_timeout,
raveit65 3243dae
+                       screen);
raveit65 3243dae
+    }
raveit65 3243dae
+  else
raveit65 3243dae
+    {
raveit65 3243dae
+      if (screen->tile_preview_timeout_id > 0)
raveit65 3243dae
+        g_source_remove (screen->tile_preview_timeout_id);
raveit65 3243dae
+
raveit65 3243dae
+      meta_screen_tile_preview_update_timeout ((gpointer)screen);
raveit65 3243dae
+    }
raveit65 3243dae
+}
raveit65 3243dae
+
raveit65 3243dae
 MetaWindow*
raveit65 3243dae
 meta_screen_get_mouse_window (MetaScreen  *screen,
raveit65 3243dae
                               MetaWindow  *not_this_one)
raveit65 3243dae
diff --git a/src/core/window-private.h b/src/core/window-private.h
raveit65 3243dae
index 447a2c4..02c518e 100644
raveit65 3243dae
--- a/src/core/window-private.h
raveit65 3243dae
+++ b/src/core/window-private.h
raveit65 3243dae
@@ -83,6 +83,12 @@ typedef enum {
raveit65 3243dae
 
raveit65 3243dae
 #define NUMBER_OF_QUEUES 3
raveit65 3243dae
 
raveit65 3243dae
+typedef enum {
raveit65 3243dae
+  META_TILE_NONE,
raveit65 3243dae
+  META_TILE_LEFT,
raveit65 3243dae
+  META_TILE_RIGHT
raveit65 3243dae
+} MetaTileMode;
raveit65 3243dae
+
raveit65 3243dae
 struct _MetaWindow
raveit65 3243dae
 {
raveit65 3243dae
   MetaDisplay *display;
raveit65 3243dae
@@ -138,6 +144,11 @@ struct _MetaWindow
raveit65 3243dae
   guint maximize_vertically_after_placement : 1;
raveit65 3243dae
   guint minimize_after_placement : 1;
raveit65 3243dae
 
raveit65 3243dae
+  /* The current or requested tile mode. If maximized_vertically is true,
raveit65 3243dae
+   * this is the current mode. If not, it is the mode which will be
raveit65 3243dae
+   * requested after the window grab is released */
raveit65 3243dae
+  guint tile_mode : 2;
raveit65 3243dae
+
raveit65 3243dae
   /* Whether we're shaded */
raveit65 3243dae
   guint shaded : 1;
raveit65 3243dae
 
raveit65 3243dae
@@ -383,8 +394,11 @@ struct _MetaWindow
raveit65 3243dae
                                         (w)->maximized_vertically)
raveit65 3243dae
 #define META_WINDOW_MAXIMIZED_VERTICALLY(w)    ((w)->maximized_vertically)
raveit65 3243dae
 #define META_WINDOW_MAXIMIZED_HORIZONTALLY(w)  ((w)->maximized_horizontally)
raveit65 3243dae
+#define META_WINDOW_TILED(w)           ((w)->maximized_vertically && \
raveit65 3243dae
+                                        !(w)->maximized_horizontally && \
raveit65 3243dae
+                                        (w)->tile_mode != META_TILE_NONE)
raveit65 3243dae
 #define META_WINDOW_ALLOWS_MOVE(w)     ((w)->has_move_func && !(w)->fullscreen)
raveit65 3243dae
-#define META_WINDOW_ALLOWS_RESIZE_EXCEPT_HINTS(w)   ((w)->has_resize_func && !META_WINDOW_MAXIMIZED (w) && !(w)->fullscreen && !(w)->shaded)
raveit65 3243dae
+#define META_WINDOW_ALLOWS_RESIZE_EXCEPT_HINTS(w)   ((w)->has_resize_func && !META_WINDOW_MAXIMIZED (w) && !META_WINDOW_TILED(w) && !(w)->fullscreen && !(w)->shaded)
raveit65 3243dae
 #define META_WINDOW_ALLOWS_RESIZE(w)   (META_WINDOW_ALLOWS_RESIZE_EXCEPT_HINTS (w) &&                \
raveit65 3243dae
                                         (((w)->size_hints.min_width < (w)->size_hints.max_width) ||  \
raveit65 3243dae
                                          ((w)->size_hints.min_height < (w)->size_hints.max_height)))
raveit65 3243dae
@@ -575,6 +589,8 @@ void meta_window_get_work_area_for_xinerama     (MetaWindow    *window,
raveit65 3243dae
 void meta_window_get_work_area_all_xineramas    (MetaWindow    *window,
raveit65 3243dae
                                                  MetaRectangle *area);
raveit65 3243dae
 
raveit65 3243dae
+void meta_window_get_current_tile_area         (MetaWindow    *window,
raveit65 3243dae
+                                                MetaRectangle *tile_area);
raveit65 3243dae
 
raveit65 3243dae
 gboolean meta_window_same_application (MetaWindow *window,
raveit65 3243dae
                                        MetaWindow *other_window);
raveit65 3243dae
diff --git a/src/core/window.c b/src/core/window.c
raveit65 3243dae
index d997bae..7a7f6be 100644
raveit65 3243dae
--- a/src/core/window.c
raveit65 3243dae
+++ b/src/core/window.c
raveit65 3243dae
@@ -470,6 +470,7 @@ meta_window_new_with_attrs (MetaDisplay       *display,
raveit65 3243dae
   window->require_on_single_xinerama = TRUE;
raveit65 3243dae
   window->require_titlebar_visible = TRUE;
raveit65 3243dae
   window->on_all_workspaces = FALSE;
raveit65 3243dae
+  window->tile_mode = META_TILE_NONE;
raveit65 3243dae
   window->shaded = FALSE;
raveit65 3243dae
   window->initially_iconic = FALSE;
raveit65 3243dae
   window->minimized = FALSE;
raveit65 3243dae
@@ -2489,7 +2490,7 @@ ensure_size_hints_satisfied (MetaRectangle    *rect,
raveit65 3243dae
 static void
raveit65 3243dae
 meta_window_save_rect (MetaWindow *window)
raveit65 3243dae
 {
raveit65 3243dae
-  if (!(META_WINDOW_MAXIMIZED (window) || window->fullscreen))
raveit65 3243dae
+  if (!(META_WINDOW_MAXIMIZED (window) || META_WINDOW_TILED (window) || window->fullscreen))
raveit65 3243dae
     {
raveit65 3243dae
       /* save size/pos as appropriate args for move_resize */
raveit65 3243dae
       if (!window->maximized_horizontally)
raveit65 3243dae
@@ -2531,7 +2532,7 @@ force_save_user_window_placement (MetaWindow *window)
raveit65 3243dae
 static void
raveit65 3243dae
 save_user_window_placement (MetaWindow *window)
raveit65 3243dae
 {
raveit65 3243dae
-  if (!(META_WINDOW_MAXIMIZED (window) || window->fullscreen))
raveit65 3243dae
+  if (!(META_WINDOW_MAXIMIZED (window) || META_WINDOW_TILED (window) || window->fullscreen))
raveit65 3243dae
     {
raveit65 3243dae
       MetaRectangle user_rect;
raveit65 3243dae
 
raveit65 3243dae
@@ -2596,6 +2597,7 @@ void
raveit65 3243dae
 meta_window_maximize (MetaWindow        *window,
raveit65 3243dae
                       MetaMaximizeFlags  directions)
raveit65 3243dae
 {
raveit65 3243dae
+  MetaRectangle *saved_rect = NULL;
raveit65 3243dae
   /* At least one of the two directions ought to be set */
raveit65 3243dae
   gboolean maximize_horizontally, maximize_vertically;
raveit65 3243dae
   maximize_horizontally = directions & META_MAXIMIZE_HORIZONTAL;
raveit65 3243dae
@@ -2631,9 +2633,16 @@ meta_window_maximize (MetaWindow        *window,
raveit65 3243dae
 	  return;
raveit65 3243dae
 	}
raveit65 3243dae
 
raveit65 3243dae
+      if (window->tile_mode != META_TILE_NONE)
raveit65 3243dae
+        {
raveit65 3243dae
+          saved_rect = &window->saved_rect;
raveit65 3243dae
+
raveit65 3243dae
+          window->maximized_vertically = FALSE;
raveit65 3243dae
+        }
raveit65 3243dae
+
raveit65 3243dae
       meta_window_maximize_internal (window,
raveit65 3243dae
                                      directions,
raveit65 3243dae
-                                     NULL);
raveit65 3243dae
+                                     saved_rect);
raveit65 3243dae
 
raveit65 3243dae
       /* move_resize with new maximization constraints
raveit65 3243dae
        */
raveit65 3243dae
@@ -2673,12 +2682,64 @@ unmaximize_window_before_freeing (MetaWindow        *window)
raveit65 3243dae
     }
raveit65 3243dae
 }
raveit65 3243dae
 
raveit65 3243dae
+static void
raveit65 3243dae
+meta_window_tile (MetaWindow *window)
raveit65 3243dae
+{
raveit65 3243dae
+  /* Don't do anything if no tiling is requested */
raveit65 3243dae
+  if (window->tile_mode == META_TILE_NONE)
raveit65 3243dae
+    return;
raveit65 3243dae
+
raveit65 3243dae
+  meta_window_maximize_internal (window, META_MAXIMIZE_VERTICAL, NULL);
raveit65 3243dae
+  meta_screen_tile_preview_update (window->screen, FALSE);
raveit65 3243dae
+
raveit65 3243dae
+  /* move_resize with new tiling constraints
raveit65 3243dae
+   */
raveit65 3243dae
+  meta_window_queue (window, META_QUEUE_MOVE_RESIZE);
raveit65 3243dae
+}
raveit65 3243dae
+
raveit65 3243dae
+static gboolean
raveit65 3243dae
+meta_window_can_tile (MetaWindow *window)
raveit65 3243dae
+{
raveit65 3243dae
+  const MetaXineramaScreenInfo *monitor;
raveit65 3243dae
+  MetaRectangle tile_area;
raveit65 3243dae
+
raveit65 3243dae
+  if (!META_WINDOW_ALLOWS_RESIZE (window))
raveit65 3243dae
+    return FALSE;
raveit65 3243dae
+
raveit65 3243dae
+  monitor = meta_screen_get_current_xinerama (window->screen);
raveit65 3243dae
+  meta_window_get_work_area_for_xinerama (window, monitor->number, &tile_area);
raveit65 3243dae
+
raveit65 3243dae
+  tile_area.width /= 2;
raveit65 3243dae
+
raveit65 3243dae
+  if (window->frame)
raveit65 3243dae
+    {
raveit65 3243dae
+      MetaFrameGeometry fgeom;
raveit65 3243dae
+
raveit65 3243dae
+      meta_frame_calc_geometry (window->frame, &fgeom);
raveit65 3243dae
+
raveit65 3243dae
+      tile_area.width  -= (fgeom.left_width + fgeom.right_width);
raveit65 3243dae
+      tile_area.height -= (fgeom.top_height + fgeom.bottom_height);
raveit65 3243dae
+    }
raveit65 3243dae
+
raveit65 3243dae
+  return tile_area.width >= window->size_hints.min_width &&
raveit65 3243dae
+         tile_area.height >= window->size_hints.min_height;
raveit65 3243dae
+}
raveit65 3243dae
+
raveit65 3243dae
 void
raveit65 3243dae
 meta_window_unmaximize (MetaWindow        *window,
raveit65 3243dae
                         MetaMaximizeFlags  directions)
raveit65 3243dae
 {
raveit65 3243dae
   /* At least one of the two directions ought to be set */
raveit65 3243dae
   gboolean unmaximize_horizontally, unmaximize_vertically;
raveit65 3243dae
+
raveit65 3243dae
+  /* Restore tiling if necessary */
raveit65 3243dae
+  if (window->tile_mode != META_TILE_NONE)
raveit65 3243dae
+    {
raveit65 3243dae
+      window->maximized_horizontally = FALSE;
raveit65 3243dae
+      meta_window_tile (window);
raveit65 3243dae
+      return;
raveit65 3243dae
+    }
raveit65 3243dae
+
raveit65 3243dae
   unmaximize_horizontally = directions & META_MAXIMIZE_HORIZONTAL;
raveit65 3243dae
   unmaximize_vertically   = directions & META_MAXIMIZE_VERTICAL;
raveit65 3243dae
   g_assert (unmaximize_horizontally || unmaximize_vertically);
raveit65 3243dae
@@ -2723,17 +2784,6 @@ meta_window_unmaximize (MetaWindow        *window,
raveit65 3243dae
        */
raveit65 3243dae
       ensure_size_hints_satisfied (&target_rect, &window->size_hints);
raveit65 3243dae
 
raveit65 3243dae
-      /* When we unmaximize, if we're doing a mouse move also we could
raveit65 3243dae
-       * get the window suddenly jumping to the upper left corner of
raveit65 3243dae
-       * the workspace, since that's where it was when the grab op
raveit65 3243dae
-       * started.  So we need to update the grab state.
raveit65 3243dae
-       */
raveit65 3243dae
-      if (meta_grab_op_is_moving (window->display->grab_op) &&
raveit65 3243dae
-          window->display->grab_window == window)
raveit65 3243dae
-        {
raveit65 3243dae
-          window->display->grab_anchor_window_pos = target_rect;
raveit65 3243dae
-        }
raveit65 3243dae
-
raveit65 3243dae
       meta_window_move_resize (window,
raveit65 3243dae
                                FALSE,
raveit65 3243dae
                                target_rect.x,
raveit65 3243dae
@@ -2745,6 +2795,19 @@ meta_window_unmaximize (MetaWindow        *window,
raveit65 3243dae
        */
raveit65 3243dae
       force_save_user_window_placement (window);
raveit65 3243dae
 
raveit65 3243dae
+      /* When we unmaximize, if we're doing a mouse move also we could
raveit65 3243dae
+       * get the window suddenly jumping to the upper left corner of
raveit65 3243dae
+       * the workspace, since that's where it was when the grab op
raveit65 3243dae
+       * started.  So we need to update the grab state. We have to do
raveit65 3243dae
+       * it after the actual operation, as the window may have been moved
raveit65 3243dae
+       * by constraints.
raveit65 3243dae
+       */
raveit65 3243dae
+      if (meta_grab_op_is_moving (window->display->grab_op) &&
raveit65 3243dae
+          window->display->grab_window == window)
raveit65 3243dae
+        {
raveit65 3243dae
+          window->display->grab_anchor_window_pos = window->user_rect;
raveit65 3243dae
+        }
raveit65 3243dae
+
raveit65 3243dae
       if (window->display->grab_wireframe_active)
raveit65 3243dae
         {
raveit65 3243dae
           window->display->grab_wireframe_rect = target_rect;
raveit65 3243dae
@@ -6898,20 +6961,58 @@ update_move (MetaWindow  *window,
raveit65 3243dae
   if (dx == 0 && dy == 0)
raveit65 3243dae
     return;
raveit65 3243dae
 
raveit65 3243dae
-  /* shake loose (unmaximize) maximized window if dragged beyond the threshold
raveit65 3243dae
-   * in the Y direction. You can't pull a window loose via X motion.
raveit65 3243dae
+  /* Originally for detaching maximized windows, but we use this
raveit65 3243dae
+   * for the zones at the sides of the monitor where trigger tiling
raveit65 3243dae
+   * because it's about the right size
raveit65 3243dae
    */
raveit65 3243dae
 
raveit65 3243dae
 #define DRAG_THRESHOLD_TO_SHAKE_THRESHOLD_FACTOR 6
raveit65 3243dae
   shake_threshold = meta_ui_get_drag_threshold (window->screen->ui) *
raveit65 3243dae
     DRAG_THRESHOLD_TO_SHAKE_THRESHOLD_FACTOR;
raveit65 3243dae
 
raveit65 3243dae
-  if (META_WINDOW_MAXIMIZED (window) && ABS (dy) >= shake_threshold)
raveit65 3243dae
+
raveit65 3243dae
+  if (meta_prefs_get_side_by_side_tiling () &&
raveit65 3243dae
+      meta_window_can_tile (window))
raveit65 3243dae
+    {
raveit65 3243dae
+      const MetaXineramaScreenInfo *monitor;
raveit65 3243dae
+      MetaRectangle work_area;
raveit65 3243dae
+
raveit65 3243dae
+      /* For tiling we are interested in the work area of the monitor where
raveit65 3243dae
+       * the pointer is located.
raveit65 3243dae
+       * Also see comment in meta_window_get_current_tile_area()
raveit65 3243dae
+       */
raveit65 3243dae
+      monitor = meta_screen_get_current_xinerama (window->screen);
raveit65 3243dae
+      meta_window_get_work_area_for_xinerama (window,
raveit65 3243dae
+                                              monitor->number,
raveit65 3243dae
+                                              &work_area);
raveit65 3243dae
+
raveit65 3243dae
+      if (y >= monitor->rect.y &&
raveit65 3243dae
+          y < (monitor->rect.y + monitor->rect.height))
raveit65 3243dae
+        {
raveit65 3243dae
+          /* check if cursor is near an edge of the work area */
raveit65 3243dae
+          if (x >= monitor->rect.x && x < (work_area.x + shake_threshold))
raveit65 3243dae
+            window->tile_mode = META_TILE_LEFT;
raveit65 3243dae
+          else if (x >= work_area.x + work_area.width - shake_threshold &&
raveit65 3243dae
+                   x < (monitor->rect.x + monitor->rect.width))
raveit65 3243dae
+            window->tile_mode = META_TILE_RIGHT;
raveit65 3243dae
+          else
raveit65 3243dae
+            window->tile_mode = META_TILE_NONE;
raveit65 3243dae
+        }
raveit65 3243dae
+    }
raveit65 3243dae
+
raveit65 3243dae
+  /* shake loose (unmaximize) maximized or tiled window if dragged beyond
raveit65 3243dae
+   * the threshold in the Y direction. Tiled windows can also be pulled
raveit65 3243dae
+   * loose via X motion.
raveit65 3243dae
+   */
raveit65 3243dae
+
raveit65 3243dae
+  if ((META_WINDOW_MAXIMIZED (window) && ABS (dy) >= shake_threshold) ||
raveit65 3243dae
+      (META_WINDOW_TILED (window) && (MAX (ABS (dx), ABS (dy)) >= shake_threshold)))
raveit65 3243dae
     {
raveit65 3243dae
       double prop;
raveit65 3243dae
 
raveit65 3243dae
       /* Shake loose */
raveit65 3243dae
-      window->shaken_loose = TRUE;
raveit65 3243dae
+      window->shaken_loose = META_WINDOW_MAXIMIZED (window);
raveit65 3243dae
+      window->tile_mode = META_TILE_NONE;
raveit65 3243dae
 
raveit65 3243dae
       /* move the unmaximized window to the cursor */
raveit65 3243dae
       prop =
raveit65 3243dae
@@ -6995,13 +7096,20 @@ update_move (MetaWindow  *window,
raveit65 3243dae
         }
raveit65 3243dae
     }
raveit65 3243dae
 
raveit65 3243dae
+  /* Delay showing the tile preview slightly to make it more unlikely to
raveit65 3243dae
+   * trigger it unwittingly, e.g. when shaking loose the window or moving
raveit65 3243dae
+   * it to another monitor.
raveit65 3243dae
+   */
raveit65 3243dae
+  meta_screen_tile_preview_update (window->screen,
raveit65 3243dae
+                                   window->tile_mode != META_TILE_NONE);
raveit65 3243dae
+
raveit65 3243dae
   if (display->grab_wireframe_active)
raveit65 3243dae
     old = display->grab_wireframe_rect;
raveit65 3243dae
   else
raveit65 3243dae
     meta_window_get_client_root_coords (window, &old;;
raveit65 3243dae
 
raveit65 3243dae
-  /* Don't allow movement in the maximized directions */
raveit65 3243dae
-  if (window->maximized_horizontally)
raveit65 3243dae
+  /* Don't allow movement in the maximized directions or while tiled */
raveit65 3243dae
+  if (window->maximized_horizontally || META_WINDOW_TILED (window))
raveit65 3243dae
     new_x = old.x;
raveit65 3243dae
   if (window->maximized_vertically)
raveit65 3243dae
     new_y = old.y;
raveit65 3243dae
@@ -7417,7 +7525,9 @@ meta_window_handle_mouse_grab_op_event (MetaWindow *window,
raveit65 3243dae
         {
raveit65 3243dae
           if (meta_grab_op_is_moving (window->display->grab_op))
raveit65 3243dae
             {
raveit65 3243dae
-              if (event->xbutton.root == window->screen->xroot)
raveit65 3243dae
+              if (window->tile_mode != META_TILE_NONE)
raveit65 3243dae
+                meta_window_tile (window);
raveit65 3243dae
+              else if (event->xbutton.root == window->screen->xroot)
raveit65 3243dae
                 update_move (window, event->xbutton.state & ShiftMask,
raveit65 3243dae
                              event->xbutton.x_root, event->xbutton.y_root);
raveit65 3243dae
             }
raveit65 3243dae
@@ -7575,6 +7685,29 @@ meta_window_get_work_area_all_xineramas (MetaWindow    *window,
raveit65 3243dae
               window->desc, area->x, area->y, area->width, area->height);
raveit65 3243dae
 }
raveit65 3243dae
 
raveit65 3243dae
+void
raveit65 3243dae
+meta_window_get_current_tile_area (MetaWindow    *window,
raveit65 3243dae
+                                   MetaRectangle *tile_area)
raveit65 3243dae
+{
raveit65 3243dae
+  const MetaXineramaScreenInfo *monitor;
raveit65 3243dae
+
raveit65 3243dae
+  g_return_if_fail (window->tile_mode != META_TILE_NONE);
raveit65 3243dae
+
raveit65 3243dae
+  /* The definition of "current" of meta_window_get_work_area_current_xinerama()
raveit65 3243dae
+   * and meta_screen_get_current_xinerama() is slightly different: the former
raveit65 3243dae
+   * refers to the monitor which contains the largest part of the window, the
raveit65 3243dae
+   * latter to the one where the pointer is located.
raveit65 3243dae
+   */
raveit65 3243dae
+  monitor = meta_screen_get_current_xinerama (window->screen);
raveit65 3243dae
+  meta_window_get_work_area_for_xinerama (window, monitor->number, tile_area);
raveit65 3243dae
+
raveit65 3243dae
+  if (window->tile_mode == META_TILE_LEFT  ||
raveit65 3243dae
+      window->tile_mode == META_TILE_RIGHT)
raveit65 3243dae
+    tile_area->width /= 2;
raveit65 3243dae
+
raveit65 3243dae
+  if (window->tile_mode == META_TILE_RIGHT)
raveit65 3243dae
+    tile_area->x += tile_area->width;
raveit65 3243dae
+}
raveit65 3243dae
 
raveit65 3243dae
 gboolean
raveit65 3243dae
 meta_window_same_application (MetaWindow *window,
raveit65 3243dae
diff --git a/src/include/core.h b/src/include/core.h
raveit65 3243dae
index 66db2f8..14c1c15 100644
raveit65 3243dae
--- a/src/include/core.h
raveit65 3243dae
+++ b/src/include/core.h
raveit65 3243dae
@@ -116,6 +116,10 @@ void meta_core_user_focus   (Display *xdisplay,
raveit65 3243dae
                              Window   frame_xwindow,
raveit65 3243dae
                              guint32  timestamp);
raveit65 3243dae
 
raveit65 3243dae
+void meta_core_lower_beneath_focus_window (Display *xdisplay,
raveit65 3243dae
+                                           Window   xwindow,
raveit65 3243dae
+                                           guint32  timestamp);
raveit65 3243dae
+
raveit65 3243dae
 void meta_core_minimize         (Display *xdisplay,
raveit65 3243dae
                                  Window   frame_xwindow);
raveit65 3243dae
 void meta_core_toggle_maximize  (Display *xdisplay,
raveit65 3243dae
diff --git a/src/include/prefs.h b/src/include/prefs.h
raveit65 3243dae
index 2b7cfe4..4856d58 100644
raveit65 3243dae
--- a/src/include/prefs.h
raveit65 3243dae
+++ b/src/include/prefs.h
raveit65 3243dae
@@ -63,6 +63,7 @@ typedef enum
raveit65 3243dae
   META_PREF_COMPOSITING_FAST_ALT_TAB,
raveit65 3243dae
   META_PREF_RESIZE_WITH_RIGHT_BUTTON,
raveit65 3243dae
   META_PREF_CENTER_NEW_WINDOWS,
raveit65 3243dae
+  META_PREF_SIDE_BY_SIDE_TILING,
raveit65 3243dae
   META_PREF_FORCE_FULLSCREEN
raveit65 3243dae
 } MetaPreference;
raveit65 3243dae
 
raveit65 3243dae
@@ -95,6 +96,7 @@ MetaWrapStyle               meta_prefs_get_wrap_style         (void);
raveit65 3243dae
 gboolean                    meta_prefs_get_reduced_resources  (void);
raveit65 3243dae
 gboolean                    meta_prefs_get_mate_accessibility (void);
raveit65 3243dae
 gboolean                    meta_prefs_get_mate_animations   (void);
raveit65 3243dae
+gboolean                    meta_prefs_get_side_by_side_tiling (void);
raveit65 3243dae
 
raveit65 3243dae
 const char*                 meta_prefs_get_command            (int i);
raveit65 3243dae
 
raveit65 3243dae
diff --git a/src/include/tile-preview.h b/src/include/tile-preview.h
raveit65 3243dae
new file mode 100644
raveit65 3243dae
index 0000000..b0ca3b0
raveit65 3243dae
--- a/dev/null
raveit65 3243dae
+++ b/src/include/tile-preview.h
raveit65 3243dae
@@ -0,0 +1,37 @@
raveit65 3243dae
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
raveit65 3243dae
+
raveit65 3243dae
+/* Meta tile preview */
raveit65 3243dae
+
raveit65 3243dae
+/*
raveit65 3243dae
+ * Copyright (C) 2010 Florian Müllner
raveit65 3243dae
+ *
raveit65 3243dae
+ * This program is free software; you can redistribute it and/or
raveit65 3243dae
+ * modify it under the terms of the GNU General Public License as
raveit65 3243dae
+ * published by the Free Software Foundation; either version 2 of the
raveit65 3243dae
+ * License, or (at your option) any later version.
raveit65 3243dae
+ *
raveit65 3243dae
+ * This program is distributed in the hope that it will be useful, but
raveit65 3243dae
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
raveit65 3243dae
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
raveit65 3243dae
+ * General Public License for more details.
raveit65 3243dae
+ *
raveit65 3243dae
+ * You should have received a copy of the GNU General Public License
raveit65 3243dae
+ * along with this program; if not, write to the Free Software
raveit65 3243dae
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
raveit65 3243dae
+ * 02111-1307, USA.
raveit65 3243dae
+ */
raveit65 3243dae
+#ifndef META_TILE_PREVIEW_H
raveit65 3243dae
+#define META_TILE_PREVIEW_H
raveit65 3243dae
+
raveit65 3243dae
+#include "boxes.h"
raveit65 3243dae
+
raveit65 3243dae
+typedef struct _MetaTilePreview MetaTilePreview;
raveit65 3243dae
+
raveit65 3243dae
+MetaTilePreview   *meta_tile_preview_new    (int                screen_number,
raveit65 3243dae
+                                             gboolean           composited);
raveit65 3243dae
+void               meta_tile_preview_free   (MetaTilePreview   *preview);
raveit65 3243dae
+void               meta_tile_preview_show   (MetaTilePreview   *preview,
raveit65 3243dae
+                                             MetaRectangle     *rect);
raveit65 3243dae
+void               meta_tile_preview_hide   (MetaTilePreview   *preview);
raveit65 3243dae
+
raveit65 3243dae
+#endif /* META_TILE_PREVIEW_H */
raveit65 3243dae
\ No newline at end of file
raveit65 3243dae
diff --git a/src/include/ui.h b/src/include/ui.h
raveit65 3243dae
index a886649..2352519 100644
raveit65 3243dae
--- a/src/include/ui.h
raveit65 3243dae
+++ b/src/include/ui.h
raveit65 3243dae
@@ -205,5 +205,6 @@ MetaUIDirection meta_ui_get_direction (void);
raveit65 3243dae
 GdkPixbuf *meta_ui_get_pixbuf_from_pixmap (Pixmap   pmap);
raveit65 3243dae
 
raveit65 3243dae
 #include "tabpopup.h"
raveit65 3243dae
+#include "tile-preview.h"
raveit65 3243dae
 
raveit65 3243dae
 #endif
raveit65 3243dae
diff --git a/src/org.mate.marco.gschema.xml b/src/org.mate.marco.gschema.xml
raveit65 3243dae
index 464deb3..23b10de 100644
raveit65 3243dae
--- a/src/org.mate.marco.gschema.xml
raveit65 3243dae
+++ b/src/org.mate.marco.gschema.xml
raveit65 3243dae
@@ -166,6 +166,11 @@
raveit65 3243dae
       <summary>Determine if new windows are created on the center of the screen</summary>
raveit65 3243dae
       <description>By default, marco open new windows on the top left of the screen. If this option is enabled, new windows are open on the center of the screen, instead.</description>
raveit65 3243dae
     </key>
raveit65 3243dae
+    <key name="side-by-side-tiling" type="b">
raveit65 3243dae
+      <default>false</default>
raveit65 3243dae
+      <summary>Whether to enable side-by-side tiling</summary>
raveit65 3243dae
+      <description>If enabled, dropping windows on screen edges maximizes them vertically and resizes them horizontally to cover half of the available area. Drag-dropping to the top maximizes the window.</description>
raveit65 3243dae
+    </key>
raveit65 3243dae
   </schema>
raveit65 3243dae
 
raveit65 3243dae
   <schema id="org.mate.Marco.workspace-names" path="/org/mate/marco/workspace-names/">
raveit65 3243dae
diff --git a/src/ui/tile-preview.c b/src/ui/tile-preview.c
raveit65 3243dae
new file mode 100644
raveit65 3243dae
index 0000000..e782e6a
raveit65 3243dae
--- a/dev/null
raveit65 3243dae
+++ b/src/ui/tile-preview.c
raveit65 3243dae
@@ -0,0 +1,251 @@
raveit65 3243dae
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
raveit65 3243dae
+
raveit65 3243dae
+/* Mutter tile-preview marks the area a window will *ehm* snap to */
raveit65 3243dae
+
raveit65 3243dae
+/*
raveit65 3243dae
+ * Copyright (C) 2010 Florian Müllner
raveit65 3243dae
+ *
raveit65 3243dae
+ * This program is free software; you can redistribute it and/or
raveit65 3243dae
+ * modify it under the terms of the GNU General Public License as
raveit65 3243dae
+ * published by the Free Software Foundation; either version 2 of the
raveit65 3243dae
+ * License, or (at your option) any later version.
raveit65 3243dae
+ *
raveit65 3243dae
+ * This program is distributed in the hope that it will be useful, but
raveit65 3243dae
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
raveit65 3243dae
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
raveit65 3243dae
+ * General Public License for more details.
raveit65 3243dae
+ *
raveit65 3243dae
+ * You should have received a copy of the GNU General Public License
raveit65 3243dae
+ * along with this program; if not, write to the Free Software
raveit65 3243dae
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
raveit65 3243dae
+ * 02111-1307, USA.
raveit65 3243dae
+ */
raveit65 3243dae
+
raveit65 3243dae
+#include <config.h>
raveit65 3243dae
+
raveit65 3243dae
+#include <gtk/gtk.h>
raveit65 3243dae
+#include <cairo.h>
raveit65 3243dae
+
raveit65 3243dae
+#include "tile-preview.h"
raveit65 3243dae
+#include "core.h"
raveit65 3243dae
+
raveit65 3243dae
+#define OUTLINE_WIDTH 5  /* frame width in non-composite case */
raveit65 3243dae
+
raveit65 3243dae
+
raveit65 3243dae
+struct _MetaTilePreview {
raveit65 3243dae
+  GtkWidget     *preview_window;
raveit65 3243dae
+
raveit65 3243dae
+  GdkColor      *preview_color;
raveit65 3243dae
+  guchar         preview_alpha;
raveit65 3243dae
+
raveit65 3243dae
+  MetaRectangle  tile_rect;
raveit65 3243dae
+
raveit65 3243dae
+  gboolean       has_alpha: 1;
raveit65 3243dae
+};
raveit65 3243dae
+
raveit65 3243dae
+static gboolean
raveit65 3243dae
+meta_tile_preview_expose (GtkWidget      *widget,
raveit65 3243dae
+                          GdkEventExpose *event,
raveit65 3243dae
+                          gpointer        user_data)
raveit65 3243dae
+{
raveit65 3243dae
+  MetaTilePreview *preview = user_data;
raveit65 3243dae
+  GdkWindow *window;
raveit65 3243dae
+  cairo_t *cr;
raveit65 3243dae
+
raveit65 3243dae
+  window = gtk_widget_get_window (widget);
raveit65 3243dae
+  cr = gdk_cairo_create (window);
raveit65 3243dae
+
raveit65 3243dae
+  cairo_set_line_width (cr, 1.0);
raveit65 3243dae
+
raveit65 3243dae
+  if (preview->has_alpha)
raveit65 3243dae
+    {
raveit65 3243dae
+
raveit65 3243dae
+      /* Fill the preview area with a transparent color */
raveit65 3243dae
+      cairo_set_source_rgba (cr,
raveit65 3243dae
+                             (double)preview->preview_color->red   / 0xFFFF,
raveit65 3243dae
+                             (double)preview->preview_color->green / 0xFFFF,
raveit65 3243dae
+                             (double)preview->preview_color->blue  / 0xFFFF,
raveit65 3243dae
+                             (double)preview->preview_alpha / 0xFF);
raveit65 3243dae
+
raveit65 3243dae
+      cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
raveit65 3243dae
+      cairo_paint (cr);
raveit65 3243dae
+
raveit65 3243dae
+      /* Use the opaque color for the border */
raveit65 3243dae
+      gdk_cairo_set_source_color (cr, preview->preview_color);
raveit65 3243dae
+    }
raveit65 3243dae
+  else
raveit65 3243dae
+    {
raveit65 3243dae
+      GtkStyle *style = gtk_widget_get_style (preview->preview_window);
raveit65 3243dae
+
raveit65 3243dae
+      gdk_cairo_set_source_color (cr, &style->white);
raveit65 3243dae
+
raveit65 3243dae
+      cairo_rectangle (cr,
raveit65 3243dae
+                       OUTLINE_WIDTH - 0.5, OUTLINE_WIDTH - 0.5,
raveit65 3243dae
+                       preview->tile_rect.width - 2 * (OUTLINE_WIDTH - 1) - 1,
raveit65 3243dae
+                       preview->tile_rect.height - 2 * (OUTLINE_WIDTH - 1) - 1);
raveit65 3243dae
+      cairo_stroke (cr);
raveit65 3243dae
+    }
raveit65 3243dae
+
raveit65 3243dae
+  cairo_rectangle (cr,
raveit65 3243dae
+                   0.5, 0.5,
raveit65 3243dae
+                   preview->tile_rect.width - 1,
raveit65 3243dae
+                   preview->tile_rect.height - 1);
raveit65 3243dae
+  cairo_stroke (cr);
raveit65 3243dae
+
raveit65 3243dae
+  cairo_destroy (cr);
raveit65 3243dae
+
raveit65 3243dae
+  return FALSE;
raveit65 3243dae
+}
raveit65 3243dae
+
raveit65 3243dae
+static void
raveit65 3243dae
+on_preview_window_style_set (GtkWidget *widget,
raveit65 3243dae
+                             GtkStyle  *previous,
raveit65 3243dae
+                             gpointer   user_data)
raveit65 3243dae
+{
raveit65 3243dae
+  MetaTilePreview *preview = user_data;
raveit65 3243dae
+  GtkStyle *style;
raveit65 3243dae
+
raveit65 3243dae
+  style = gtk_rc_get_style_by_paths (gtk_widget_get_settings (widget),
raveit65 3243dae
+                                     "GtkWindow.GtkIconView",
raveit65 3243dae
+                                     "GtkWindow.GtkIconView",
raveit65 3243dae
+                                     GTK_TYPE_ICON_VIEW);
raveit65 3243dae
+
raveit65 3243dae
+  if (style != NULL)
raveit65 3243dae
+    g_object_ref (style);
raveit65 3243dae
+  else
raveit65 3243dae
+    style = gtk_style_new ();
raveit65 3243dae
+
raveit65 3243dae
+  gtk_style_get (style, GTK_TYPE_ICON_VIEW,
raveit65 3243dae
+                 "selection-box-color", &preview->preview_color,
raveit65 3243dae
+                 "selection-box-alpha", &preview->preview_alpha,
raveit65 3243dae
+                 NULL);
raveit65 3243dae
+  if (!preview->preview_color)
raveit65 3243dae
+    {
raveit65 3243dae
+      GdkColor selection = style->base[GTK_STATE_SELECTED];
raveit65 3243dae
+      preview->preview_color = gdk_color_copy (&selection);
raveit65 3243dae
+    }
raveit65 3243dae
+
raveit65 3243dae
+  g_object_unref (style);
raveit65 3243dae
+}
raveit65 3243dae
+
raveit65 3243dae
+MetaTilePreview *
raveit65 3243dae
+meta_tile_preview_new (int      screen_number,
raveit65 3243dae
+                       gboolean composited)
raveit65 3243dae
+{
raveit65 3243dae
+  MetaTilePreview *preview;
raveit65 3243dae
+  GdkColormap *rgba_colormap;
raveit65 3243dae
+  GdkScreen *screen;
raveit65 3243dae
+
raveit65 3243dae
+  screen = gdk_display_get_screen (gdk_display_get_default (), screen_number);
raveit65 3243dae
+  rgba_colormap = gdk_screen_get_rgba_colormap (screen);
raveit65 3243dae
+
raveit65 3243dae
+  preview = g_new (MetaTilePreview, 1);
raveit65 3243dae
+
raveit65 3243dae
+  preview->preview_window = gtk_window_new (GTK_WINDOW_POPUP);
raveit65 3243dae
+
raveit65 3243dae
+  gtk_window_set_screen (GTK_WINDOW (preview->preview_window), screen);
raveit65 3243dae
+  gtk_widget_set_app_paintable (preview->preview_window, TRUE);
raveit65 3243dae
+
raveit65 3243dae
+  preview->preview_color = NULL;
raveit65 3243dae
+  preview->preview_alpha = 0xFF;
raveit65 3243dae
+
raveit65 3243dae
+  preview->tile_rect.x = preview->tile_rect.y = 0;
raveit65 3243dae
+  preview->tile_rect.width = preview->tile_rect.height = 0;
raveit65 3243dae
+
raveit65 3243dae
+  preview->has_alpha = rgba_colormap && composited;
raveit65 3243dae
+
raveit65 3243dae
+  if (preview->has_alpha)
raveit65 3243dae
+    {
raveit65 3243dae
+      gtk_widget_set_colormap (preview->preview_window, rgba_colormap);
raveit65 3243dae
+
raveit65 3243dae
+      g_signal_connect (preview->preview_window, "style-set",
raveit65 3243dae
+                        G_CALLBACK (on_preview_window_style_set), preview);
raveit65 3243dae
+    }
raveit65 3243dae
+
raveit65 3243dae
+  gtk_widget_realize (preview->preview_window);
raveit65 3243dae
+  gdk_window_set_back_pixmap (gtk_widget_get_window (preview->preview_window),
raveit65 3243dae
+                              NULL, FALSE);
raveit65 3243dae
+
raveit65 3243dae
+  g_signal_connect (preview->preview_window, "expose-event",
raveit65 3243dae
+                    G_CALLBACK (meta_tile_preview_expose), preview);
raveit65 3243dae
+
raveit65 3243dae
+  return preview;
raveit65 3243dae
+}
raveit65 3243dae
+
raveit65 3243dae
+void
raveit65 3243dae
+meta_tile_preview_free (MetaTilePreview *preview)
raveit65 3243dae
+{
raveit65 3243dae
+  gtk_widget_destroy (preview->preview_window);
raveit65 3243dae
+
raveit65 3243dae
+  if (preview->preview_color)
raveit65 3243dae
+    gdk_color_free (preview->preview_color);
raveit65 3243dae
+
raveit65 3243dae
+  g_free (preview);
raveit65 3243dae
+}
raveit65 3243dae
+
raveit65 3243dae
+void
raveit65 3243dae
+meta_tile_preview_show (MetaTilePreview *preview,
raveit65 3243dae
+                        MetaRectangle   *tile_rect)
raveit65 3243dae
+{
raveit65 3243dae
+  GdkWindow *window;
raveit65 3243dae
+  GdkRectangle old_rect;
raveit65 3243dae
+
raveit65 3243dae
+  if (gtk_widget_get_visible (preview->preview_window)
raveit65 3243dae
+      && preview->tile_rect.x == tile_rect->x
raveit65 3243dae
+      && preview->tile_rect.y == tile_rect->y
raveit65 3243dae
+      && preview->tile_rect.width == tile_rect->width
raveit65 3243dae
+      && preview->tile_rect.height == tile_rect->height)
raveit65 3243dae
+    return; /* nothing to do */
raveit65 3243dae
+
raveit65 3243dae
+  gtk_widget_show (preview->preview_window);
raveit65 3243dae
+  window = gtk_widget_get_window (preview->preview_window);
raveit65 3243dae
+  meta_core_lower_beneath_focus_window (gdk_display,
raveit65 3243dae
+                                        GDK_WINDOW_XWINDOW (window),
raveit65 3243dae
+                                        gtk_get_current_event_time ());
raveit65 3243dae
+
raveit65 3243dae
+  old_rect.x = old_rect.y = 0;
raveit65 3243dae
+  old_rect.width = preview->tile_rect.width;
raveit65 3243dae
+  old_rect.height = preview->tile_rect.height;
raveit65 3243dae
+
raveit65 3243dae
+  gdk_window_invalidate_rect (window, &old_rect, FALSE);
raveit65 3243dae
+
raveit65 3243dae
+  preview->tile_rect = *tile_rect;
raveit65 3243dae
+
raveit65 3243dae
+  gdk_window_move_resize (window,
raveit65 3243dae
+                          preview->tile_rect.x, preview->tile_rect.y,
raveit65 3243dae
+                          preview->tile_rect.width, preview->tile_rect.height);
raveit65 3243dae
+
raveit65 3243dae
+  if (!preview->has_alpha)
raveit65 3243dae
+    {
raveit65 3243dae
+      GdkRectangle outer_rect, inner_rect;
raveit65 3243dae
+      GdkRegion *outer_region, *inner_region;
raveit65 3243dae
+      GdkColor black;
raveit65 3243dae
+
raveit65 3243dae
+      black = gtk_widget_get_style (preview->preview_window)->black;
raveit65 3243dae
+      gdk_window_set_background (window, &black);
raveit65 3243dae
+
raveit65 3243dae
+      outer_rect.x = outer_rect.y = 0;
raveit65 3243dae
+      outer_rect.width = preview->tile_rect.width;
raveit65 3243dae
+      outer_rect.height = preview->tile_rect.height;
raveit65 3243dae
+
raveit65 3243dae
+      inner_rect.x = OUTLINE_WIDTH;
raveit65 3243dae
+      inner_rect.y = OUTLINE_WIDTH;
raveit65 3243dae
+      inner_rect.width = outer_rect.width - 2 * OUTLINE_WIDTH;
raveit65 3243dae
+      inner_rect.height = outer_rect.height - 2 * OUTLINE_WIDTH;
raveit65 3243dae
+
raveit65 3243dae
+      outer_region = gdk_region_rectangle (&outer_rect);
raveit65 3243dae
+      inner_region = gdk_region_rectangle (&inner_rect);
raveit65 3243dae
+
raveit65 3243dae
+      gdk_region_subtract (outer_region, inner_region);
raveit65 3243dae
+      gdk_region_destroy (inner_region);
raveit65 3243dae
+
raveit65 3243dae
+      gdk_window_shape_combine_region (window, outer_region, 0, 0);
raveit65 3243dae
+      gdk_region_destroy (outer_region);
raveit65 3243dae
+    }
raveit65 3243dae
+}
raveit65 3243dae
+
raveit65 3243dae
+void
raveit65 3243dae
+meta_tile_preview_hide (MetaTilePreview *preview)
raveit65 3243dae
+{
raveit65 3243dae
+  gtk_widget_hide (preview->preview_window);
raveit65 3243dae
+}
raveit65 3243dae
\ No newline at end of file
raveit65 3243dae
--
raveit65 3243dae
cgit 
raveit65 3243dae