Index: src/display.c
===================================================================
RCS file: /cvs/gnome/metacity/src/display.c,v
retrieving revision 1.291
diff -u -d -p -r1.291 display.c
--- src/display.c	13 Sep 2006 16:32:32 -0000	1.291
+++ src/display.c	19 Sep 2006 12:19:50 -0000
@@ -3528,8 +3528,11 @@ meta_display_end_grab_op (MetaDisplay *d
     return;
 
   if (display->grab_window != NULL)
-    display->grab_window->shaken_loose = FALSE;
-  
+    {
+      display->grab_window->shaken_loose_vertically = FALSE;
+      display->grab_window->shaken_loose_horizontally = FALSE;
+    }
+
   if (display->grab_window != NULL &&
       !meta_prefs_get_raise_on_click () &&
       (meta_grab_op_is_moving (display->grab_op) ||
Index: src/keybindings.c
===================================================================
RCS file: /cvs/gnome/metacity/src/keybindings.c,v
retrieving revision 1.134
diff -u -d -p -r1.134 keybindings.c
--- src/keybindings.c	13 Sep 2006 16:32:32 -0000	1.134
+++ src/keybindings.c	19 Sep 2006 12:19:50 -0000
@@ -1758,7 +1758,8 @@ process_mouse_move_resize_grab (MetaDisp
        * wireframe mode, we just need to set grab_was_cancelled to tru
        * to avoid avoid moveresizing to the position of the wireframe.
        */
-      if (window->shaken_loose)
+      if (window->shaken_loose_horizontally &&
+          window->shaken_loose_vertically)
         meta_window_maximize (window,
                               META_MAXIMIZE_HORIZONTAL |
                               META_MAXIMIZE_VERTICAL);
@@ -1835,7 +1836,8 @@ process_keyboard_move_grab (MetaDisplay 
        * mode, we just need to set grab_was_cancelled to tru to avoid
        * avoid moveresizing to the position of the wireframe.
        */
-      if (window->shaken_loose)
+      if (window->shaken_loose_horizontally &&
+          window->shaken_loose_vertically)
         meta_window_maximize (window,
                               META_MAXIMIZE_HORIZONTAL |
                               META_MAXIMIZE_VERTICAL);
Index: src/window.c
===================================================================
RCS file: /cvs/gnome/metacity/src/window.c,v
retrieving revision 1.449
diff -u -d -p -r1.449 window.c
--- src/window.c	13 Sep 2006 22:21:19 -0000	1.449
+++ src/window.c	19 Sep 2006 12:19:51 -0000
@@ -477,7 +477,8 @@ meta_window_new_with_attrs (MetaDisplay 
   window->initial_timestamp_set = FALSE;
   window->net_wm_user_time_set = FALSE;
   window->calc_placement = FALSE;
-  window->shaken_loose = FALSE;
+  window->shaken_loose_vertically = FALSE;
+  window->shaken_loose_horizontally = FALSE;
   window->have_focus_click_grab = FALSE;
   window->disable_sync = FALSE;
   
@@ -2232,6 +2233,10 @@ meta_window_maximize_internal (MetaWindo
   window->maximized_vertically = 
     window->maximized_vertically   || maximize_vertically;
 
+  /* Never have shaken_loose_* set while maximized in any direction. */
+  window->shaken_loose_horizontally = FALSE;
+  window->shaken_loose_vertically = FALSE;
+
   /* Fix for #336850: If the frame shape isn't reapplied, it is
    * possible that the frame will retains its rounded corners. That
    * happens if the client's size when maximized equals the unmaximized
@@ -6516,6 +6521,462 @@ update_move_timeout (gpointer data)
   return FALSE;
 }
 
+/* Transform 'x' into a normalized coordinate relative to the outer
+ * window of 'rect', including its frame (if any).
+ * If x is on the left frame border, this function returns 0,
+ * if x is on the right frame border, 1.0 is returned.
+ */
+static double
+normalized_x (MetaRectangle *rect,
+              MetaFrame     *frame,
+              int           x)
+{
+  return
+      (double)(x - rect->x + (frame ? frame->child_x : 0)) /
+      (rect->width + (frame ? frame->child_x + frame->right_width : 0));
+}
+
+/* Transform 'y' into a normalized coordinate relative to the outer
+ * window of 'rect', including its frame (if any).
+ * If y is on the top frame border, this function returns 0,
+ * if y is on the bottom frame border, 1.0 is returned.
+ */
+static double
+normalized_y (MetaRectangle *rect,
+              MetaFrame     *frame,
+              int           y)
+{
+  return
+      (double)(y - rect->y + (frame ? frame->child_y : 0)) /
+      (rect->height + (frame ? frame->child_y + frame->bottom_height : 0));
+}
+
+/* Check if a window at position new_x, new_y, while the pointer
+ * is at position x, y would maximize to the respective relevant
+ * screen border(s), using a threshold 'shake_threshold'. If so,
+ * set *target_monitor to the monitor that it will be maximized
+ * on, set *target_work_area to the work area of that monitor,
+ * and set *far_side to true if we are against a far right and/or
+ * bottom monitor taking relevant directions into account.
+ * Set *target_monitor to -1 otherwise.
+ * Returns the current monitor.
+ */
+static int
+check_threshold (MetaWindow    *window,
+                 int           shake_threshold,
+                 int           x,
+		 int           y,
+		 int           new_x,
+		 int           new_y,
+                 gboolean      relevant_x,
+		 gboolean      relevant_y,
+                 int           *target_monitor,
+		 MetaRectangle *target_work_area,
+		 gboolean      *far_side)
+{
+  const MetaXineramaScreenInfo *wxinerama;
+  int window_center_x, window_center_y;
+  int minimum_distance = 1000000;
+  int monitor;
+  gboolean target_at_bottom, target_at_right;
+  int workspace_bottom = 0, workspace_right = 0;
+
+  wxinerama = meta_screen_get_xinerama_for_window (window->screen, window);
+
+  meta_window_get_position(window, &window_center_x, &window_center_y);
+  window_center_x += window->rect.width / 2;
+  window_center_y += window->rect.height / 2;
+
+  /* Where does the window go if we call meta_window_maximize? */
+  for (monitor = 0; monitor < window->screen->n_xinerama_infos; monitor++)
+    {
+      MetaRectangle work_area;
+      int delta_x, delta_y;
+      int distance;
+
+      meta_window_get_work_area_for_xinerama (window, monitor, &work_area);
+
+      /* Because theorectically the pointer could be outside any
+       * xinerama, we find the monitor that is closest to its
+       * position rather than the monitor under the pointer.
+       * In practise this shouldn't be needed, but it is much
+       * more robust.
+       */
+
+      /* Calculate the distance to the center of this work area.
+       * We use the "Manhattan metric" for the distance, because
+       * that works just as well. */
+      delta_x = work_area.x + work_area.width / 2 - window_center_x;
+      delta_y = work_area.y + work_area.height / 2 - window_center_y;
+      distance = ABS(delta_x) + ABS(delta_y); /* Manhattan distance */
+
+      if (distance < minimum_distance)
+	{
+	  minimum_distance = distance;
+	  *target_work_area = work_area;
+	  *target_monitor = monitor;
+	}
+      /* Find limits of monitor area (this assumes N x M monitors) */
+      if (workspace_right < work_area.x + work_area.width)
+        workspace_right = work_area.x + work_area.width;
+      if (workspace_bottom < work_area.y + work_area.height)
+        workspace_bottom = work_area.y + work_area.height;
+    }
+
+  /* Is the target monitor on the far bottom/right side?
+   * The 100 is to account (royally) for a possible difference
+   * in strut sizes (ie panels) per monitor.
+   */
+  target_at_right = target_work_area->x > 100 &&
+      (workspace_right - target_work_area->x - target_work_area->width <= 100);
+  target_at_bottom = target_work_area->y > 100 &&
+      (workspace_bottom - target_work_area->y - target_work_area->height <= 100);
+
+  if (far_side)
+    /* This means we'll be checking the opposite side of the window */
+    *far_side = (relevant_x && target_at_right) ||
+		(relevant_y && target_at_bottom);
+
+  /* Check that pointer is inside the target work area
+   * and that the window is near the relevant boundary
+   * of that work area. The former to make sure that
+   * after maximization, the pointer is still inside
+   * the window. This check is therefore not necessary
+   * when no maximization takes place in the tested
+   * direction.
+   * If the target monitor is at the bottom or right
+   * then compare the bottom and right edge respecitively,
+   * instead of the top and left side of the window.
+   */
+  if ((window->shaken_loose_horizontally &&
+       !(x >= target_work_area->x &&
+	 x < target_work_area->x + target_work_area->width)) ||
+      (window->shaken_loose_vertically &&
+       !(y >= target_work_area->y &&
+	 y < target_work_area->y + target_work_area->height)) ||
+      (relevant_x &&
+        (target_at_right ?
+	 ABS(new_x + window->saved_rect.width -
+	     target_work_area->x - target_work_area->width) :
+	 ABS(new_x - target_work_area->x)) >= shake_threshold) ||
+      (relevant_y &&
+        (target_at_bottom ?
+	 ABS(new_y +  window->saved_rect.height -
+	     target_work_area->y - target_work_area->height) :
+	 ABS(new_y - target_work_area->y)) >= shake_threshold))
+    /* Not close to a relevant edge: no maximization. */
+    *target_monitor = -1;
+
+  /* Return current monitor. */
+  return wxinerama->number;
+}
+
+static gboolean
+do_shake_loose(MetaWindow *window, int const x, int const y,
+               int const dx, int const dy, int const new_x, int const new_y)
+{
+  MetaDisplay *display = window->display;
+  int shake_threshold;
+  gboolean relevant_x, relevant_y;
+  int relevant_delta;
+
+  /* Default value for this movement. */
+  display->grab_last_user_action_was_snap = FALSE;
+
+  /* Shake loose (unmaximize) maximized window if dragged beyond the threshold
+   * in the Y direction (or X direction if only maximized horizontally).
+   */
+
+#define DRAG_THRESHOLD_TO_SHAKE_THRESHOLD_FACTOR 12
+  shake_threshold = meta_ui_get_drag_threshold (window->screen->ui) *
+    DRAG_THRESHOLD_TO_SHAKE_THRESHOLD_FACTOR;
+    
+  /* Keep track of which directions are relevant (sensitive to the threshold) */
+  relevant_y = window->maximized_vertically || window->shaken_loose_vertically;
+  /* When shaking loose a fully maximized window, we demand that the
+   * vertical threshold is reached (and only the vertical threshold).
+   */
+  relevant_x = !relevant_y &&
+      (window->maximized_horizontally || window->shaken_loose_horizontally);
+  /* At this point only one of relevant_x or relevant_y is set. */
+
+  relevant_delta = relevant_y ? dy : dx;
+
+  /* If the window is maximized, see if we need to shake it loose. */
+  if ((window->maximized_vertically || window->maximized_horizontally) &&
+      ABS(relevant_delta) >= shake_threshold)
+    {
+      /* 'saved_rect' should contain the rectangle that we unmaximize to.
+       * Overwrite it here so that the subsequent call to
+       * meta_window_unmaximize will put the window in a sane postion
+       * relative to the current pointer.
+       *
+       * "Sane" meaning the following:
+       * 1) The pointer should remain inside the window (including frame).
+       * 2) The result should be such that the window is not within or close
+       *    to the threshold (causing it rapidly to be maximized again).
+       * 3) If possible, it should be as close as possible to a point
+       *    proportially equivalent to where it was initially at the
+       *    beginning of the grab.
+       *
+       * 1)
+       * The initial grab position should always be inside, or on
+       * the frame of a window -- otherwise it cannot be grabbed.
+       * After (un)maximization, the grab positions are updated;
+       * Point 1 garantees that also then the grab position remains
+       * inside or on the frame of the window.
+       *
+       * 2)
+       * Stability is achieved as follows: if the pointer is moving
+       * to the right - then no special measures are needed in practise
+       * because the threshold sensitive left-border of a window will
+       * only move FURTHER away from the left edge of a monitor when
+       * unmaximizing (shrinking). However, when moving a horizontally
+       * maximized window to the left, it can easily happen moving it
+       * just a few pixels further it (un)maximizes again, not a pretty
+       * sight. Therefore, unmaximizing is prohibitted if moving the
+       * mouse further in the same direction will cause it to be
+       * maximized again on the same monitor.
+       *
+       * 3)
+       * Great care has been taken to keep the pointer at the SAME
+       * place inside the unmaximized window, every time it is un-
+       * maximized again. This is done by putting the pointer
+       * proportionally at the same place inside the window when
+       * maximizing, and moving the window here such that again
+       * that same proportionality is maintained. The proportionality
+       * is thus a constant factor -- this is also nice for the eye
+       * when jumping from unmaximized to maximized or back.
+       */
+
+      /* Move the window to the pointer: this block sets new_rect_x and
+       * new_rect_y, candidate values for saved_rect before unmazimizing.
+       */
+      double prop_x_factor =
+	  normalized_x(&display->grab_anchor_window_pos,
+	               window->frame,
+		       display->grab_anchor_root_x);
+      double prop_y_factor =
+	  normalized_y(&display->grab_anchor_window_pos,
+	               window->frame,
+		       display->grab_anchor_root_y);
+      int frame_left = window->frame ? window->frame->child_x : 0;
+      int frame_top = window->frame ? window->frame->child_y : 0;
+      int frame_dx = window->frame ?
+	  (window->frame->child_x + window->frame->right_width) : 0;
+      int frame_dy = window->frame ?
+	  (window->frame->child_y + window->frame->bottom_height) : 0;
+      int saved_full_width = window->saved_rect.width + frame_dx;
+      int saved_full_height = window->saved_rect.height + frame_dy;
+
+      /* Horizontal placement proportially equivalent to grab position */
+      int new_rect_x = x - saved_full_width * prop_x_factor + frame_left;
+
+      /* Vertical placement proportially equivalent to grab position */
+      int new_rect_y = y - saved_full_height * prop_y_factor + frame_top;
+
+      /* These two variables are initialized to avoid a compiler warning.
+       * They are assigned a value by the call to check_threshold.
+       */
+      int target_monitor = 0;
+      MetaRectangle target_work_area = { 0 };
+      int current_monitor;
+      gboolean far_side;
+
+      /* Remember which directions were shaken loose.
+       * These values are used by check_threshold.
+       */
+      window->shaken_loose_vertically = window->maximized_vertically;
+      window->shaken_loose_horizontally = window->maximized_horizontally;
+
+      current_monitor =
+          check_threshold(window,
+	                  shake_threshold,
+	                  x, y,
+	                  new_rect_x, new_rect_y,
+			  relevant_x, relevant_y,
+			  &target_monitor, &target_work_area, &far_side);
+
+      // Now check if we fulfill point 2.
+      if (/* Inhibit maximization to the same monitor as we are already on */
+          target_monitor != current_monitor &&
+	  /* Allow immediate maximization to a different monitor */
+          (target_monitor != -1 ||
+	   /* Do not unmaximize the window if that means that it will
+	    * maximize again on the same monitor if we continue to move
+	    * in that same direction. */
+	   /* Target is not on the far side? */
+	   (!far_side &&
+	    (relevant_delta > 0 ||	/* Moving away from threshold? */
+	     /* New position is beyond threshold? */
+	     ((!relevant_x ||
+	       new_rect_x < display->grab_anchor_window_pos.x) &&
+	      (!relevant_y ||
+	       new_rect_y < display->grab_anchor_window_pos.y)))) ||
+	   /* Target is on the far side? */
+	   (far_side &&
+	    (relevant_delta < 0 ||	/* Moving away from threshold? */
+	     /* New position is beyond threshold? */
+	     ((!relevant_x ||
+	       new_rect_x + window->saved_rect.width >
+	           display->grab_anchor_window_pos.x +
+	           display->grab_anchor_window_pos.width) &&
+	      (!relevant_y ||
+	       new_rect_y + window->saved_rect.height >
+	           display->grab_anchor_window_pos.y +
+	           display->grab_anchor_window_pos.height))))
+	  ))
+	{
+	  /* Ok, copy the calculated position to saved_rect */
+	  window->saved_rect.x = new_rect_x;
+	  window->saved_rect.y = new_rect_y;
+
+	  /* Reinitialize grab positions to new (unmaximized) position. */
+	  display->grab_anchor_root_x = x;
+	  display->grab_anchor_root_y = y;
+	  display->grab_anchor_window_pos.x = window->saved_rect.x;
+	  display->grab_anchor_window_pos.y = window->saved_rect.y;
+
+	  meta_window_unmaximize (window,
+				  META_MAXIMIZE_HORIZONTAL |
+				  META_MAXIMIZE_VERTICAL);
+
+	  /* At this point, shaken_loose_* is set and maximized_* both unset. */
+	  return TRUE;
+	}
+
+      /* Abort shaking loose */
+      window->shaken_loose_vertically = FALSE;
+      window->shaken_loose_horizontally = FALSE;
+    }
+  /* If the window has been shaken loose, see if we need to remaximize
+   * it again, possibly on an other xinerama monitor.
+   */
+  else if (window->shaken_loose_vertically || window->shaken_loose_horizontally)
+    {
+      /* These two variables are initialized to avoid a compiler warning.
+       * They are assigned a value by the call to check_threshold.
+       */
+      int target_monitor = 0;
+      MetaRectangle target_work_area = { 0 };
+
+      int current_monitor =
+          check_threshold(window,
+	                  shake_threshold,
+	                  x, y,
+	                  new_x, new_y,
+			  relevant_x, relevant_y,
+			  &target_monitor, &target_work_area, NULL);
+
+      if (target_monitor != -1)
+	{
+	  int new_ptr_x, new_ptr_y;
+
+	  /* Move the saved rect to the upper-left corner of the work space
+	   * if window is maximized on a different monitor, so the user
+	   * isn't surprised on a later unmaximize.
+	   */
+	  if (current_monitor != target_monitor)
+	    {
+	      window->saved_rect.x = target_work_area.x;
+	      window->saved_rect.y = target_work_area.y;
+	      
+	      if (window->frame) 
+		{
+		  window->saved_rect.x += window->frame->child_x;
+		  window->saved_rect.y += window->frame->child_y;
+		}
+
+	      meta_window_unmaximize (window,
+				      META_MAXIMIZE_HORIZONTAL |
+				      META_MAXIMIZE_VERTICAL);
+	    }
+
+	  /* Reinitialize initial grab positions for newly maximized windows */
+	  new_ptr_x = x;
+	  if (window->shaken_loose_horizontally)
+	    {
+	      double prop_x_factor =
+		  normalized_x(&display->grab_anchor_window_pos,
+		               window->frame,
+			       display->grab_anchor_root_x);
+	      display->grab_anchor_window_pos.x = target_work_area.x;
+	      display->grab_anchor_window_pos.width = target_work_area.width;
+	      if (window->frame)
+	        {
+		  display->grab_anchor_window_pos.x += window->frame->child_x;
+		  display->grab_anchor_window_pos.width -=
+		      window->frame->child_x + window->frame->right_width;
+	        }
+
+	      /* Move the pointer to keep the same proportional
+	       * position inside the window. */
+	      new_ptr_x = target_work_area.x + prop_x_factor * target_work_area.width;
+	      display->grab_last_user_action_was_snap = TRUE;
+	    }
+	  else
+	    display->grab_anchor_window_pos.x = new_x;
+	  new_ptr_y = y;
+	  if (window->shaken_loose_vertically)
+	    {
+	      double prop_y_factor =
+		  normalized_y(&display->grab_anchor_window_pos,
+		               window->frame,
+			       display->grab_anchor_root_y);
+	      display->grab_anchor_window_pos.y = target_work_area.y;
+	      display->grab_anchor_window_pos.height = target_work_area.height;
+	      if (window->frame)
+	        {
+		  display->grab_anchor_window_pos.y += window->frame->child_y;
+                  display->grab_anchor_window_pos.height -=
+		      window->frame->child_y + window->frame->bottom_height;
+		}
+	      /* Move the pointer to keep the same proportional
+	       * position inside the window. */
+	      new_ptr_y = target_work_area.y + prop_y_factor * target_work_area.height;
+	      display->grab_last_user_action_was_snap = TRUE;
+	    }
+	  else
+	    display->grab_anchor_window_pos.y = new_y;
+
+	  /* Actually maximize it. This resets shaken_loose_*. */
+	  meta_window_maximize (window,
+	      (window->shaken_loose_horizontally ?
+	       META_MAXIMIZE_HORIZONTAL : 0) |
+	      (window->shaken_loose_vertically ?
+	       META_MAXIMIZE_VERTICAL : 0));
+
+	  display->grab_anchor_root_x = new_ptr_x;
+	  display->grab_anchor_root_y = new_ptr_y;
+
+	  /* Move the pointer so that we start fresh with a 'pull' of 0 */
+          if (new_ptr_x != x || new_ptr_y != y)
+	    {
+	      meta_error_trap_push (display);
+
+	      meta_topic (META_DEBUG_WINDOW_OPS,
+			  "Warping pointer to %d,%d\n", new_ptr_x, new_ptr_y);
+
+	      display->grab_latest_motion_x = new_ptr_x;
+	      display->grab_latest_motion_y = new_ptr_y;
+
+	      XWarpPointer (display->xdisplay,
+			    None,
+			    window->screen->xroot,
+			    0, 0, 0, 0, 
+			    new_ptr_x, new_ptr_y);
+
+	      meta_error_trap_pop (display, FALSE);
+            }
+
+	  /* At this point maximize_* is set and shaken_loose* both unset. */
+	  return TRUE;
+	}
+    }
+
+  return FALSE;
+}
+
 static void
 update_move (MetaWindow  *window,
              gboolean     snap,
@@ -6525,18 +6986,21 @@ update_move (MetaWindow  *window,
   int dx, dy;
   int new_x, new_y;
   MetaRectangle old;
-  int shake_threshold;
   MetaDisplay *display = window->display;
   
+  /* x,y is the current position of the pointer */
   display->grab_latest_motion_x = x;
   display->grab_latest_motion_y = y;
-  
+
+  /* grab_anchor_root_x,grab_anchor_root_y was the position
+   * of pointer at the time the grab operation began (mouse
+   * button pressed), or since the last (un)maximize (shake
+   * loose and subsequential remaximize).
+   * dx,dy is the movement since.
+   */
   dx = x - display->grab_anchor_root_x;
   dy = y - display->grab_anchor_root_y;
 
-  new_x = display->grab_anchor_window_pos.x + dx;
-  new_y = display->grab_anchor_window_pos.y + dy;
-
   meta_verbose ("x,y = %d,%d anchor ptr %d,%d anchor pos %d,%d dx,dy %d,%d\n",
                 x, y,
                 display->grab_anchor_root_x,
@@ -6545,106 +7009,24 @@ update_move (MetaWindow  *window,
                 display->grab_anchor_window_pos.y,
                 dx, dy);
 
-  /* Don't bother doing anything if no move has been specified.  (This
-   * happens often, even in keyboard moving, due to the warping of the
-   * pointer.
+  /* Don't bother doing anything if no move has been specified.
+   * (This happens often, even in keyboard moving, due to the
+   * warping of the pointer.
    */
   if (dx == 0 && dy == 0)
     return;
 
-  /* shake loose (unmaximize) maximized window if dragged beyond the threshold
-   * in the Y direction. You can't pull a window loose via X motion.
-   */
-
-#define DRAG_THRESHOLD_TO_SHAKE_THRESHOLD_FACTOR 6
-  shake_threshold = meta_ui_get_drag_threshold (window->screen->ui) *
-    DRAG_THRESHOLD_TO_SHAKE_THRESHOLD_FACTOR;
-    
-  if (META_WINDOW_MAXIMIZED (window) && ABS (dy) >= shake_threshold)
-    {
-      double prop;
-
-      /* Shake loose */
-      window->shaken_loose = TRUE;
-                  
-      /* move the unmaximized window to the cursor */
-      prop = 
-        ((double)(x - display->grab_initial_window_pos.x)) / 
-        ((double)display->grab_initial_window_pos.width);
-
-      display->grab_initial_window_pos.x = 
-        x - window->saved_rect.width * prop;
-      display->grab_initial_window_pos.y = y;
-
-      if (window->frame)
-        {
-          display->grab_initial_window_pos.y += window->frame->child_y / 2;
-        }
-
-      window->saved_rect.x = display->grab_initial_window_pos.x;
-      window->saved_rect.y = display->grab_initial_window_pos.y;
-      display->grab_anchor_root_x = x;
-      display->grab_anchor_root_y = y;
-
-      meta_window_unmaximize (window,
-                              META_MAXIMIZE_HORIZONTAL |
-                              META_MAXIMIZE_VERTICAL);
-
-      return;
-    }
-  /* remaximize window on an other xinerama monitor if window has
-   * been shaken loose or it is still maximized (then move straight)
+  /* grab_anchor_window_pos contains the coordinates of the top-left
+   * of the client window (the inner window) of when the grab began,
+   * or since the last (un)maximize.
+   * Set new_x,new_y to the new position.
    */
-  else if (window->shaken_loose || META_WINDOW_MAXIMIZED (window))
-    {
-      const MetaXineramaScreenInfo *wxinerama;
-      MetaRectangle work_area;
-      int monitor;
-
-      wxinerama = meta_screen_get_xinerama_for_window (window->screen, window);
-
-      for (monitor = 0; monitor < window->screen->n_xinerama_infos; monitor++)
-        {
-          meta_window_get_work_area_for_xinerama (window, monitor, &work_area);
-
-          /* check if cursor is near the top of a xinerama work area */ 
-          if (x >= work_area.x &&
-              x < (work_area.x + work_area.width) &&
-              y >= work_area.y &&
-              y < (work_area.y + shake_threshold))
-            {
-              /* move the saved rect if window will become maximized on an
-               * other monitor so user isn't surprised on a later unmaximize
-               */
-              if (wxinerama->number != monitor)
-                {
-                  window->saved_rect.x = work_area.x;
-                  window->saved_rect.y = work_area.y;
-                  
-                  if (window->frame) 
-                    {
-                      window->saved_rect.x += window->frame->child_x;
-                      window->saved_rect.y += window->frame->child_y;
-                    }
-
-                  meta_window_unmaximize (window,
-                                          META_MAXIMIZE_HORIZONTAL |
-                                          META_MAXIMIZE_VERTICAL);
-                }
-
-              display->grab_initial_window_pos = work_area;
-              display->grab_anchor_root_x = x;
-              display->grab_anchor_root_y = y;
-              window->shaken_loose = FALSE;
-              
-              meta_window_maximize (window,
-                                    META_MAXIMIZE_HORIZONTAL |
-                                    META_MAXIMIZE_VERTICAL);
+  new_x = display->grab_anchor_window_pos.x + dx;
+  new_y = display->grab_anchor_window_pos.y + dy;
 
-              return;
-            }
-        }
-    }
+  /* Handle (un)maximization thresholds */
+  if (do_shake_loose(window, x, y, dx, dy, new_x, new_y))
+    return;
 
   if (display->grab_wireframe_active)
     old = display->grab_wireframe_rect;
Index: src/window.h
===================================================================
RCS file: /cvs/gnome/metacity/src/window.h,v
retrieving revision 1.133
diff -u -d -p -r1.133 window.h
--- src/window.h	13 Sep 2006 16:32:33 -0000	1.133
+++ src/window.h	19 Sep 2006 12:19:51 -0000
@@ -288,8 +288,11 @@ struct _MetaWindow
   /* icon props have changed */
   guint need_reread_icon : 1;
   
-  /* if TRUE, window was maximized at start of current grab op */
-  guint shaken_loose : 1;
+  /* if TRUE, window was maximized, or vertically maximized, at start of current grab op */
+  guint shaken_loose_vertically : 1;
+
+  /* if TRUE, window was horizontally maximized, at start of current grab op */
+  guint shaken_loose_horizontally : 1;
 
   /* if TRUE we have a grab on the focus click buttons */
   guint have_focus_click_grab : 1;
