summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlo Wood <carlo@alinoe.com>2011-01-21 23:17:46 -0500
committerThomas Thurman <tthurman@gnome.org>2011-01-21 23:17:46 -0500
commit12cbf9c7306296a559a71b2ca9cc725465a96816 (patch)
tree69c505f6e94b8c8c4bff6d3910fa9d16c515abb7
parentaebcd470db8238b0917420a670c1fd0f9ff71903 (diff)
downloadmetacity-bug356715.tar.gz
Allow horizontally-maximised windows to be shaken loose between xineramas. Closes #356715.bug356715
-rw-r--r--src/core/constraints.c2
-rw-r--r--src/core/display.c3
-rw-r--r--src/core/keybindings.c91
-rw-r--r--src/core/screen-private.h2
-rw-r--r--src/core/screen.c38
-rw-r--r--src/core/window-private.h7
-rw-r--r--src/core/window.c629
7 files changed, 615 insertions, 157 deletions
diff --git a/src/core/constraints.c b/src/core/constraints.c
index 5606b76b..e2ee3abc 100644
--- a/src/core/constraints.c
+++ b/src/core/constraints.c
@@ -129,7 +129,7 @@ typedef struct
FixedDirections fixed_directions;
/* work_area_xinerama - current xinerama region minus struts
- * entire_xinerama - current xienrama, including strut regions
+ * entire_xinerama - current xinerama, including strut regions
*/
MetaRectangle work_area_xinerama;
MetaRectangle entire_xinerama;
diff --git a/src/core/display.c b/src/core/display.c
index e816823a..10d1ecc1 100644
--- a/src/core/display.c
+++ b/src/core/display.c
@@ -3483,9 +3483,6 @@ meta_display_end_grab_op (MetaDisplay *display,
if (display->grab_op == META_GRAB_OP_NONE)
return;
- if (display->grab_window != NULL)
- display->grab_window->shaken_loose = FALSE;
-
if (display->grab_window != NULL &&
!meta_prefs_get_raise_on_click () &&
(meta_grab_op_is_moving (display->grab_op) ||
diff --git a/src/core/keybindings.c b/src/core/keybindings.c
index 8ea6b415..86707013 100644
--- a/src/core/keybindings.c
+++ b/src/core/keybindings.c
@@ -1425,6 +1425,51 @@ meta_display_process_key_event (MetaDisplay *display,
!all_keys_grabbed && window);
}
+static void
+process_cancel_grab(MetaDisplay *display,
+ MetaWindow *window)
+{
+ /* End move or resize and restore to original state.
+ * In normal cases, we need to do a moveresize now to get the
+ * position back to the original. In wireframe mode, we just
+ * need to set grab_was_cancelled to true to avoid avoid
+ * moveresizing to the position of the wireframe.
+ */
+ if (!display->grab_wireframe_active)
+ {
+ /* If the window was a maximized window that had been
+ * "shaken loose" we need to remaximize it.
+ *
+ * This needs to be done BEFORE calling meta_window_move_resize
+ * because that function calls constrain_size_increments
+ * which uses window->maximized_horizontally/vertically.
+ * Trying to set a window to grab_initial_window_pos.width
+ * without having maximized_horizontally set, for an originally
+ * horizontally maximized window, can easily fail the
+ * size-increments contrain, causing it to resize the window
+ * based on the current frame size, overwriting the y coordinate
+ * in the process.
+ */
+ if (window->shaken_loose_horizontally ||
+ window->shaken_loose_vertically)
+ meta_window_maximize (window,
+ (window->shaken_loose_horizontally ?
+ META_MAXIMIZE_HORIZONTAL : 0) |
+ (window->shaken_loose_vertically ?
+ META_MAXIMIZE_VERTICAL : 0));
+
+ /* Now it is safe to move it back to where it came from */
+ meta_window_move_resize (display->grab_window,
+ TRUE,
+ display->grab_initial_window_pos.x,
+ display->grab_initial_window_pos.y,
+ display->grab_initial_window_pos.width,
+ display->grab_initial_window_pos.height);
+ }
+ else
+ display->grab_was_cancelled = TRUE;
+}
+
static gboolean
process_mouse_move_resize_grab (MetaDisplay *display,
MetaScreen *screen,
@@ -1438,26 +1483,7 @@ process_mouse_move_resize_grab (MetaDisplay *display,
if (keysym == XK_Escape)
{
- /* End move or resize and restore to original state. If the
- * window was a maximized window that had been "shaken loose" we
- * need to remaximize it. In normal cases, we need to do a
- * moveresize now to get the position back to the original. In
- * 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)
- meta_window_maximize (window,
- META_MAXIMIZE_HORIZONTAL |
- META_MAXIMIZE_VERTICAL);
- else if (!display->grab_wireframe_active)
- meta_window_move_resize (display->grab_window,
- TRUE,
- display->grab_initial_window_pos.x,
- display->grab_initial_window_pos.y,
- display->grab_initial_window_pos.width,
- display->grab_initial_window_pos.height);
- else
- display->grab_was_cancelled = TRUE;
+ process_cancel_grab (display, window);
/* End grab */
return FALSE;
@@ -1511,29 +1537,8 @@ process_keyboard_move_grab (MetaDisplay *display,
incr = NORMAL_INCREMENT;
if (keysym == XK_Escape)
- {
- /* End move and restore to original state. If the window was a
- * maximized window that had been "shaken loose" we need to
- * remaximize it. In normal cases, we need to do a moveresize
- * now to get the position back to the original. In 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)
- meta_window_maximize (window,
- META_MAXIMIZE_HORIZONTAL |
- META_MAXIMIZE_VERTICAL);
- else if (!display->grab_wireframe_active)
- meta_window_move_resize (display->grab_window,
- TRUE,
- display->grab_initial_window_pos.x,
- display->grab_initial_window_pos.y,
- display->grab_initial_window_pos.width,
- display->grab_initial_window_pos.height);
- else
- display->grab_was_cancelled = TRUE;
- }
-
+ process_cancel_grab (display, window);
+
/* When moving by increments, we still snap to edges if the move
* to the edge is smaller than the increment. This is because
* Shift + arrow to snap is sort of a hidden feature. This way
diff --git a/src/core/screen-private.h b/src/core/screen-private.h
index 8430f198..c8aacf0f 100644
--- a/src/core/screen-private.h
+++ b/src/core/screen-private.h
@@ -166,7 +166,7 @@ MetaWindow* meta_screen_get_mouse_window (MetaScreen *scre
const MetaXineramaScreenInfo* meta_screen_get_current_xinerama (MetaScreen *screen);
const MetaXineramaScreenInfo* meta_screen_get_xinerama_for_rect (MetaScreen *screen,
- MetaRectangle *rect);
+ const MetaRectangle *rect);
const MetaXineramaScreenInfo* meta_screen_get_xinerama_for_window (MetaScreen *screen,
MetaWindow *window);
diff --git a/src/core/screen.c b/src/core/screen.c
index f2029477..fcc0a396 100644
--- a/src/core/screen.c
+++ b/src/core/screen.c
@@ -1481,8 +1481,8 @@ meta_screen_get_mouse_window (MetaScreen *screen,
}
const MetaXineramaScreenInfo*
-meta_screen_get_xinerama_for_rect (MetaScreen *screen,
- MetaRectangle *rect)
+meta_screen_get_xinerama_for_rect (MetaScreen *screen,
+ const MetaRectangle *rect)
{
int i;
int best_xinerama, xinerama_score;
@@ -1509,6 +1509,40 @@ meta_screen_get_xinerama_for_rect (MetaScreen *screen,
}
}
+ if (xinerama_score == 0)
+ {
+ /* If no overlapping area is found at all, returning xinerama 0
+ * isn't always the best. For example, if we are on xinerama 1
+ * and we move the window all the way down, then the inner window
+ * shifts completely off the monitor (resulting it getting here
+ * with xinerama_score == 0), yet we want to return xinerama 1
+ * of course and not 0, because that would cause the (still visible)
+ * title bar to jump to a different monitor.
+ *
+ * Try to make a reasonable guess that will make sense in most
+ * practical cases by returning the xinerama with the shortest
+ * distance (using the "Manhattan" metric) between the centers
+ * of rect and the xinerama.
+ */
+ int min_distance = 1000000;
+ int rect_center_x = rect->x + rect->width / 2;
+ int rect_center_y = rect->y + rect->height / 2;
+ for (i = 0; i < screen->n_xinerama_infos; i++)
+ {
+ int xinerama_center_x = screen->xinerama_infos[i].rect.x +
+ screen->xinerama_infos[i].rect.width / 2;
+ int xinerama_center_y = screen->xinerama_infos[i].rect.y +
+ screen->xinerama_infos[i].rect.height / 2;
+ int distance = ABS(rect_center_x - xinerama_center_x) +
+ ABS(rect_center_y - xinerama_center_y);
+ if (distance < min_distance)
+ {
+ min_distance = distance;
+ best_xinerama = i;
+ }
+ }
+ }
+
return &screen->xinerama_infos[best_xinerama];
}
diff --git a/src/core/window-private.h b/src/core/window-private.h
index 7cf5b04a..c0c831e8 100644
--- a/src/core/window-private.h
+++ b/src/core/window-private.h
@@ -298,8 +298,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;
diff --git a/src/core/window.c b/src/core/window.c
index 11994d51..dddb7fdd 100644
--- a/src/core/window.c
+++ b/src/core/window.c
@@ -494,7 +494,8 @@ meta_window_new_with_attrs (MetaDisplay *display,
window->net_wm_user_time_set = FALSE;
window->user_time_window = None;
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;
@@ -2488,14 +2489,16 @@ meta_window_save_rect (MetaWindow *window)
if (!(META_WINDOW_MAXIMIZED (window) || window->fullscreen))
{
/* save size/pos as appropriate args for move_resize */
- if (!window->maximized_horizontally)
+ if (!window->maximized_horizontally &&
+ !window->shaken_loose_horizontally)
{
window->saved_rect.x = window->rect.x;
window->saved_rect.width = window->rect.width;
if (window->frame)
window->saved_rect.x += window->frame->rect.x;
}
- if (!window->maximized_vertically)
+ if (!window->maximized_vertically &&
+ !window->shaken_loose_vertically)
{
window->saved_rect.y = window->rect.y;
window->saved_rect.height = window->rect.height;
@@ -2533,12 +2536,14 @@ save_user_window_placement (MetaWindow *window)
meta_window_get_client_root_coords (window, &user_rect);
- if (!window->maximized_horizontally)
+ if (!window->maximized_horizontally &&
+ !window->shaken_loose_horizontally)
{
window->user_rect.x = user_rect.x;
window->user_rect.width = user_rect.width;
}
- if (!window->maximized_vertically)
+ if (!window->maximized_vertically &&
+ !window->shaken_loose_vertically)
{
window->user_rect.y = user_rect.y;
window->user_rect.height = user_rect.height;
@@ -2576,6 +2581,10 @@ meta_window_maximize_internal (MetaWindow *window,
if (maximize_horizontally || maximize_vertically)
window->force_save_user_rect = FALSE;
+ /* 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
@@ -2730,8 +2739,17 @@ meta_window_unmaximize (MetaWindow *window,
window->display->grab_anchor_window_pos = target_rect;
}
+ /* An unmaximize is a 'user action' (we pass TRUE here) because it
+ * needs to set user_rect to the new position (being saved_rect).
+ * The reason for that is that we want that a subsequential partial
+ * maximize should react exactly as if the window was brand new:
+ * simply expand from its current position. Without updating
+ * user_rect, it would jump back to where it was before the
+ * unmaximize, which could easily be at coordinate 0 when the last
+ * maximize was in the other direction (or full).
+ */
meta_window_move_resize (window,
- FALSE,
+ TRUE,
target_rect.x,
target_rect.y,
target_rect.width,
@@ -6892,6 +6910,483 @@ 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));
+}
+
+static int const NONE = -1; /* Not equal to an possible monitor number */
+
+/* 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 'snap_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 NONE otherwise.
+ * Returns the current monitor.
+ */
+static int
+check_threshold (MetaWindow *window,
+ int snap_threshold,
+ int x,
+ int y,
+ const MetaRectangle *new_rect,
+ gboolean relevant_x,
+ gboolean relevant_y,
+ int *target_monitor,
+ MetaRectangle *target_work_area,
+ gboolean *far_side)
+{
+ const MetaXineramaScreenInfo *wxinerama;
+ const MetaXineramaScreenInfo *xinerama_info;
+
+ gboolean target_at_bottom, target_at_right;
+
+ wxinerama = meta_screen_get_xinerama_for_window (window->screen, window);
+
+ /* Where does the window go if we call meta_window_maximize? */
+ xinerama_info = meta_screen_get_xinerama_for_rect (window->screen, new_rect);
+ *target_monitor = xinerama_info->number;
+ meta_window_get_work_area_for_xinerama (window, *target_monitor, target_work_area);
+
+ /* Is the target monitor on the far bottom/right side? */
+ target_at_right =
+ meta_screen_get_xinerama_neighbor(window->screen,
+ *target_monitor, META_SCREEN_LEFT) != NULL &&
+ meta_screen_get_xinerama_neighbor(window->screen,
+ *target_monitor, META_SCREEN_RIGHT) == NULL;
+ target_at_bottom =
+ meta_screen_get_xinerama_neighbor(window->screen,
+ *target_monitor, META_SCREEN_UP) != NULL &&
+ meta_screen_get_xinerama_neighbor(window->screen,
+ *target_monitor, META_SCREEN_DOWN) == NULL;
+
+ 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_rect->x + window->saved_rect.width +
+ (window->frame ? window->frame->right_width : 0) -
+ target_work_area->x - target_work_area->width) :
+ ABS(new_rect->x -
+ (window->frame ? window->frame->child_x : 0) -
+ target_work_area->x)) >= snap_threshold) ||
+ (relevant_y &&
+ (target_at_bottom ?
+ ABS(new_rect->y + window->saved_rect.height +
+ (window->frame ? window->frame->bottom_height : 0) -
+ target_work_area->y - target_work_area->height) :
+ ABS(new_rect->y -
+ (window->frame ? window->frame->child_y : 0) -
+ target_work_area->y)) >= snap_threshold))
+ /* Not close to a relevant edge: no maximization. */
+ *target_monitor = NONE;
+
+ /* Return current monitor. */
+ return wxinerama->number;
+}
+
+/* Check if a window, when translated over (dx,dy) from its current
+ * position and with the grab pointer at location (x,y) would
+ * shake loose from being maximized, or would re-maximize when
+ * already being shaken loose. If so, handle it.
+ *
+ * Returns true when this was the case and the move has been handled,
+ * returns false otherwise, in which case nothing has been done.
+ */
+static gboolean
+do_shake_loose(MetaWindow *window,
+ int const x, int const y,
+ int const dx, int const dy)
+{
+ MetaRectangle new_rect;
+ MetaDisplay *display = window->display;
+ int snap_threshold, shake_threshold;
+ gboolean relevant_x, relevant_y;
+ int relevant_delta;
+
+ /* Calculate the new rectangle, if we'd just normally move */
+ new_rect = display->grab_anchor_window_pos;
+ new_rect.x += dx;
+ new_rect.y += dy;
+
+ /* 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_SNAP_THRESHOLD_FACTOR 4
+#define SHAKE_THRESHOLD 256
+ snap_threshold =
+ DRAG_THRESHOLD_TO_SNAP_THRESHOLD_FACTOR *
+ meta_ui_get_drag_threshold (window->screen->ui);
+ shake_threshold = SHAKE_THRESHOLD;
+
+ /* 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 at most 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)
+ {
+ MetaRectangle new_saved_rect = window->saved_rect;
+
+ /* '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
+ * proportionally 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 guarantees 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 prohibited 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 proportionally equivalent to grab position */
+ new_saved_rect.x = x - saved_full_width * prop_x_factor + frame_left;
+
+ /* Vertical placement proportionally equivalent to grab position */
+ new_saved_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,
+ snap_threshold,
+ x, y,
+ &new_saved_rect,
+ 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 != NONE ||
+ /* 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_saved_rect.x < display->grab_anchor_window_pos.x) &&
+ (!relevant_y ||
+ new_saved_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_saved_rect.x + window->saved_rect.width >
+ display->grab_anchor_window_pos.x +
+ display->grab_anchor_window_pos.width) &&
+ (!relevant_y ||
+ new_saved_rect.y + window->saved_rect.height >
+ display->grab_anchor_window_pos.y +
+ display->grab_anchor_window_pos.height))))
+ ))
+ {
+ /* Keep a copy of saved_rect. */
+ MetaRectangle restore_saved_rect = window->saved_rect;
+
+ /* 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 = new_saved_rect.x;
+ display->grab_anchor_window_pos.y = new_saved_rect.y;
+
+ /* Unmaximize the window to new_saved_rect */
+ window->saved_rect = new_saved_rect;
+ meta_window_unmaximize (window,
+ META_MAXIMIZE_HORIZONTAL |
+ META_MAXIMIZE_VERTICAL);
+ window->saved_rect = restore_saved_rect;
+
+ /* 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 };
+
+ check_threshold(window,
+ snap_threshold,
+ x, y,
+ &new_rect,
+ relevant_x, relevant_y,
+ &target_monitor, &target_work_area, NULL);
+
+ if (target_monitor != NONE)
+ {
+ int new_ptr_x, new_ptr_y;
+ MetaRectangle* rect_ptr = &window->saved_rect;
+
+ /* Move the saved_rect and user_rect to the target monitor that
+ * we maximize on, so the user isn't surprised on a later unmaximize.
+ * Also see the note below point 9 of comment #4 of bug #356715.
+ */
+ for(;;)
+ {
+ const MetaXineramaScreenInfo *xinerama_info_rect;
+
+ xinerama_info_rect =
+ meta_screen_get_xinerama_for_rect (window->screen,
+ rect_ptr);
+
+ if (xinerama_info_rect->number != target_monitor)
+ {
+ /* Move rect_ptr to the target monitor, because we demand
+ * that unmaximizing a window never switches monitor.
+ */
+ rect_ptr->x +=
+ window->screen->xinerama_infos[target_monitor].rect.x -
+ xinerama_info_rect->rect.x;
+ rect_ptr->y +=
+ window->screen->xinerama_infos[target_monitor].rect.y -
+ xinerama_info_rect->rect.y;
+
+ /* Make sure that the rect will be visible on the new
+ * monitor (it might not, because the target monitor is
+ * smaller.
+ */
+ if (!meta_rectangle_overlap(rect_ptr,
+ &target_work_area))
+ {
+ /* If not visible - move it to the top-left position. */
+ rect_ptr->x = target_work_area.x;
+ rect_ptr->y = target_work_area.y;
+ if (window->frame)
+ {
+ rect_ptr->x += window->frame->child_x;
+ rect_ptr->y += window->frame->child_y;
+ }
+ }
+ }
+
+ if (rect_ptr == &window->user_rect)
+ break;
+ rect_ptr = &window->user_rect;
+ }
+
+ /* 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_rect.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_rect.y;
+
+ /* Update state. This resets shaken_loose_*. */
+ meta_window_maximize_internal (window,
+ (window->shaken_loose_horizontally ?
+ META_MAXIMIZE_HORIZONTAL : 0) |
+ (window->shaken_loose_vertically ?
+ META_MAXIMIZE_VERTICAL : 0),
+ &window->saved_rect);
+ meta_window_queue(window, META_QUEUE_MOVE_RESIZE);
+
+ 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,
@@ -6901,18 +7396,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,
@@ -6928,102 +7426,17 @@ update_move (MetaWindow *window,
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);
+ /* Handle (un)maximization thresholds */
+ if (do_shake_loose(window, x, y, dx, dy))
+ return;
- 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;
- }
-
- window->user_rect.x = window->saved_rect.x;
- window->user_rect.y = window->saved_rect.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);
-
- return;
- }
- }
- }
+ new_x = display->grab_anchor_window_pos.x + dx;
+ new_y = display->grab_anchor_window_pos.y + dy;
if (display->grab_wireframe_active)
old = display->grab_wireframe_rect;
@@ -7464,6 +7877,12 @@ meta_window_handle_mouse_grab_op_event (MetaWindow *window,
}
}
+ if (window->shaken_loose_vertically || window->shaken_loose_horizontally)
+ {
+ window->shaken_loose_vertically = FALSE;
+ window->shaken_loose_horizontally = FALSE;
+ save_user_window_placement(window);
+ }
meta_display_end_grab_op (window->display, event->xbutton.time);
break;