summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cvsignore1
-rw-r--r--ChangeLog111
-rw-r--r--doc/how-constraints-works.txt283
-rw-r--r--src/.cvsignore1
-rw-r--r--src/Makefile.am8
-rw-r--r--src/bell.c4
-rw-r--r--src/boxes.c1745
-rw-r--r--src/boxes.h239
-rw-r--r--src/common.h24
-rw-r--r--src/constraints.c2367
-rw-r--r--src/constraints.h33
-rw-r--r--src/core.c40
-rw-r--r--src/core.h4
-rw-r--r--src/display.c143
-rw-r--r--src/display.h28
-rw-r--r--src/edge-resistance.c1278
-rw-r--r--src/edge-resistance.h46
-rw-r--r--src/frame.c6
-rw-r--r--src/frames.c30
-rw-r--r--src/keybindings.c359
-rw-r--r--src/menu.c2
-rw-r--r--src/place.c111
-rw-r--r--src/screen.c182
-rw-r--r--src/screen.h13
-rw-r--r--src/session.c49
-rw-r--r--src/stack.c17
-rw-r--r--src/tabpopup.c25
-rw-r--r--src/tabpopup.h6
-rw-r--r--src/testboxes.c1400
-rw-r--r--src/util.c42
-rw-r--r--src/util.h2
-rw-r--r--src/window.c1085
-rw-r--r--src/window.h59
-rw-r--r--src/workspace.c259
-rw-r--r--src/workspace.h20
35 files changed, 7321 insertions, 2701 deletions
diff --git a/.cvsignore b/.cvsignore
index e72ba9b..8c231c0 100644
--- a/.cvsignore
+++ b/.cvsignore
@@ -15,6 +15,7 @@ ltconfig
ltmain.sh
stamp-h
stamp-h.in
+stamp-h1
stamp.h
version.h
config.h.in
diff --git a/ChangeLog b/ChangeLog
index a991789..60718ea 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,114 @@
+2005-11-18 Elijah Newren <newren@gmail.com>
+
+ Merge of all the changes on the constraints_experiments branch.
+ This is just a summary, to get the full ChangeLog of those
+ changes (approx. 2000 lines):
+ cvs -q -z3 update -Pd -r constraints_experiments
+ cvs -q -z3 diff -pu -r CONSTRAINTS_EXPERIMENTS_BRANCHPOINT ChangeLog
+
+ Bugs fixed:
+ unfiled - constraints.c is overly complicated[1]
+ unfiled - constraints.c is not robust when all constraints
+ cannot simultaneously be met (constraints need to be
+ prioritized)
+ unfiled - keep-titlebar-onscreen constraint is decoration
+ unaware (since get_outermost_onscreen_positions()
+ forgets to include decorations)
+ unfiled - keyboard snap-moving and snap-resizing snap to hidden
+ edges
+ 109553 - gravity w/ simultaneous move & resize doesn't work
+ 113601 - maximize vertical and horizontal should toggle and be
+ constrained
+ 122196 - windows show up under vertical panels
+ 122670 - jerky/random resizing of window via keyboard[2]
+ 124582 - keyboard and mouse snap-resizing and snap-moving
+ erroneously moves the window multidimensionally
+ 136307 - don't allow apps to resize themselves off the screen
+ (*cough* filechooser *cough*)
+ 142016, 143784 - windows should not span multiple xineramas
+ unless placed there by the user
+ 143145 - clamp new windows to screensize and force them
+ onscreen, if they'll fit
+ 144126 - Handle pathological strut lists sanely[3]
+ 149867 - fixed aspect ratio windows are difficult to resize[4]
+ 152898 - make screen edges consistent; allow easy slamming of
+ windows into the left, right, and bottom edges of the
+ screen too.
+ 154706 - bouncing weirdness at screen edge with keyboard moving
+ or resizing
+ 156699 - avoid struts when placing windows, if possible (nasty
+ a11y blocker)
+ 302456 - dragging offscreen too restrictive
+ 304857 - wireframe moving off the top of the screen is misleading
+ 308521 - make uni-directional resizing easier with
+ alt-middle-drag and prevent the occasional super
+ annoying resize-the-wrong-side(s) behavior
+ 312007 - snap-resize moves windows with a minimum size
+ constraint
+ 312104 - resizing the top of a window can cause the bottom to
+ grow
+ 319351 - don't instantly snap on mouse-move-snapping, remove
+ braindeadedness of having order of releasing shift and
+ releasing button press matter so much
+
+ [1] fixed in my opinion, anyway.
+ [2] Actually, it's not totally fixed--it's just annoying
+ instead of almost completely unusable. Matthias had a
+ suggestion that may fix the remainder of the problems (see
+ http://tinyurl.com/bwzuu).
+ [3] This bug was originally about not-quite-so-pathological
+ cases but was left open for the worse cases. The code from
+ the branch handles the remainder of the cases mentioned in
+ this bug.
+ [4] Actually, although it's far better there's still some minor
+ issues left: a slight drift that's only noticeable after
+ lots of resizing, and potential problems with partially
+ onscreen constraints due to not clearing any
+ fixed_directions flags (aspect ratio windows get resized in
+ both directions and thus aren't fixed in one of them)
+
+ New feature:
+ 81704 - edge resistance for user move and resize operations;
+ in particular 3 different kinds of resistance are
+ implemented:
+ Pixel-Distance: window movement is resisted when it
+ aligns with an edge unless the movement is greater than
+ a threshold number of pixels
+ Timeout: window movement past an edge is prevented until
+ a certain amount of time has elapsed during the
+ operation since the first request to move it past that
+ edge
+ Keyboard-Buildup: when moving or resizing with the
+ keyboard, once a window is aligned with a certain edge
+ it cannot move past until the correct direction has
+ been pressed enough times (e.g. 2 or 3 times)
+
+ Major changes:
+ - constraints.c has been rewritten; very few lines of code from
+ the old version remain. There is a comment near the top of
+ the function explaining the basics of how the new framework
+ works. A more detailed explanation can be found in
+ doc/how-constraints-works.txt
+ - edge-resistance.[ch] are new files implementing edge-resistance.
+ - boxes.[ch] are new files containing low-level error-prone
+ functions used heavily in constraints.c and edge-resistance.c,
+ among various places throughout the code. testboxes.c
+ contains a thorough testsuite for the boxes.[ch] functions
+ compiled into a program, testboxes.
+ - meta_window_move_resize_internal() *must* be told the gravity
+ of the associated operation (if it's just a move operation,
+ the gravity will be ignored, but for resize and move+resize
+ the correct value is needed)
+ - the craziness of different values that
+ meta_window_move_resize_internal() accepts has been documented
+ in a large comment at the beginning of the function. It may
+ be possible to clean this up some, but until then things will
+ remain as they were before--caller beware.
+ - screen and xinerama usable areas (i.e. places not covered by
+ e.g. panels) are cached in the workspace now, as are the
+ screen and xinerama edges. These get updated with the
+ workarea in src/workspace.c:ensure_work_areas_validated()
+
2005-11-14 Elijah Newren <newren@gmail.com>
* configure.in: post-release version bump to 2.13.2
diff --git a/doc/how-constraints-works.txt b/doc/how-constraints-works.txt
new file mode 100644
index 0000000..327e5fe
--- /dev/null
+++ b/doc/how-constraints-works.txt
@@ -0,0 +1,283 @@
+File contents:
+ Basic Ideas
+ Important points to remember
+ Explanation of fields in the ConstraintInfo struct
+ Gory details of resize_gravity vs. fixed_directions
+
+IMPORTANT NOTE: There's a big comment at the top of constraints.c
+explaining how to add extra constraints or tweak others. Read it. I put
+that information there because it may be enough information by itself for
+people to hack on constraints.c. I won't duplicate that information in
+this file; this file is for deeper details.
+
+
+---------------------------------------------------------------------------
+Basic Ideas
+---------------------------------------------------------------------------
+There are a couple basic ideas behind how this constraints.c code works and
+why it works that way:
+
+ 1) Split the low-level error-prone operations into a special file
+ 2) Add robustness by prioritizing constraints
+ 3) Make use of a minimal spanning set of rectangles for the
+ "onscreen region" (screen minus struts).
+ 4) Constraints can be user-action vs app-action oriented
+ 5) Avoid over-complification ;-)
+
+Some more details explaining these basic ideas:
+
+ 1) Split tedious operations out
+
+ boxes.[ch] have been added which contain many common, tedious, and
+ error-prone operations. I find that this separation helps a lot for
+ managing the complexity and ensuring that things work correctly.
+ Also, note that testboxes.c thoroughly tests all functionality in
+ boxes.[ch] and a testboxes program is automatically compiled.
+
+ Note that functions have also been added to this file to handle some
+ of the tedium necessary for edge resistance as well.
+
+ 2) Prioritize constraints
+
+ In the old code, if each and every constraint could not be
+ simultaneously satisfied, then it would result in some
+ difficult-to-predict set of constraints being violated. This was
+ because constraints were applied in order, with the possibility for
+ each making changes that violated previous constraints, with no
+ checking done at the end.
+
+ Now, all constraints have an associated priority, defined in the
+ ConstraintPriority enum near the top of constraints.c. The
+ constraints are all applied, and then are all checked; if not all are
+ satisfied then the least important constraints are dropped and the
+ process is repeated. This ensures that the most important constraints
+ are satisfied.
+
+ A special note to make here is that if any one given constraint is
+ impossible to satisfy even individually (e.g. if minimum size hints
+ specify a larger window than the screen size, making the
+ fully-onscreen constraint impossible to satisfy) then we treat the
+ constraint as being satisfied. This sounds counter-intuitive, but the
+ idea is that we want to satisfy as many constraints as possible and if
+ we treat it as a violation then all constraints with a lesser priority
+ also get dropped along with the impossible to satisfy one.
+
+ 3) Using maximal/spanning rectangles
+
+ The constraints rely heavily on something I call spanning rectangles
+ (which Soeren referred to as maximal rectangles, a name which I think
+ I like better but I don't want to go change all the code now). These
+ spanning rectangles have the property that a window will fit on the
+ screen if and only if it fits within at least one of the rectangles.
+ Soeren had an alternative way of describing these rectangles, namely
+ that they were rectangles with the property that if you made any of
+ them larger in any direction, they would overlap with struts or be
+ offscreen (with the implicit assumption that there are enough of these
+ rectangles that combined they cover all relevant parts of the screen).
+ Note that, by necessity, these spanning/maximal rectangles will often
+ overlap each other.
+
+ Such a list makes it relatively easy to define operations like
+ window-is-onscreen or clamp-window-to-region or
+ shove-window-into-region. Since we have a on-single-xinerama
+ constraint in addition to the onscreen constraint(s), we cache
+ number_xineramas + 1 of these lists in the workspace. These lists
+ then only need to be updated whenever the workarea is (e.g. when strut
+ list change or screen or xinerama size changes).
+
+ 4) Constraints can be user-action vs app-action oriented
+
+ Such differentiation requires special care for the constraints to be
+ consistent; e.g. if the user does something and one constraint
+ applies, then the app does something you have to be careful that the
+ constraint on the app action doesn't result in some jarring motion.
+
+ In particular, the constraints currently allow offscreen movement or
+ resizing for user actions only. The way consistency is handled is
+ that at the end of the constraints, update_onscreen_requirements()
+ checks to see if the window is offscreen or split across xineramas and
+ updates window->require_fully_onscreen and
+ window->require_on_single_xinerama appropriately.
+
+ 5) Avoid over-complification
+
+ The previous code tried to reform the constraints into terms of a
+ single variable. This made the code rather difficult to
+ understand. ("This is a rather complicated fix for an obscure bug
+ that happened when resizing a window and encountering a constraint
+ such as the top edge of the screen.") It also failed, even on the
+ very example for which it used as justification for the complexity
+ (bug 312104 -- when keyboard resizing the top of the window,
+ Metacity extends the bottom once the titlebar hits the top panel),
+ though the reason why it failed is somewhat mysterious as it should
+ have worked. Further, it didn't really reform the constraints in
+ terms of a single variable -- there was both an x_move_delta and an
+ x_resize_delta, and the existence of both caused bug 109553
+ (gravity with simultaneous move and resize doesn't work)
+
+
+---------------------------------------------------------------------------
+Important points to remember
+---------------------------------------------------------------------------
+
+ - Inner vs Outer window
+
+ Note that because of how configure requests work and
+ meta_window_move_resize_internal() and friends are set up, that the
+ rectangles passed to meta_window_constrain() are with respect to inner
+ window positions instead of outer window positions (meaning that window
+ manager decorations are not included in the position/size). For the
+ constraints that need to be enforced with respect to outer window
+ positions, you'll need to make use of the extend_by_frame() and
+ unextend_by_frame() functions.
+
+ - meta_window_move_resize_internal() accepts a really hairy set of
+ inputs. See the huge comment at the beginning of that function.
+ constraints gets screwed up if that function can't sanitize the input,
+ so be very careful about that. It used to be pretty busted.
+
+
+---------------------------------------------------------------------------
+Explanation of fields in the ConstraintInfo strut
+---------------------------------------------------------------------------
+
+As of the time of this writing, ConstraintInfo had the following fields:
+ orig
+ current
+ fgeom
+ action_type
+ is_user_action
+ resize_gravity
+ fixed_directions
+ work_area_xinerama
+ entire_xinerama
+ usable_screen_region
+ usable_xinerama_region
+
+A brief description of each and/or pointers to more information are found
+below:
+ orig
+ The previous position and size of the window, ignoring any window
+ decorations
+ current
+ The requested position and size of the window, ignoring any window
+ decorations. This rectangle gets modified by the various constraints
+ to specify the allowed position closest to the requested position.
+ fgeom
+ The geometry of the window frame (i.e. "decorations"), if it exists.
+ Otherwise, it's a dummy 0-size frame for convenience (i.e. this pointer
+ is guaranteed to be non-NULL so you don't have to do the stupid check).
+ action_type
+ Whether the action being constrained is a move, resize, or a combined
+ move and resize. Some constraints can run faster with this information
+ (e.g. constraining size increment hints or min size hints don't need to
+ do anything for pure move operations). This may also be used for
+ providing slightly different behavior (e.g. clip-to-region instead of
+ shove-into-region for resize vs. moving operations), but doesn't
+ currently have a lot of use for this.
+ is_user_action
+ Used to determine whether the action being constrained is a user
+ action. If so, certain parts of the constraint may be relaxed. Note
+ that this requires care to get right; see item 4 of the basic ideas
+ section for more details.
+ resize_gravity
+ The gravity used in the resize operation, used in order to make sure
+ windows are resized correctly if constraints specify that their size
+ must be modified. Explained further in the resize_gravity
+ vs. fixed_directions section.
+ fixed_directions
+ There may be multiple solutions to shoving a window back onscreen.
+ Typically, the shortest distance used is the solution picked, but if
+ e.g. an application only moved its window in a single direction, it's
+ more desirable that the window is shoved back in that direction than in
+ a different one. fixed_directions facilitates that. Explained further
+ in the resize_gravity vs. fixed_directions section.
+ work_area_xinerama
+ This region is defined in the workspace and just cached here for
+ convenience. It is basically the area obtained by taking the current
+ xinerama, treating all partial struts as full struts, and then
+ subtracting all struts from the current xinerama region. Useful
+ e.g. for enforcing maximization constraints.
+ entire_xinerama
+ Just a cache of the rectangle corresponding to the entire current
+ xinerama, including struts. Useful e.g. for enforcing fullscreen
+ constraints.
+ usable_screen_region
+ The set of maximal/spanning rectangles for the entire screen; this
+ region doesn't overlap with any struts and helps to enforce
+ e.g. onscreen constraints.
+ usable_xinerama_region
+ The set of maximal/spanning rectangles for the current xinerama; this
+ region doesn't overlap with any struts on the xinerama and helps to
+ enforce e.g. the on-single-xinerama constraint.
+
+
+---------------------------------------------------------------------------
+Gory details of resize_gravity vs. fixed_directions
+---------------------------------------------------------------------------
+
+Note that although resize_gravity and fixed_directions look similar, they
+are used for different purposes:
+
+ - resize_gravity is only for resize operations and is used for
+ constraints unrelated to keeping a window within a certain region
+ - fixed_directions is for both move and resize operations and is
+ specifically for keeping a window within a specified region.
+
+Examples of where each are used:
+
+ - If a window is simultaneously moved and resized to the southeast corner
+ with SouthEastGravity, but it turns out that the window was sized to
+ something smaller than the minimum size hint, then the size_hints
+ constraint should resize the window using the resize_gravity to ensure
+ that the southeast corner doesn't move.
+ - If an application resizes itself so that it grows downward only (which
+ I note could be using any of three different gravities, most likely
+ NorthWest), and happens to put the southeast part of the window under a
+ partial strut, then the window needs to be forced back on screen.
+ (Yes, shoved onscreen and not clipped; see bug 136307). It may be the
+ case that moving the window to the left results in less movement of the
+ window than moving the window up, which, in the absence of fixed
+ directions would cause us to chose moving to the left. But since the
+ user knows that only the height of the window is changing, they would
+ find moving to the left weird (especially if this were a dialog that
+ had been centered on its parent). It'd be better to shove the window
+ upwards so we make sure to keep the left and right sides fixed in this
+ case. Note that moving the window upwards (or leftwards) is probably
+ totally against the gravity in this case; but that's okay because
+ gravity typically assumes there's more than enough onscreen space for
+ the resize and we only override the gravity when that assumption is
+ wrong.
+
+For the paranoid, a fixed directions might give an impossible to fulfill
+constraint (I don't think that's true currently in the code, but I haven't
+thought it through in a while). If this ever becomes a problem, it should
+be relatively simple to throw out the fixed directions when this happens
+and rerun the constraint. Of course, it might be better to rethink things
+to just avoid such a problem.
+
+The nitty gritty of what gets fixed:
+ User move:
+ in x direction - y direction fixed
+ in y direction - x direction fixed
+ in both dirs. - neither direction fixed
+ User resize: (note that for clipping, only 1 side ever changed)
+ in x direction - y direction fixed (technically opposite x side fixed too)
+ in y direction - x direction fixed (technically opposite y side fixed too)
+ in both dirs. - neither direction fixed
+ App move:
+ in x direction - y direction fixed
+ in y direction - x direction fixed
+ in both dirs. - neither direction fixed
+ App resize
+ in x direction - y direction fixed
+ in y direction - x direction fixed
+ in 2 parallel directions (center side gravity) - other dir. fixed
+ in 2 orthogonal directions (corner gravity) - neither dir. fixed
+ in 3 or 4 directions (a center-like gravity) - neither dir. fixed
+ Move & resize
+ Treat like resize case though this will usually mean all four sides
+ change and result in neither direction being fixed
+ Note that in all cases, if neither direction moves it is likely do to a
+ change in struts and thus neither direction should be fixed despite the
+ lack of movement.
diff --git a/src/.cvsignore b/src/.cvsignore
index d3a9165..b998675 100644
--- a/src/.cvsignore
+++ b/src/.cvsignore
@@ -5,6 +5,7 @@ Makefile
metacity
metacity-theme-viewer
metacity-dialog
+testboxes
testgradient
inlinepixbufs.h
metacity.desktop
diff --git a/src/Makefile.am b/src/Makefile.am
index 3ed5e1d..efbc0b2 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -13,6 +13,8 @@ metacity_SOURCES= \
async-getprop.h \
bell.h \
bell.c \
+ boxes.h \
+ boxes.c \
common.h \
compositor.c \
compositor.h \
@@ -25,6 +27,8 @@ metacity_SOURCES= \
display.h \
draw-workspace.c \
draw-workspace.h \
+ edge-resistance.c \
+ edge-resistance.h \
effects.c \
effects.h \
errors.c \
@@ -133,11 +137,13 @@ metacity_LDADD=@METACITY_LIBS@ $(EFENCE)
metacity_theme_viewer_LDADD= @METACITY_LIBS@ libmetacity-private.la
metacity_dialog_LDADD=@METACITY_LIBS@
+testboxes_SOURCES=util.h util.c boxes.h boxes.c testboxes.c
testgradient_SOURCES=gradient.h gradient.c testgradient.c
testasyncgetprop_SOURCES=async-getprop.h async-getprop.c testasyncgetprop.c
-noinst_PROGRAMS=testgradient testasyncgetprop
+noinst_PROGRAMS=testboxes testgradient testasyncgetprop
+testboxes_LDADD= @METACITY_LIBS@
testgradient_LDADD= @METACITY_LIBS@
testasyncgetprop_LDADD= @METACITY_LIBS@
diff --git a/src/bell.c b/src/bell.c
index b002170..cb1cf35 100644
--- a/src/bell.c
+++ b/src/bell.c
@@ -30,8 +30,8 @@ meta_bell_flash_screen (MetaDisplay *display,
MetaScreen *screen)
{
Window root = screen->xroot;
- int width = screen->width;
- int height = screen->height;
+ int width = screen->rect.width;
+ int height = screen->rect.height;
if (screen->flash_window == None)
{
diff --git a/src/boxes.c b/src/boxes.c
new file mode 100644
index 0000000..06f23c0
--- /dev/null
+++ b/src/boxes.c
@@ -0,0 +1,1745 @@
+/* Simple box operations */
+
+/*
+ * Copyright (C) 2005 Elijah Newren
+ * [meta_rectangle_intersect() is copyright the GTK+ Team according to Havoc,
+ * see gdkrectangle.c. As far as Havoc knows, he probably wrote
+ * meta_rectangle_equal(), and I'm guessing it's (C) Red Hat. So...]
+ * Copyright (C) 1995-2000 GTK+ Team
+ * Copyright (C) 2002 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "boxes.h"
+#include "util.h"
+#include <X11/Xutil.h> /* Just for the definition of the various gravities */
+#include <stdio.h> /* For snprintf */
+
+char*
+meta_rectangle_to_string (const MetaRectangle *rect,
+ char *output)
+{
+ /* 25 chars: 2 commas, space, plus, trailing \0 + 5 for each digit.
+ * Should be more than enough space. Note that of this space, the
+ * trailing \0 will be overwritten for all but the last rectangle.
+ */
+ g_snprintf (output, RECT_LENGTH, "%d,%d +%d,%d",
+ rect->x, rect->y, rect->width, rect->height);
+
+ return output;
+}
+
+char*
+meta_rectangle_region_to_string (GList *region,
+ const char *separator_string,
+ char *output)
+{
+ /* 27 chars: 2 commas, 2 square brackets, space, plus, trailing \0 + 5
+ * for each digit. Should be more than enough space. Note that of this
+ * space, the trailing \0 will be overwritten for all but the last
+ * rectangle.
+ */
+ char rect_string[RECT_LENGTH];
+
+ if (region == NULL)
+ snprintf (output, 10, "(EMPTY)");
+
+ char *cur = output;
+ GList *tmp = region;
+ while (tmp)
+ {
+ MetaRectangle *rect = tmp->data;
+ g_snprintf (rect_string, RECT_LENGTH, "[%d,%d +%d,%d]",
+ rect->x, rect->y, rect->width, rect->height);
+ cur = g_stpcpy (cur, rect_string);
+ tmp = tmp->next;
+ if (tmp)
+ cur = g_stpcpy (cur, separator_string);
+ }
+
+ return output;
+}
+
+char*
+meta_rectangle_edge_to_string (const MetaEdge *edge,
+ char *output)
+{
+ /* 25 chars: 2 commas, space, plus, trailing \0 + 5 for each digit.
+ * Should be more than enough space. Note that of this space, the
+ * trailing \0 will be overwritten for all but the last rectangle.
+ *
+ * Plus 2 for parenthesis, 4 for 2 more numbers, 2 more commas, and
+ * 2 more spaces, for a total of 10 more.
+ */
+ g_snprintf (output, EDGE_LENGTH, "[%d,%d +%d,%d], %2d, %2d",
+ edge->rect.x, edge->rect.y, edge->rect.width, edge->rect.height,
+ edge->side_type, edge->edge_type);
+
+ return output;
+}
+
+char*
+meta_rectangle_edge_list_to_string (GList *edge_list,
+ const char *separator_string,
+ char *output)
+{
+ /* 27 chars: 2 commas, 2 square brackets, space, plus, trailing \0 + 5 for
+ * each digit. Should be more than enough space. Note that of this
+ * space, the trailing \0 will be overwritten for all but the last
+ * rectangle.
+ *
+ * Plus 2 for parenthesis, 4 for 2 more numbers, 2 more commas, and
+ * 2 more spaces, for a total of 10 more.
+ */
+ char rect_string[EDGE_LENGTH];
+
+ if (edge_list == NULL)
+ snprintf (output, 10, "(EMPTY)");
+
+ char *cur = output;
+ GList *tmp = edge_list;
+ while (tmp)
+ {
+ MetaEdge *edge = tmp->data;
+ MetaRectangle *rect = &edge->rect;
+ g_snprintf (rect_string, EDGE_LENGTH, "([%d,%d +%d,%d], %2d, %2d)",
+ rect->x, rect->y, rect->width, rect->height,
+ edge->side_type, edge->edge_type);
+ cur = g_stpcpy (cur, rect_string);
+ tmp = tmp->next;
+ if (tmp)
+ cur = g_stpcpy (cur, separator_string);
+ }
+
+ return output;
+}
+
+MetaRectangle
+meta_rect (int x, int y, int width, int height)
+{
+ MetaRectangle temporary;
+ temporary.x = x;
+ temporary.y = y;
+ temporary.width = width;
+ temporary.height = height;
+
+ return temporary;
+}
+
+int
+meta_rectangle_area (const MetaRectangle *rect)
+{
+ g_return_val_if_fail (rect != NULL, 0);
+ return rect->width * rect->height;
+}
+
+gboolean
+meta_rectangle_intersect (const MetaRectangle *src1,
+ const MetaRectangle *src2,
+ MetaRectangle *dest)
+{
+ int dest_x, dest_y;
+ int dest_w, dest_h;
+ int return_val;
+
+ g_return_val_if_fail (src1 != NULL, FALSE);
+ g_return_val_if_fail (src2 != NULL, FALSE);
+ g_return_val_if_fail (dest != NULL, FALSE);
+
+ return_val = FALSE;
+
+ dest_x = MAX (src1->x, src2->x);
+ dest_y = MAX (src1->y, src2->y);
+ dest_w = MIN (src1->x + src1->width, src2->x + src2->width) - dest_x;
+ dest_h = MIN (src1->y + src1->height, src2->y + src2->height) - dest_y;
+
+ if (dest_w > 0 && dest_h > 0)
+ {
+ dest->x = dest_x;
+ dest->y = dest_y;
+ dest->width = dest_w;
+ dest->height = dest_h;
+ return_val = TRUE;
+ }
+ else
+ {
+ dest->width = 0;
+ dest->height = 0;
+ }
+
+ return return_val;
+}
+
+gboolean
+meta_rectangle_equal (const MetaRectangle *src1,
+ const MetaRectangle *src2)
+{
+ return ((src1->x == src2->x) &&
+ (src1->y == src2->y) &&
+ (src1->width == src2->width) &&
+ (src1->height == src2->height));
+}
+
+gboolean
+meta_rectangle_overlap (const MetaRectangle *rect1,
+ const MetaRectangle *rect2)
+{
+ g_return_val_if_fail (rect1 != NULL, FALSE);
+ g_return_val_if_fail (rect2 != NULL, FALSE);
+
+ return !((rect1->x + rect1->width <= rect2->x) ||
+ (rect2->x + rect2->width <= rect1->x) ||
+ (rect1->y + rect1->height <= rect2->y) ||
+ (rect2->y + rect2->height <= rect1->y));
+}
+
+gboolean
+meta_rectangle_vert_overlap (const MetaRectangle *rect1,
+ const MetaRectangle *rect2)
+{
+ return (rect1->y < rect2->y + rect2->height &&
+ rect2->y < rect1->y + rect1->height);
+}
+
+gboolean
+meta_rectangle_horiz_overlap (const MetaRectangle *rect1,
+ const MetaRectangle *rect2)
+{
+ return (rect1->x < rect2->x + rect2->width &&
+ rect2->x < rect1->x + rect1->width);
+}
+
+gboolean
+meta_rectangle_could_fit_rect (const MetaRectangle *outer_rect,
+ const MetaRectangle *inner_rect)
+{
+ return (outer_rect->width >= inner_rect->width &&
+ outer_rect->height >= inner_rect->height);
+}
+
+gboolean
+meta_rectangle_contains_rect (const MetaRectangle *outer_rect,
+ const MetaRectangle *inner_rect)
+{
+ return
+ inner_rect->x >= outer_rect->x &&
+ inner_rect->y >= outer_rect->y &&
+ inner_rect->x + inner_rect->width <= outer_rect->x + outer_rect->width &&
+ inner_rect->y + inner_rect->height <= outer_rect->y + outer_rect->height;
+}
+
+void
+meta_rectangle_resize_with_gravity (const MetaRectangle *old_rect,
+ MetaRectangle *rect,
+ int gravity,
+ int new_width,
+ int new_height)
+{
+ /* FIXME: I'm too deep into this to know whether the below comment is
+ * still clear or not now that I've moved it out of constraints.c.
+ * boxes.h has a good comment, but I'm not sure if the below info is also
+ * helpful on top of that (or whether it has superfluous info).
+ */
+
+ /* These formulas may look overly simplistic at first but you can work
+ * everything out with a left_frame_with, right_frame_width,
+ * border_width, and old and new client area widths (instead of old total
+ * width and new total width) and you come up with the same formulas.
+ *
+ * Also, note that the reason we can treat NorthWestGravity and
+ * StaticGravity the same is because we're not given a location at
+ * which to place the window--the window was already placed
+ * appropriately before. So, NorthWestGravity for this function
+ * means to just leave the upper left corner of the outer window
+ * where it already is, and StaticGravity for this function means to
+ * just leave the upper left corner of the inner window where it
+ * already is. But leaving either of those two corners where they
+ * already are will ensure that the other corner is fixed as well
+ * (since frame size doesn't change)--thus making the two
+ * equivalent.
+ */
+
+ /* First, the x direction */
+ int adjust = 0;
+ switch (gravity)
+ {
+ case NorthWestGravity:
+ case WestGravity:
+ case SouthWestGravity:
+ rect->x = old_rect->x;
+ break;
+
+ case NorthGravity:
+ case CenterGravity:
+ case SouthGravity:
+ /* FIXME: Needing to adjust new_width kind of sucks, but not doing so
+ * would cause drift.
+ */
+ new_width -= (old_rect->width - new_width) % 2;
+ rect->x = old_rect->x + (old_rect->width - new_width)/2;
+ break;
+
+ case NorthEastGravity:
+ case EastGravity:
+ case SouthEastGravity:
+ rect->x = old_rect->x + (old_rect->width - new_width);
+ break;
+
+ case StaticGravity:
+ default:
+ rect->x = old_rect->x;
+ break;
+ }
+ rect->width = new_width;
+
+ /* Next, the y direction */
+ adjust = 0;
+ switch (gravity)
+ {
+ case NorthWestGravity:
+ case NorthGravity:
+ case NorthEastGravity:
+ rect->y = old_rect->y;
+ break;
+
+ case WestGravity:
+ case CenterGravity:
+ case EastGravity:
+ /* FIXME: Needing to adjust new_height kind of sucks, but not doing so
+ * would cause drift.
+ */
+ new_height -= (old_rect->height - new_height) % 2;
+ rect->y = old_rect->y + (old_rect->height - new_height)/2;
+ break;
+
+ case SouthWestGravity:
+ case SouthGravity:
+ case SouthEastGravity:
+ rect->y = old_rect->y + (old_rect->height - new_height);
+ break;
+
+ case StaticGravity:
+ default:
+ rect->y = old_rect->y;
+ break;
+ }
+ rect->height = new_height;
+}
+
+/* Not so simple helper function for get_minimal_spanning_set_for_region() */
+static GList*
+merge_spanning_rects_in_region (GList *region)
+{
+ /* NOTE FOR ANY OPTIMIZATION PEOPLE OUT THERE: Please see the
+ * documentation of get_minimal_spanning_set_for_region() for performance
+ * considerations that also apply to this function.
+ */
+
+ GList* compare;
+ compare = region;
+
+ if (region == NULL)
+ {
+ meta_warning ("Region to merge was empty! Either you have a some "
+ "pathological STRUT list or there's a bug somewhere!\n");
+ return NULL;
+ }
+
+ while (compare && compare->next)
+ {
+ MetaRectangle *a = compare->data;
+ GList *other = compare->next;
+
+ g_assert (a->width > 0 && a->height > 0);
+
+ while (other)
+ {
+ MetaRectangle *b = other->data;
+ GList *delete_me = NULL;
+
+ g_assert (b->width > 0 && b->height > 0);
+
+ /* If a contains b, just remove b */
+ if (meta_rectangle_contains_rect (a, b))
+ {
+ delete_me = other;
+ }
+ /* If b contains a, just remove a */
+ else if (meta_rectangle_contains_rect (a, b))
+ {
+ delete_me = compare;
+ }
+ /* If a and b might be mergeable horizontally */
+ else if (a->y == b->y && a->height == b->height)
+ {
+ /* If a and b overlap */
+ if (meta_rectangle_overlap (a, b))
+ {
+ int new_x = MIN (a->x, b->x);
+ a->width = MAX (a->x + a->width, b->x + b->width) - new_x;
+ a->x = new_x;
+ delete_me = other;
+ }
+ /* If a and b are adjacent */
+ else if (a->x + a->width == b->x || a->x == b->x + b->width)
+ {
+ int new_x = MIN (a->x, b->x);
+ a->width = MAX (a->x + a->width, b->x + b->width) - new_x;
+ a->x = new_x;
+ delete_me = other;
+ }
+ }
+ /* If a and b might be mergeable vertically */
+ else if (a->x == b->x && a->width == b->width)
+ {
+ /* If a and b overlap */
+ if (meta_rectangle_overlap (a, b))
+ {
+ int new_y = MIN (a->y, b->y);
+ a->height = MAX (a->y + a->height, b->y + b->height) - new_y;
+ a->y = new_y;
+ delete_me = other;
+ }
+ /* If a and b are adjacent */
+ else if (a->y + a->height == b->y || a->y == b->y + b->height)
+ {
+ int new_y = MIN (a->y, b->y);
+ a->height = MAX (a->y + a->height, b->y + b->height) - new_y;
+ a->y = new_y;
+ delete_me = other;
+ }
+ }
+
+ other = other->next;
+
+ /* Delete any rectangle in the list that is no longer wanted */
+ if (delete_me != NULL)
+ {
+ /* Deleting the rect we compare others to is a little tricker */
+ if (compare == delete_me)
+ {
+ compare = compare->next;
+ other = compare->next;
+ a = compare->data;
+ }
+
+ /* Okay, we can free it now */
+ g_free (delete_me->data);
+ region = g_list_delete_link (region, delete_me);
+ }
+
+ }
+
+ compare = compare->next;
+ }
+
+ return region;
+}
+
+/* Simple helper function for get_minimal_spanning_set_for_region()... */
+static gint
+compare_rect_areas (gconstpointer a, gconstpointer b)
+{
+ const MetaRectangle *a_rect = (gconstpointer) a;
+ const MetaRectangle *b_rect = (gconstpointer) b;
+
+ int a_area = meta_rectangle_area (a_rect);
+ int b_area = meta_rectangle_area (b_rect);
+
+ return b_area - a_area; /* positive ret value denotes b > a, ... */
+}
+
+/* This function is trying to find a "minimal spanning set (of rectangles)"
+ * for a given region.
+ *
+ * The region is given by taking basic_rect, then removing the areas
+ * covered by all the rectangles in the all_struts list, and then expanding
+ * the resulting region by the given number of pixels in each direction.
+ *
+ * A "minimal spanning set (of rectangles)" is the best name I could come
+ * up with for the concept I had in mind. Basically, for a given region, I
+ * want a set of rectangles with the property that a window is contained in
+ * the region if and only if it is contained within at least one of the
+ * rectangles.
+ *
+ * The GList* returned will be a list of (allocated) MetaRectangles.
+ * The list will need to be freed by calling
+ * meta_rectangle_free_spanning_set() on it (or by manually
+ * implementing that function...)
+ */
+GList*
+meta_rectangle_get_minimal_spanning_set_for_region (
+ const MetaRectangle *basic_rect,
+ const GSList *all_struts)
+{
+ /* NOTE FOR OPTIMIZERS: This function *might* be somewhat slow,
+ * especially due to the call to merge_spanning_rects_in_region() (which
+ * is O(n^2) where n is the size of the list generated in this function).
+ * This is made more onerous due to the fact that it involves a fair
+ * number of memory allocation and deallocation calls. However, n is 1
+ * for default installations of Gnome (because partial struts aren't used
+ * by default and only partial struts increase the size of the spanning
+ * set generated). With one partial strut, n will be 2 or 3. With 2
+ * partial struts, n will probably be 4 or 5. So, n probably isn't large
+ * enough to make this worth bothering. Further, it is only called from
+ * workspace.c:ensure_work_areas_validated (at least as of the time of
+ * writing this comment), which in turn should only be called if the
+ * strut list changes or the screen or xinerama size changes. If it ever
+ * does show up on profiles (most likely because people start using
+ * ridiculously huge numbers of partial struts), possible optimizations
+ * include:
+ *
+ * (1) rewrite merge_spanning_rects_in_region() to be O(n) or O(nlogn).
+ * I'm not totally sure it's possible, but with a couple copies of
+ * the list and sorting them appropriately, I believe it might be.
+ * (2) only call merge_spanning_rects_in_region() with a subset of the
+ * full list of rectangles. I believe from some of my preliminary
+ * debugging and thinking about it that it is possible to figure out
+ * apriori groups of rectangles which are only merge candidates with
+ * each other. (See testboxes.c:get_screen_region() when which==2
+ * and track the steps of this function carefully to see what gave
+ * me the hint that this might work)
+ * (3) figure out how to avoid merge_spanning_rects_in_region(). I think
+ * it might be possible to modify this function to make that
+ * possible, and I spent just a little while thinking about it, but n
+ * wasn't large enough to convince me to care yet.
+ * (4) Some of the stuff Rob mentioned at http://mail.gnome.org/archives\
+ * /metacity-devel-list/2005-November/msg00028.html. (Sorry for the
+ * URL splitting.)
+ */
+
+ GList *ret;
+ GList *tmp_list;
+ const GSList *strut_iter;
+ MetaRectangle *temp_rect;
+
+ /* The algorithm is basically as follows:
+ * Initialize rectangle_set to basic_rect
+ * Foreach strut:
+ * Foreach rectangle in rectangle_set:
+ * - Split the rectangle into new rectangles that don't overlap the
+ * strut (but which are as big as possible otherwise)
+ * - Remove the old (pre-split) rectangle from the rectangle_set,
+ * and replace it with the new rectangles generated from the
+ * splitting
+ */
+
+ temp_rect = g_new (MetaRectangle, 1);
+ *temp_rect = *basic_rect;
+ ret = g_list_prepend (NULL, temp_rect);
+
+ strut_iter = all_struts;
+ while (strut_iter)
+ {
+ GList *rect_iter;
+ MetaRectangle *strut = (MetaRectangle*) strut_iter->data;
+
+ tmp_list = ret;
+ ret = NULL;
+ rect_iter = tmp_list;
+ while (rect_iter)
+ {
+ MetaRectangle *rect = (MetaRectangle*) rect_iter->data;
+ if (!meta_rectangle_overlap (rect, strut))
+ ret = g_list_prepend (ret, rect);
+ else
+ {
+ /* If there is area in rect left of strut */
+ if (rect->x < strut->x)
+ {
+ temp_rect = g_new (MetaRectangle, 1);
+ *temp_rect = *rect;
+ temp_rect->width = strut->x - rect->x;
+ ret = g_list_prepend (ret, temp_rect);
+ }
+ /* If there is area in rect right of strut */
+ if (rect->x + rect->width > strut->x + strut->width)
+ {
+ int new_x;
+ temp_rect = g_new (MetaRectangle, 1);
+ *temp_rect = *rect;
+ new_x = strut->x + strut->width;
+ temp_rect->width = rect->x + rect->width - new_x;
+ temp_rect->x = new_x;
+ ret = g_list_prepend (ret, temp_rect);
+ }
+ /* If there is area in rect above strut */
+ if (rect->y < strut->y)
+ {
+ temp_rect = g_new (MetaRectangle, 1);
+ *temp_rect = *rect;
+ temp_rect->height = strut->y - rect->y;
+ ret = g_list_prepend (ret, temp_rect);
+ }
+ /* If there is area in rect below strut */
+ if (rect->y + rect->height > strut->y + strut->height)
+ {
+ int new_y;
+ temp_rect = g_new (MetaRectangle, 1);
+ *temp_rect = *rect;
+ new_y = strut->y + strut->height;
+ temp_rect->height = rect->y + rect->height - new_y;
+ temp_rect->y = new_y;
+ ret = g_list_prepend (ret, temp_rect);
+ }
+ g_free (rect);
+ }
+ rect_iter = rect_iter->next;
+ }
+ g_list_free (tmp_list);
+ strut_iter = strut_iter->next;
+ }
+
+ /* Sort by maximal area, just because I feel like it... */
+ ret = g_list_sort (ret, compare_rect_areas);
+
+ /* Merge rectangles if possible so that the list really is minimal */
+ ret = merge_spanning_rects_in_region (ret);
+
+ return ret;
+}
+
+GList*
+meta_rectangle_expand_region (GList *region,
+ const int left_expand,
+ const int right_expand,
+ const int top_expand,
+ const int bottom_expand)
+{
+ /* Now it's time to do the directional expansion */
+ GList *tmp_list = region;
+ while (tmp_list)
+ {
+ MetaRectangle *rect = (MetaRectangle*) tmp_list->data;
+ rect->x -= left_expand;
+ rect->width += (left_expand + right_expand);
+ rect->y -= top_expand;
+ rect->height += (top_expand + bottom_expand);
+ tmp_list = tmp_list->next;
+ }
+
+ return region;
+}
+
+void
+meta_rectangle_free_list_and_elements (GList *filled_list)
+{
+ g_list_foreach (filled_list,
+ (void (*)(gpointer,gpointer))&g_free, /* ew, for ugly */
+ NULL);
+ g_list_free (filled_list);
+}
+
+gboolean
+meta_rectangle_could_fit_in_region (const GList *spanning_rects,
+ const MetaRectangle *rect)
+{
+ const GList *temp;
+ gboolean could_fit;
+
+ temp = spanning_rects;
+ could_fit = FALSE;
+ while (!could_fit && temp != NULL)
+ {
+ could_fit = could_fit || meta_rectangle_could_fit_rect (temp->data, rect);
+ temp = temp->next;
+ }
+
+ return could_fit;
+}
+
+gboolean
+meta_rectangle_contained_in_region (const GList *spanning_rects,
+ const MetaRectangle *rect)
+{
+ const GList *temp;
+ gboolean contained;
+
+ temp = spanning_rects;
+ contained = FALSE;
+ while (!contained && temp != NULL)
+ {
+ contained = contained || meta_rectangle_contains_rect (temp->data, rect);
+ temp = temp->next;
+ }
+
+ return contained;
+}
+
+void
+meta_rectangle_clamp_to_fit_into_region (const GList *spanning_rects,
+ FixedDirections fixed_directions,
+ MetaRectangle *rect,
+ const MetaRectangle *min_size)
+{
+ const GList *temp;
+ const MetaRectangle *best_rect = NULL;
+ int best_overlap = 0;
+
+ /* First, find best rectangle from spanning_rects to which we can clamp
+ * rect to fit into.
+ */
+ temp = spanning_rects;
+ while (temp)
+ {
+ int factor = 1;
+ MetaRectangle *compare_rect = temp->data;
+ int maximal_overlap_amount_for_compare;
+
+ /* If x is fixed and the entire width of rect doesn't fit in compare, set
+ * factor to 0.
+ */
+ if ((fixed_directions & FIXED_DIRECTION_X) &&
+ (compare_rect->x > rect->x ||
+ compare_rect->x + compare_rect->width < rect->x + rect->width))
+ factor = 0;
+
+ /* If y is fixed and the entire height of rect doesn't fit in compare, set
+ * factor to 0.
+ */
+ if ((fixed_directions & FIXED_DIRECTION_Y) &&
+ (compare_rect->y > rect->y ||
+ compare_rect->y + compare_rect->height < rect->y + rect->height))
+ factor = 0;
+
+ /* If compare can't hold the min_size window, set factor to 0 */
+ if (compare_rect->width < min_size->width ||
+ compare_rect->height < min_size->height)
+ factor = 0;
+
+ /* Determine maximal overlap amount */
+ maximal_overlap_amount_for_compare =
+ MIN (rect->width, compare_rect->width) *
+ MIN (rect->height, compare_rect->height);
+ maximal_overlap_amount_for_compare *= factor;
+
+ /* See if this is the best rect so far */
+ if (maximal_overlap_amount_for_compare > best_overlap)
+ {
+ best_rect = compare_rect;
+ best_overlap = maximal_overlap_amount_for_compare;
+ }
+
+ temp = temp->next;
+ }
+
+ /* Clamp rect appropriately */
+ if (best_rect == NULL)
+ {
+ meta_warning ("No rect whose size to clamp to found!\n");
+
+ /* If it doesn't fit, at least make it no bigger than it has to be */
+ if (!(fixed_directions & FIXED_DIRECTION_X))
+ rect->width = min_size->width;
+ if (!(fixed_directions & FIXED_DIRECTION_Y))
+ rect->height = min_size->height;
+ }
+ else
+ {
+ rect->width = MIN (rect->width, best_rect->width);
+ rect->height = MIN (rect->height, best_rect->height);
+ }
+}
+
+void
+meta_rectangle_clip_to_region (const GList *spanning_rects,
+ FixedDirections fixed_directions,
+ MetaRectangle *rect)
+{
+ const GList *temp;
+ const MetaRectangle *best_rect = NULL;
+ int best_overlap = 0;
+
+ /* First, find best rectangle from spanning_rects to which we will clip
+ * rect into.
+ */
+ temp = spanning_rects;
+ while (temp)
+ {
+ int factor = 1;
+ MetaRectangle *compare_rect = temp->data;
+ MetaRectangle overlap;
+ int maximal_overlap_amount_for_compare;
+
+ /* If x is fixed and the entire width of rect doesn't fit in compare, set
+ * factor to 0.
+ */
+ if ((fixed_directions & FIXED_DIRECTION_X) &&
+ (compare_rect->x > rect->x ||
+ compare_rect->x + compare_rect->width < rect->x + rect->width))
+ factor = 0;
+
+ /* If y is fixed and the entire height of rect doesn't fit in compare, set
+ * factor to 0.
+ */
+ if ((fixed_directions & FIXED_DIRECTION_Y) &&
+ (compare_rect->y > rect->y ||
+ compare_rect->y + compare_rect->height < rect->y + rect->height))
+ factor = 0;
+
+ /* Determine maximal overlap amount */
+ meta_rectangle_intersect (rect, compare_rect, &overlap);
+ maximal_overlap_amount_for_compare = meta_rectangle_area (&overlap);
+ maximal_overlap_amount_for_compare *= factor;
+
+ /* See if this is the best rect so far */
+ if (maximal_overlap_amount_for_compare > best_overlap)
+ {
+ best_rect = compare_rect;
+ best_overlap = maximal_overlap_amount_for_compare;
+ }
+
+ temp = temp->next;
+ }
+
+ /* Clip rect appropriately */
+ if (best_rect == NULL)
+ meta_warning ("No rect to clip to found!\n");
+ else
+ {
+ /* Extra precaution with checking fixed direction shouldn't be needed
+ * due to logic above, but it shouldn't hurt either.
+ */
+ if (!(fixed_directions & FIXED_DIRECTION_X))
+ {
+ /* Find the new left and right */
+ int new_x = MAX (rect->x, best_rect->x);
+ rect->width = MIN ((rect->x + rect->width) - new_x,
+ (best_rect->x + best_rect->width) - new_x);
+ rect->x = new_x;
+ }
+
+ /* Extra precaution with checking fixed direction shouldn't be needed
+ * due to logic above, but it shouldn't hurt either.
+ */
+ if (!(fixed_directions & FIXED_DIRECTION_Y))
+ {
+ /* Clip the top, if needed */
+ int new_y = MAX (rect->y, best_rect->y);
+ rect->height = MIN ((rect->y + rect->height) - new_y,
+ (best_rect->y + best_rect->height) - new_y);
+ rect->y = new_y;
+ }
+ }
+}
+
+void
+meta_rectangle_shove_into_region (const GList *spanning_rects,
+ FixedDirections fixed_directions,
+ MetaRectangle *rect)
+{
+ const GList *temp;
+ const MetaRectangle *best_rect = NULL;
+ int best_overlap = 0;
+ int shortest_distance = G_MAXINT;
+
+ /* First, find best rectangle from spanning_rects to which we will shove
+ * rect into.
+ */
+ temp = spanning_rects;
+ while (temp)
+ {
+ int factor = 1;
+ MetaRectangle *compare_rect = temp->data;
+ int maximal_overlap_amount_for_compare;
+ int dist_to_compare;
+
+ /* If x is fixed and the entire width of rect doesn't fit in compare, set
+ * factor to 0.
+ */
+ if ((fixed_directions & FIXED_DIRECTION_X) &&
+ (compare_rect->x > rect->x ||
+ compare_rect->x + compare_rect->width < rect->x + rect->width))
+ factor = 0;
+
+ /* If y is fixed and the entire height of rect doesn't fit in compare, set
+ * factor to 0.
+ */
+ if ((fixed_directions & FIXED_DIRECTION_Y) &&
+ (compare_rect->y > rect->y ||
+ compare_rect->y + compare_rect->height < rect->y + rect->height))
+ factor = 0;
+
+ /* Determine maximal overlap amount between rect & compare_rect */
+ maximal_overlap_amount_for_compare =
+ MIN (rect->width, compare_rect->width) *
+ MIN (rect->height, compare_rect->height);
+
+ /* Determine distance necessary to put rect into comapre_rect */
+ dist_to_compare = 0;
+ if (compare_rect->x > rect->x)
+ dist_to_compare += compare_rect->x - rect->x;
+ if (compare_rect->x + compare_rect->width < rect->x + rect->width)
+ dist_to_compare += (rect->x + rect->width) -
+ (compare_rect->x + compare_rect->width);
+ if (compare_rect->y > rect->y)
+ dist_to_compare += compare_rect->y - rect->y;
+ if (compare_rect->y + compare_rect->height < rect->y + rect->height)
+ dist_to_compare += (rect->y + rect->height) -
+ (compare_rect->y + compare_rect->height);
+
+ /* If we'd have to move in the wrong direction, disqualify compare_rect */
+ if (factor == 0)
+ {
+ maximal_overlap_amount_for_compare = 0;
+ dist_to_compare = G_MAXINT;
+ }
+
+ /* See if this is the best rect so far */
+ if ((maximal_overlap_amount_for_compare > best_overlap) ||
+ (maximal_overlap_amount_for_compare == best_overlap &&
+ dist_to_compare < shortest_distance))
+ {
+ best_rect = compare_rect;
+ best_overlap = maximal_overlap_amount_for_compare;
+ shortest_distance = dist_to_compare;
+ }
+
+ temp = temp->next;
+ }
+
+ /* Shove rect appropriately */
+ if (best_rect == NULL)
+ meta_warning ("No rect to shove into found!\n");
+ else
+ {
+ /* Extra precaution with checking fixed direction shouldn't be needed
+ * due to logic above, but it shouldn't hurt either.
+ */
+ if (!(fixed_directions & FIXED_DIRECTION_X))
+ {
+ /* Shove to the right, if needed */
+ if (best_rect->x > rect->x)
+ rect->x = best_rect->x;
+
+ /* Shove to the left, if needed */
+ if (best_rect->x + best_rect->width < rect->x + rect->width)
+ rect->x = (best_rect->x + best_rect->width) - rect->width;
+ }
+
+ /* Extra precaution with checking fixed direction shouldn't be needed
+ * due to logic above, but it shouldn't hurt either.
+ */
+ if (!(fixed_directions & FIXED_DIRECTION_Y))
+ {
+ /* Shove down, if needed */
+ if (best_rect->y > rect->y)
+ rect->y = best_rect->y;
+
+ /* Shove up, if needed */
+ if (best_rect->y + best_rect->height < rect->y + rect->height)
+ rect->y = (best_rect->y + best_rect->height) - rect->height;
+ }
+ }
+}
+
+void
+meta_rectangle_find_linepoint_closest_to_point (double x1,
+ double y1,
+ double x2,
+ double y2,
+ double px,
+ double py,
+ double *valx,
+ double *valy)
+{
+ /* I'll use the shorthand rx, ry for the return values, valx & valy.
+ * Now, we need (rx,ry) to be on the line between (x1,y1) and (x2,y2).
+ * For that to happen, we first need the slope of the line from (x1,y1)
+ * to (rx,ry) must match the slope of (x1,y1) to (x2,y2), i.e.:
+ * (ry-y1) (y2-y1)
+ * ------- = -------
+ * (rx-x1) (x2-x1)
+ * If x1==x2, though, this gives divide by zero errors, so we want to
+ * rewrite the equation by multiplying both sides by (rx-x1)*(x2-x1):
+ * (ry-y1)(x2-x1) = (y2-y1)(rx-x1)
+ * This is a valid requirement even when x1==x2 (when x1==x2, this latter
+ * equation will basically just mean that rx must be equal to both x1 and
+ * x2)
+ *
+ * The other requirement that we have is that the line from (rx,ry) to
+ * (px,py) must be perpendicular to the line from (x1,y1) to (x2,y2). So
+ * we just need to get a vector in the direction of each line, take the
+ * dot product of the two, and ensure that the result is 0:
+ * (rx-px)*(x2-x1) + (ry-py)*(y2-y1) = 0.
+ *
+ * This gives us two equations and two unknowns:
+ *
+ * (ry-y1)(x2-x1) = (y2-y1)(rx-x1)
+ * (rx-px)*(x2-x1) + (ry-py)*(y2-y1) = 0.
+ *
+ * This particular pair of equations is always solvable so long as
+ * (x1,y1) and (x2,y2) are not the same point (and note that anyone who
+ * calls this function that way is braindead because it means that they
+ * really didn't specify a line after all). However, the caller should
+ * be careful to avoid making (x1,y1) and (x2,y2) too close (e.g. like
+ * 10^{-8} apart in each coordinate), otherwise roundoff error could
+ * cause issues. Solving these equations by hand (or using Maple(TM) or
+ * Mathematica(TM) or whatever) results in slightly messy expressions,
+ * but that's all the below few lines do.
+ */
+
+ double diffx, diffy, den;
+ diffx = x2 - x1;
+ diffy = y2 - y1;
+ den = diffx * diffx + diffy * diffy;
+
+ *valx = (py * diffx * diffy + px * diffx * diffx +
+ y2 * x1 * diffy - y1 * x2 * diffy) / den;
+ *valy = (px * diffx * diffy + py * diffy * diffy +
+ x2 * y1 * diffx - x1 * y2 * diffx) / den;
+}
+
+/***************************************************************************/
+/* */
+/* Switching gears to code for edges instead of just rectangles */
+/* */
+/***************************************************************************/
+
+static GList*
+get_rect_minus_overlap (const GList *rect_in_list,
+ MetaRectangle *overlap)
+{
+ MetaRectangle *temp;
+ MetaRectangle *rect = rect_in_list->data;
+ GList *ret = NULL;
+
+ if (BOX_LEFT (*rect) < BOX_LEFT (*overlap))
+ {
+ temp = g_new (MetaRectangle, 1);
+ *temp = *rect;
+ temp->width = BOX_LEFT (*overlap) - BOX_LEFT (*rect);
+ ret = g_list_prepend (ret, temp);
+ }
+ if (BOX_RIGHT (*rect) > BOX_RIGHT (*overlap))
+ {
+ temp = g_new (MetaRectangle, 1);
+ *temp = *rect;
+ temp->x = BOX_RIGHT (*overlap);
+ temp->width = BOX_RIGHT (*rect) - BOX_RIGHT (*overlap);
+ ret = g_list_prepend (ret, temp);
+ }
+ if (BOX_TOP (*rect) < BOX_TOP (*overlap))
+ {
+ temp = g_new (MetaRectangle, 1);
+ temp->x = overlap->x;
+ temp->width = overlap->width;
+ temp->y = BOX_TOP (*rect);
+ temp->height = BOX_TOP (*overlap) - BOX_TOP (*rect);
+ ret = g_list_prepend (ret, temp);
+ }
+ if (BOX_BOTTOM (*rect) > BOX_BOTTOM (*overlap))
+ {
+ temp = g_new (MetaRectangle, 1);
+ temp->x = overlap->x;
+ temp->width = overlap->width;
+ temp->y = BOX_BOTTOM (*overlap);
+ temp->height = BOX_BOTTOM (*rect) - BOX_BOTTOM (*overlap);
+ ret = g_list_prepend (ret, temp);
+ }
+
+ return ret;
+}
+
+static GList*
+replace_rect_with_list (GList *old_element,
+ GList *new_list)
+{
+ GList *ret;
+ g_assert (old_element != NULL);
+
+ if (!new_list)
+ {
+ /* If there is no new list, just remove the old_element */
+ ret = old_element->next;
+ g_list_remove_link (old_element, old_element);
+ }
+ else
+ {
+ /* Fix up the prev and next pointers everywhere */
+ ret = new_list;
+ if (old_element->prev)
+ {
+ old_element->prev->next = new_list;
+ new_list->prev = old_element->prev;
+ }
+ if (old_element->next)
+ {
+ GList *tmp = g_list_last (new_list);
+ old_element->next->prev = tmp;
+ tmp->next = old_element->next;
+ }
+ }
+
+ /* Free the old_element and return the appropriate "next" point */
+ g_free (old_element->data);
+ g_list_free_1 (old_element);
+ return ret;
+}
+
+/* Make a copy of the strut list, make sure that copy only contains parts
+ * of the old_struts that intersect with the rection rect, and then do some
+ * magic to make all the new struts disjoint (okay, we we break up struts
+ * that aren't disjoint in a way that the overlapping part is only included
+ * once, so it's not really magic...).
+ */
+static GList*
+get_disjoint_strut_list_in_region (const GSList *old_struts,
+ const MetaRectangle *region)
+{
+ GList *struts;
+ GList *tmp;
+
+ /* First, copy the list */
+ struts = NULL;
+ while (old_struts)
+ {
+ MetaRectangle *cur = old_struts->data;
+ MetaRectangle *copy = g_new (MetaRectangle, 1);
+ *copy = *cur;
+ if (meta_rectangle_intersect (copy, region, copy))
+ struts = g_list_prepend (struts, copy);
+ else
+ g_free (copy);
+
+ old_struts = old_struts->next;
+ }
+
+ /* Now, loop over the list and check for intersections, fixing things up
+ * where they do intersect.
+ */
+ tmp = struts;
+ while (tmp)
+ {
+ GList *compare;
+
+ MetaRectangle *cur = tmp->data;
+
+ compare = tmp->next;
+ while (compare)
+ {
+ MetaRectangle *comp = compare->data;
+ MetaRectangle overlap;
+
+ if (meta_rectangle_intersect (cur, comp, &overlap))
+ {
+ /* Get a list of rectangles for each strut that don't overlap
+ * the intersection region.
+ */
+ GList *cur_leftover = get_rect_minus_overlap (tmp, &overlap);
+ GList *comp_leftover = get_rect_minus_overlap (compare, &overlap);
+
+ /* Add the intersection region to cur_leftover */
+ MetaRectangle *overlap_allocated = g_new (MetaRectangle, 1);
+ *overlap_allocated = overlap;
+ cur_leftover = g_list_prepend (cur_leftover, overlap_allocated);
+
+ /* Fix up tmp, compare, and cur -- maybe struts too */
+ if (struts == tmp)
+ {
+ struts = replace_rect_with_list (tmp, cur_leftover);
+ tmp = struts;
+ }
+ else
+ tmp = replace_rect_with_list (tmp, cur_leftover);
+ compare = replace_rect_with_list (compare, comp_leftover);
+
+ if (compare == NULL)
+ break;
+
+ cur = tmp->data;
+ }
+
+ compare = compare->next;
+ }
+
+ tmp = tmp->next;
+ }
+
+ return struts;
+}
+
+/* To make things easily testable, provide a nice way of sorting edges */
+gint
+meta_rectangle_edge_cmp (gconstpointer a, gconstpointer b)
+{
+ const MetaEdge *a_edge_rect = (gconstpointer) a;
+ const MetaEdge *b_edge_rect = (gconstpointer) b;
+
+ int a_compare, b_compare;
+
+ a_compare = a_edge_rect->side_type;
+ b_compare = b_edge_rect->side_type;
+
+ if (a_compare == b_compare)
+ {
+ if (a_edge_rect->side_type == META_DIRECTION_LEFT ||
+ a_edge_rect->side_type == META_DIRECTION_RIGHT)
+ {
+ a_compare = a_edge_rect->rect.x;
+ b_compare = b_edge_rect->rect.x;
+ if (a_compare == b_compare)
+ {
+ a_compare = a_edge_rect->rect.y;
+ b_compare = b_edge_rect->rect.y;
+ }
+ }
+ else if (a_edge_rect->side_type == META_DIRECTION_TOP ||
+ a_edge_rect->side_type == META_DIRECTION_BOTTOM)
+ {
+ a_compare = a_edge_rect->rect.y;
+ b_compare = b_edge_rect->rect.y;
+ if (a_compare == b_compare)
+ {
+ a_compare = a_edge_rect->rect.x;
+ b_compare = b_edge_rect->rect.x;
+ }
+ }
+ else
+ g_assert ("Some idiot wanted to sort sides of different types.\n");
+ }
+
+ return a_compare - b_compare; /* positive value denotes a > b ... */
+}
+
+/* Determine whether two given edges overlap */
+static gboolean
+edges_overlap (const MetaEdge *edge1,
+ const MetaEdge *edge2)
+{
+ if (edge1->rect.width == 0 && edge2->rect.width == 0)
+ {
+ return meta_rectangle_vert_overlap (&edge1->rect, &edge2->rect) &&
+ edge1->rect.x == edge2->rect.x;
+ }
+ else if (edge1->rect.height == 0 && edge2->rect.height == 0)
+ {
+ return meta_rectangle_horiz_overlap (&edge1->rect, &edge2->rect) &&
+ edge1->rect.y == edge2->rect.y;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+static gboolean
+rectangle_and_edge_intersection (const MetaRectangle *rect,
+ const MetaEdge *edge,
+ MetaEdge *overlap,
+ int *handle_type)
+{
+ const MetaRectangle *rect2 = &edge->rect;
+ MetaRectangle *result = &overlap->rect;
+ gboolean intersect = TRUE;
+
+ /* We don't know how to set these, so set them to invalid values */
+ overlap->edge_type = -1;
+ overlap->side_type = -1;
+
+ /* Figure out what the intersection is */
+ result->x = MAX (rect->x, rect2->x);
+ result->y = MAX (rect->y, rect2->y);
+ result->width = MIN (BOX_RIGHT (*rect), BOX_RIGHT (*rect2)) - result->x;
+ result->height = MIN (BOX_BOTTOM (*rect), BOX_BOTTOM (*rect2)) - result->y;
+
+ /* Find out if the intersection is empty; have to do it this way since
+ * edges have a thickness of 0
+ */
+ if ((result->width < 0 || result->height < 0) ||
+ (result->width == 0 && result->height == 0))
+ {
+ result->width = 0;
+ result->height = 0;
+ intersect = FALSE;
+ }
+ else
+ {
+ /* Need to figure out the handle_type, a somewhat weird quantity:
+ * 0 - overlap is in middle of rect
+ * -1 - overlap is at the side of rect, and is on the opposite side
+ * of rect than the edge->side_type side
+ * 1 - overlap is at the side of rect, and the side of rect it is
+ * on is the edge->side_type side
+ */
+ switch (edge->side_type)
+ {
+ case META_DIRECTION_LEFT:
+ if (result->x == rect->x)
+ *handle_type = 1;
+ else if (result->x == BOX_RIGHT (*rect))
+ *handle_type = -1;
+ else
+ *handle_type = 0;
+ break;
+ case META_DIRECTION_RIGHT:
+ if (result->x == rect->x)
+ *handle_type = -1;
+ else if (result->x == BOX_RIGHT (*rect))
+ *handle_type = 1;
+ else
+ *handle_type = 0;
+ break;
+ case META_DIRECTION_TOP:
+ if (result->y == rect->y)
+ *handle_type = 1;
+ else if (result->y == BOX_BOTTOM (*rect))
+ *handle_type = -1;
+ else
+ *handle_type = 0;
+ break;
+ case META_DIRECTION_BOTTOM:
+ if (result->y == rect->y)
+ *handle_type = -1;
+ else if (result->y == BOX_BOTTOM (*rect))
+ *handle_type = 1;
+ else
+ *handle_type = 0;
+ break;
+ }
+ }
+ return intersect;
+}
+
+/* Add all edges of the given rect to cur_edges and return the result. If
+ * rect_is_internal is false, the side types are switched (LEFT<->RIGHT and
+ * TOP<->BOTTOM).
+ */
+static GList*
+add_edges (GList *cur_edges,
+ const MetaRectangle *rect,
+ gboolean rect_is_internal)
+{
+ MetaEdge *temp_edge;
+ int i;
+
+ for (i=0; i<4; i++)
+ {
+ temp_edge = g_new (MetaEdge, 1);
+ temp_edge->rect = *rect;
+ switch (i)
+ {
+ case 0:
+ temp_edge->side_type =
+ rect_is_internal ? META_DIRECTION_LEFT : META_DIRECTION_RIGHT;
+ temp_edge->rect.width = 0;
+ break;
+ case 1:
+ temp_edge->side_type =
+ rect_is_internal ? META_DIRECTION_RIGHT : META_DIRECTION_LEFT;
+ temp_edge->rect.x += temp_edge->rect.width;
+ temp_edge->rect.width = 0;
+ break;
+ case 2:
+ temp_edge->side_type =
+ rect_is_internal ? META_DIRECTION_TOP : META_DIRECTION_BOTTOM;
+ temp_edge->rect.height = 0;
+ break;
+ case 3:
+ temp_edge->side_type =
+ rect_is_internal ? META_DIRECTION_BOTTOM : META_DIRECTION_TOP;
+ temp_edge->rect.y += temp_edge->rect.height;
+ temp_edge->rect.height = 0;
+ break;
+ }
+ temp_edge->edge_type = META_EDGE_SCREEN;
+ cur_edges = g_list_prepend (cur_edges, temp_edge);
+ }
+
+ return cur_edges;
+}
+
+/* Remove any part of old_edge that intersects remove and add any resulting
+ * edges to cur_list. Return cur_list when finished.
+ */
+static GList*
+split_edge (GList *cur_list,
+ const MetaEdge *old_edge,
+ const MetaEdge *remove)
+{
+ MetaEdge *temp_edge;
+ switch (old_edge->side_type)
+ {
+ case META_DIRECTION_LEFT:
+ case META_DIRECTION_RIGHT:
+ g_assert (meta_rectangle_vert_overlap (&old_edge->rect, &remove->rect));
+ if (BOX_TOP (old_edge->rect) < BOX_TOP (remove->rect))
+ {
+ temp_edge = g_new (MetaEdge, 1);
+ *temp_edge = *old_edge;
+ temp_edge->rect.height = BOX_TOP (remove->rect)
+ - BOX_TOP (old_edge->rect);
+ cur_list = g_list_prepend (cur_list, temp_edge);
+ }
+ if (BOX_BOTTOM (old_edge->rect) > BOX_BOTTOM (remove->rect))
+ {
+ temp_edge = g_new (MetaEdge, 1);
+ *temp_edge = *old_edge;
+ temp_edge->rect.y = BOX_BOTTOM (remove->rect);
+ temp_edge->rect.height = BOX_BOTTOM (old_edge->rect)
+ - BOX_BOTTOM (remove->rect);
+ cur_list = g_list_prepend (cur_list, temp_edge);
+ }
+ break;
+ case META_DIRECTION_TOP:
+ case META_DIRECTION_BOTTOM:
+ g_assert (meta_rectangle_horiz_overlap (&old_edge->rect, &remove->rect));
+ if (BOX_LEFT (old_edge->rect) < BOX_LEFT (remove->rect))
+ {
+ temp_edge = g_new (MetaEdge, 1);
+ *temp_edge = *old_edge;
+ temp_edge->rect.width = BOX_LEFT (remove->rect)
+ - BOX_LEFT (old_edge->rect);
+ cur_list = g_list_prepend (cur_list, temp_edge);
+ }
+ if (BOX_RIGHT (old_edge->rect) > BOX_RIGHT (remove->rect))
+ {
+ temp_edge = g_new (MetaEdge, 1);
+ *temp_edge = *old_edge;
+ temp_edge->rect.x = BOX_RIGHT (remove->rect);
+ temp_edge->rect.width = BOX_RIGHT (old_edge->rect)
+ - BOX_RIGHT (remove->rect);
+ cur_list = g_list_prepend (cur_list, temp_edge);
+ }
+ break;
+ }
+
+ return cur_list;
+}
+
+/* Split up edge and remove preliminary edges from strut_edges depending on
+ * if and how strut and edge intersect.
+ */
+static void
+fix_up_edges (MetaRectangle *strut, MetaEdge *edge,
+ GList **strut_edges, GList **edge_splits,
+ gboolean *edge_needs_removal)
+{
+ MetaEdge overlap;
+ int handle_type;
+
+ if (!rectangle_and_edge_intersection (strut, edge, &overlap, &handle_type))
+ return;
+
+ if (handle_type == 0 || handle_type == 1)
+ {
+ /* Put the result of removing overlap from edge into edge_splits */
+ *edge_splits = split_edge (*edge_splits, edge, &overlap);
+ *edge_needs_removal = TRUE;
+ }
+
+ if (handle_type == -1 || handle_type == 1)
+ {
+ /* Remove the overlap from strut_edges */
+ /* First, loop over the edges of the strut */
+ GList *tmp = *strut_edges;
+ while (tmp)
+ {
+ MetaEdge *cur = tmp->data;
+ /* If this is the edge that overlaps, then we need to split it */
+ if (edges_overlap (cur, &overlap))
+ {
+ /* Split this edge into some new ones */
+ *strut_edges = split_edge (*strut_edges, cur, &overlap);
+
+ /* Delete the old one */
+ GList *delete_me = tmp;
+ tmp = tmp->next;
+ g_free (cur);
+ *strut_edges = g_list_delete_link (*strut_edges, delete_me);
+ }
+ else
+ tmp = tmp->next;
+ }
+ }
+}
+
+/* This function removes intersections of edges with the rectangles from the
+ * list of edges.
+ */
+GList*
+meta_rectangle_remove_intersections_with_boxes_from_edges (
+ GList *edges,
+ const GSList *rectangles)
+{
+ const GSList *rect_iter;
+ const int opposing = 1;
+
+ /* Now remove all intersections of rectangles with the edge list */
+ rect_iter = rectangles;
+ while (rect_iter)
+ {
+ MetaRectangle *rect = rect_iter->data;
+ GList *edge_iter = edges;
+ while (edge_iter)
+ {
+ MetaEdge *edge = edge_iter->data;
+ MetaEdge overlap;
+ int handle;
+ gboolean edge_iter_advanced = FALSE;
+
+ /* If this edge overlaps with this rect... */
+ if (rectangle_and_edge_intersection (rect, edge, &overlap, &handle))
+ {
+
+ /* "Intersections" where the edges touch but are opposite
+ * sides (e.g. a left edge against the right edge) should not
+ * be split. Note that the comments in
+ * rectangle_and_edge_intersection() say that opposing edges
+ * occur when handle is -1, BUT you need to remember that we
+ * treat the left side of a window as a right edge because
+ * it's what the right side of the window being moved should
+ * be-resisted-by/snap-to. So opposing is really 1. Anyway,
+ * we just keep track of it in the opposing constant set up
+ * above and if handle isn't equal to that, then we know the
+ * edge should be split.
+ */
+ if (handle != opposing)
+ {
+ /* Keep track of this edge so we can delete it below */
+ GList *delete_me = edge_iter;
+ edge_iter = edge_iter->next;
+ edge_iter_advanced = TRUE;
+
+ /* Split the edge and add the result to beginning of edges */
+ edges = split_edge (edges, edge, &overlap);
+
+ /* Now free the edge... */
+ g_free (edge);
+ edges = g_list_delete_link (edges, delete_me);
+ }
+ }
+
+ if (!edge_iter_advanced)
+ edge_iter = edge_iter->next;
+ }
+
+ rect_iter = rect_iter->next;
+ }
+
+ return edges;
+}
+
+/* This function is trying to find all the edges of an onscreen region. */
+GList*
+meta_rectangle_find_onscreen_edges (const MetaRectangle *basic_rect,
+ const GSList *all_struts)
+{
+ GList *ret;
+ GList *fixed_struts;
+ GList *edge_iter;
+ const GList *strut_iter;
+
+ /* The algorithm is basically as follows:
+ * Make sure the struts are disjoint
+ * Initialize the edge_set to the edges of basic_rect
+ * Foreach strut:
+ * Put together a preliminary new edge from the edges of the strut
+ * Foreach edge in edge_set:
+ * - Split the edge if it is partially contained inside the strut
+ * - If the edge matches an edge of the strut (i.e. a strut just
+ * against the edge of the screen or a not-next-to-edge-of-screen
+ * strut adjacent to another), then both the edge from the
+ * edge_set and the preliminary edge for the strut will need to
+ * be split
+ * Add any remaining "preliminary" strut edges to the edge_set
+ */
+
+ /* Make sure the struts are disjoint */
+ fixed_struts = get_disjoint_strut_list_in_region (all_struts, basic_rect);
+
+ /* Start off the list with the edges of basic_rect */
+ ret = add_edges (NULL, basic_rect, TRUE);
+
+ strut_iter = fixed_struts;
+ while (strut_iter)
+ {
+ MetaRectangle *strut = (MetaRectangle*) strut_iter->data;
+
+ /* Get the new possible edges we may need to add from the strut */
+ GList *new_strut_edges = add_edges (NULL, strut, FALSE);
+
+ edge_iter = ret;
+ while (edge_iter)
+ {
+ MetaEdge *cur_edge = edge_iter->data;
+ GList *splits_of_cur_edge = NULL;
+ gboolean edge_needs_removal = FALSE;
+
+ fix_up_edges (strut, cur_edge,
+ &new_strut_edges, &splits_of_cur_edge,
+ &edge_needs_removal);
+
+ if (edge_needs_removal)
+ {
+ /* Delete the old edge */
+ GList *delete_me = edge_iter;
+ edge_iter = edge_iter->next;
+ g_free (cur_edge);
+ ret = g_list_delete_link (ret, delete_me);
+
+ /* Add the new split parts of the edge */
+ ret = g_list_concat (splits_of_cur_edge, ret);
+ }
+ else
+ {
+ edge_iter = edge_iter->next;
+ }
+
+ /* edge_iter was already advanced above */
+ }
+
+ ret = g_list_concat (new_strut_edges, ret);
+ strut_iter = strut_iter->next;
+ }
+
+ /* Sort the list */
+ ret = g_list_sort (ret, meta_rectangle_edge_cmp);
+
+ /* Free the fixed struts list */
+ meta_rectangle_free_list_and_elements (fixed_struts);
+
+ return ret;
+}
+
+GList*
+meta_rectangle_find_nonintersected_xinerama_edges (
+ const GList *xinerama_rects,
+ const GSList *all_struts)
+{
+ /* This function cannot easily be merged with
+ * meta_rectangle_find_onscreen_edges() because real screen edges
+ * and strut edges both are of the type "there ain't anything
+ * immediately on the other side"; xinerama edges are different.
+ */
+ GList *ret;
+ const GList *cur;
+
+ /* Initialize the return list to be empty */
+ ret = NULL;
+
+ /* start of ret with all the edges of xineramas that are adjacent to
+ * another xinerama.
+ */
+ cur = xinerama_rects;
+ while (cur)
+ {
+ MetaRectangle *cur_rect = cur->data;
+ const GList *compare = xinerama_rects;
+ while (compare)
+ {
+ MetaRectangle *compare_rect = compare->data;
+
+ /* Check if cur might be horizontally adjacent to compare */
+ if (meta_rectangle_vert_overlap(cur_rect, compare_rect))
+ {
+ MetaDirection side_type;
+ int y = MAX (cur_rect->y, compare_rect->y);
+ int height = MIN (BOX_BOTTOM (*cur_rect) - y,
+ BOX_BOTTOM (*compare_rect) - y);
+ int width = 0;
+ int x;
+
+ if (BOX_LEFT (*cur_rect) == BOX_RIGHT (*compare_rect))
+ {
+ /* compare_rect is to the left of cur_rect */
+ x = BOX_LEFT (*cur_rect);
+ side_type = META_DIRECTION_LEFT;
+ }
+ else if (BOX_RIGHT (*cur_rect) == BOX_LEFT (*compare_rect))
+ {
+ /* compare_rect is to the right of cur_rect */
+ x = BOX_RIGHT (*cur_rect);
+ side_type = META_DIRECTION_RIGHT;
+ }
+ else
+ /* These rectangles aren't adjacent after all */
+ x = INT_MIN;
+
+ /* If the rectangles really are adjacent */
+ if (x != INT_MIN)
+ {
+ /* We need a left edge for the xinerama on the right, and
+ * a right edge for the xinerama on the left. Just fill
+ * up the edges and stick 'em on the list.
+ */
+ MetaEdge *new_edge = g_new (MetaEdge, 1);
+
+ new_edge->rect = meta_rect (x, y, width, height);
+ new_edge->side_type = side_type;
+ new_edge->edge_type = META_EDGE_XINERAMA;
+
+ ret = g_list_prepend (ret, new_edge);
+ }
+ }
+
+ /* Check if cur might be vertically adjacent to compare */
+ if (meta_rectangle_horiz_overlap(cur_rect, compare_rect))
+ {
+ MetaDirection side_type;
+ int x = MAX (cur_rect->x, compare_rect->x);
+ int width = MIN (BOX_RIGHT (*cur_rect) - x,
+ BOX_RIGHT (*compare_rect) - x);
+ int height = 0;
+ int y;
+
+ if (BOX_TOP (*cur_rect) == BOX_BOTTOM (*compare_rect))
+ {
+ /* compare_rect is to the top of cur_rect */
+ y = BOX_TOP (*cur_rect);
+ side_type = META_DIRECTION_TOP;
+ }
+ else if (BOX_BOTTOM (*cur_rect) == BOX_TOP (*compare_rect))
+ {
+ /* compare_rect is to the bottom of cur_rect */
+ y = BOX_BOTTOM (*cur_rect);
+ side_type = META_DIRECTION_BOTTOM;
+ }
+ else
+ /* These rectangles aren't adjacent after all */
+ y = INT_MIN;
+
+ /* If the rectangles really are adjacent */
+ if (y != INT_MIN)
+ {
+ /* We need a top edge for the xinerama on the bottom, and
+ * a bottom edge for the xinerama on the top. Just fill
+ * up the edges and stick 'em on the list.
+ */
+ MetaEdge *new_edge = g_new (MetaEdge, 1);
+
+ new_edge->rect = meta_rect (x, y, width, height);
+ new_edge->side_type = side_type;
+ new_edge->edge_type = META_EDGE_XINERAMA;
+
+ ret = g_list_prepend (ret, new_edge);
+ }
+ }
+
+ compare = compare->next;
+ }
+ cur = cur->next;
+ }
+
+ ret = meta_rectangle_remove_intersections_with_boxes_from_edges (ret,
+ all_struts);
+
+ /* Sort the list */
+ ret = g_list_sort (ret, meta_rectangle_edge_cmp);
+
+ return ret;
+}
diff --git a/src/boxes.h b/src/boxes.h
new file mode 100644
index 0000000..cea7cf3
--- /dev/null
+++ b/src/boxes.h
@@ -0,0 +1,239 @@
+/* Simple box operations */
+
+/*
+ * Copyright (C) 2005 Elijah Newren
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef META_BOXES_H
+#define META_BOXES_H
+
+#include <glib.h>
+#include "common.h"
+
+typedef struct _MetaRectangle MetaRectangle;
+
+struct _MetaRectangle
+{
+ int x;
+ int y;
+ int width;
+ int height;
+};
+
+#define BOX_LEFT(box) ((box).x) /* Leftmost pixel of rect */
+#define BOX_RIGHT(box) ((box).x + (box).width) /* One pixel past right */
+#define BOX_TOP(box) ((box).y) /* Topmost pixel of rect */
+#define BOX_BOTTOM(box) ((box).y + (box).height) /* One pixel past bottom */
+
+typedef enum
+{
+ FIXED_DIRECTION_X = 1 << 0,
+ FIXED_DIRECTION_Y = 1 << 1,
+} FixedDirections;
+
+typedef enum
+{
+ META_EDGE_WINDOW,
+ META_EDGE_XINERAMA,
+ META_EDGE_SCREEN
+} MetaEdgeType;
+
+typedef struct _MetaEdge MetaEdge;
+struct _MetaEdge
+{
+ MetaRectangle rect; /* width or height should be 1 */
+ MetaDirection side_type; /* should only have 1 of the 4 directions set */
+ MetaEdgeType edge_type;
+};
+
+/* Output functions -- note that the output buffer had better be big enough:
+ * rect_to_string: RECT_LENGTH
+ * region_to_string: (RECT_LENGTH+strlen(separator_string)) *
+ * g_list_length (region)
+ * edge_to_string: EDGE_LENGTH
+ * edge_list_to_...: (EDGE_LENGTH+strlen(separator_string)) *
+ * g_list_length (edge_list)
+ */
+#define RECT_LENGTH 27
+#define EDGE_LENGTH 37
+char* meta_rectangle_to_string (const MetaRectangle *rect,
+ char *output);
+char* meta_rectangle_region_to_string (GList *region,
+ const char *separator_string,
+ char *output);
+char* meta_rectangle_edge_to_string (const MetaEdge *edge,
+ char *output);
+char* meta_rectangle_edge_list_to_string (
+ GList *edge_list,
+ const char *separator_string,
+ char *output);
+
+/* Function to make initializing a rect with a single line of code easy */
+MetaRectangle meta_rect (int x, int y, int width, int height);
+
+/* Basic comparison functions */
+int meta_rectangle_area (const MetaRectangle *rect);
+gboolean meta_rectangle_intersect (const MetaRectangle *src1,
+ const MetaRectangle *src2,
+ MetaRectangle *dest);
+gboolean meta_rectangle_equal (const MetaRectangle *src1,
+ const MetaRectangle *src2);
+
+/* overlap is similar to intersect but doesn't provide location of
+ * intersection information.
+ */
+gboolean meta_rectangle_overlap (const MetaRectangle *rect1,
+ const MetaRectangle *rect2);
+
+/* vert_overlap means ignore the horizontal location and ask if the
+ * vertical parts overlap. An alternate way to think of it is "Does there
+ * exist a way to shift either rect horizontally so that the two rects
+ * overlap?" horiz_overlap is similar.
+ */
+gboolean meta_rectangle_vert_overlap (const MetaRectangle *rect1,
+ const MetaRectangle *rect2);
+gboolean meta_rectangle_horiz_overlap (const MetaRectangle *rect1,
+ const MetaRectangle *rect2);
+
+/* could_fit_rect determines whether "outer_rect" is big enough to contain
+ * inner_rect. contains_rect checks whether it actually contains it.
+ */
+gboolean meta_rectangle_could_fit_rect (const MetaRectangle *outer_rect,
+ const MetaRectangle *inner_rect);
+gboolean meta_rectangle_contains_rect (const MetaRectangle *outer_rect,
+ const MetaRectangle *inner_rect);
+
+/* Resize old_rect to the given new_width and new_height, but store the
+ * result in rect. NOTE THAT THIS IS RESIZE ONLY SO IT CANNOT BE USED FOR
+ * A MOVERESIZE OPERATION (that simplies the routine a little bit as it
+ * means there's no difference between NorthWestGravity and StaticGravity.
+ * Also, I lied a little bit--technically, you could use it in a MoveResize
+ * operation if you muck with old_rect just right).
+ */
+void meta_rectangle_resize_with_gravity (const MetaRectangle *old_rect,
+ MetaRectangle *rect,
+ int gravity,
+ int new_width,
+ int new_height);
+
+/* find a list of rectangles with the property that a window is contained
+ * in the given region if and only if it is contained in one of the
+ * rectangles in the list.
+ *
+ * In this case, the region is given by taking basic_rect, removing from
+ * it the intersections with all the rectangles in the all_struts list,
+ * then expanding all the rectangles in the resulting list by the given
+ * amounts on each side.
+ *
+ * See boxes.c for more details.
+ */
+GList* meta_rectangle_get_minimal_spanning_set_for_region (
+ const MetaRectangle *basic_rect,
+ const GSList *all_struts);
+
+GList* meta_rectangle_expand_region (GList *region,
+ const int left_expand,
+ const int right_expand,
+ const int top_expand,
+ const int bottom_expand);
+
+/* Free the list created by
+ * meta_rectangle_get_minimal_spanning_set_for_region()
+ * or
+ * meta_rectangle_find_onscreen_edges ()
+ * or
+ * meta_rectangle_find_nonintersected_xinerama_edges()
+ */
+void meta_rectangle_free_list_and_elements (GList *filled_list);
+
+/* could_fit_in_region determines whether one of the spanning_rects is
+ * big enough to contain rect. contained_in_region checks whether one
+ * actually contains it.
+ */
+gboolean meta_rectangle_could_fit_in_region (
+ const GList *spanning_rects,
+ const MetaRectangle *rect);
+gboolean meta_rectangle_contained_in_region (
+ const GList *spanning_rects,
+ const MetaRectangle *rect);
+
+/* Make the rectangle small enough to fit into one of the spanning_rects,
+ * but make it no smaller than min_size.
+ */
+void meta_rectangle_clamp_to_fit_into_region (
+ const GList *spanning_rects,
+ FixedDirections fixed_directions,
+ MetaRectangle *rect,
+ const MetaRectangle *min_size);
+
+/* Clip the rectangle so that it fits into one of the spanning_rects, assuming
+ * it overlaps with at least one of them
+ */
+void meta_rectangle_clip_to_region (const GList *spanning_rects,
+ FixedDirections fixed_directions,
+ MetaRectangle *rect);
+
+/* Shove the rectangle into one of the spanning_rects, assuming it fits in
+ * one of them.
+ */
+void meta_rectangle_shove_into_region(
+ const GList *spanning_rects,
+ FixedDirections fixed_directions,
+ MetaRectangle *rect);
+
+/* Finds the point on the line connecting (x1,y1) to (x2,y2) which is closest
+ * to (px, py). Useful for finding an optimal rectangle size when given a
+ * range between two sizes that are all candidates.
+ */
+void meta_rectangle_find_linepoint_closest_to_point (double x1, double y1,
+ double x2, double y2,
+ double px, double py,
+ double *valx, double *valy);
+
+/***************************************************************************/
+/* */
+/* Switching gears to code for edges instead of just rectangles */
+/* */
+/***************************************************************************/
+
+/* Compare two edges, so that sorting functions can put a list of edges in
+ * canonical order.
+ */
+gint meta_rectangle_edge_cmp (gconstpointer a, gconstpointer b);
+
+/* Removes an parts of edges in the given list that intersect any box in the
+ * given rectangle list. Returns the result.
+ */
+GList* meta_rectangle_remove_intersections_with_boxes_from_edges (
+ GList *edges,
+ const GSList *rectangles);
+
+/* Finds all the edges of an onscreen region, returning a GList* of
+ * MetaEdgeRect's.
+ */
+GList* meta_rectangle_find_onscreen_edges (const MetaRectangle *basic_rect,
+ const GSList *all_struts);
+
+/* Finds edges between adjacent xineramas which are not covered by the given
+ * struts.
+ */
+GList* meta_rectangle_find_nonintersected_xinerama_edges (
+ const GList *xinerama_rects,
+ const GSList *all_struts);
+
+#endif /* META_BOXES_H */
diff --git a/src/common.h b/src/common.h
index c7c8108..391601c 100644
--- a/src/common.h
+++ b/src/common.h
@@ -2,6 +2,7 @@
/*
* Copyright (C) 2001 Havoc Pennington
+ * Copyright (C) 2005 Elijah Newren
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -64,7 +65,8 @@ typedef enum
META_MENU_OP_MOVE_LEFT = 1 << 13,
META_MENU_OP_MOVE_RIGHT = 1 << 14,
META_MENU_OP_MOVE_UP = 1 << 15,
- META_MENU_OP_MOVE_DOWN = 1 << 16
+ META_MENU_OP_MOVE_DOWN = 1 << 16,
+ META_MENU_OP_RECOVER = 1 << 17
} MetaMenuOp;
typedef struct _MetaWindowMenu MetaWindowMenu;
@@ -184,6 +186,22 @@ typedef enum
META_VIRTUAL_MOD5_MASK = 1 << 14
} MetaVirtualModifier;
+/* Relative directions or sides seem to come up all over the place... */
+/* FIXME: Replace
+ * place.[ch]:MetaWindowEdgePosition,
+ * screen.[ch]:MetaScreenDirection,
+ * workspace.[ch]:MetaMotionDirection,
+ * with the use of MetaDirection.
+ */
+typedef enum
+{
+ META_DIRECTION_LEFT = 1 << 0,
+ META_DIRECTION_RIGHT = 1 << 1,
+ META_DIRECTION_TOP = 1 << 2,
+ META_DIRECTION_BOTTOM = 1 << 3,
+ META_DIRECTION_UP = 1 << 2, /* Alternate name for TOP */
+ META_DIRECTION_DOWN = 1 << 3 /* Alternate name for BOTTOM */
+} MetaDirection;
/* Function a window button can have. Note, you can't add stuff here
* without extending the theme format to draw a new function and
@@ -226,7 +244,3 @@ struct _MetaButtonLayout
(ycoord) < ((rect).y + (rect).height))
#endif
-
-
-
-
diff --git a/src/constraints.c b/src/constraints.c
index ca36522..4e88028 100644
--- a/src/constraints.c
+++ b/src/constraints.c
@@ -3,6 +3,7 @@
/*
* Copyright (C) 2002, 2003 Red Hat, Inc.
* Copyright (C) 2003, 2004 Rob Adams
+ * Copyright (C) 2005 Elijah Newren
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -22,1591 +23,1061 @@
#include <config.h>
#include "constraints.h"
-#include "window.h"
#include "workspace.h"
#include "place.h"
-/* The way this code works was suggested by Owen Taylor.
- *
- * For any move_resize, we determine which variables are "free
- * variables" and apply constraints in terms of those. During the move
- * resize, we only want to modify those variables; otherwise the
- * constraint process can have peculiar side effects when the size and
- * position constraints interact. For example, resizing a window from
- * the top might go wrong when position constraints apply to the top
- * edge, and result in the bottom edge moving downward while the top
- * stays fixed.
- *
- * After selecting the variables we plan to vary, we define
- * each constraint on the window in terms of those variables.
- *
- * Trivial example, say we are resizing vertically from the top of the
- * window. In that case we are applying the user's mouse motion delta
- * to an original size and position, note that dy is positive to
- * resize downward:
- *
- * new_height = orig_height - dy;
- * new_y = orig_y + dy;
- *
- * A constraint that the position can't go above the top panel would
- * look like this:
- *
- * new_y >= screen_top_bound
- *
- * Substitute:
- *
- * orig_y + dy >= screen_top_bound
- *
- * Find the "boundary point" by changing to an equality:
- *
- * orig_y + dy = screen_top_bound
- *
- * Solve:
- *
- * dy = screen_top_bound - orig_y
- *
- * This dy is now the _maximum_ dy and you constrain dy with that
- * value, applying it to both the move and the resize:
- *
- * new_height = orig_height - dy;
- * new_y = orig_y + dy;
- *
- * This way the constraint is applied simultaneously to size/position,
- * so you aren't running the risk of constraining one but still
- * changing the other. i.e. we've converted an operation that may
- * modify both the Y position and the height of the window into an
- * operation that modifies a single variable, dy. That variable is
- * then constrained, rather than the constraining the Y pos and height
- * separately. This is a rather complicated fix for an obscure bug
- * that happened when resizing a window and encountering a constraint
- * such as the top edge of the screen.
- *
- */
-
-
-/* To adjust for window gravity, such as a client moving itself to the
- * southeast corner, we want to compute the gravity reference point
- * - (screen_width,screen_height) in the SE corner case - using the
- * size the client had in its configure request. But then we want
- * to compute the actual position we intend to land on using
- * the real constrained dimensions of the window.
- *
- * So for a window being placed in the SE corner and simultaneously
- * resized, we get the gravity reference point, then compute where the
- * window should go to maintain that ref. point at its current size
- * instead of at the requested size, and conceptually move the window
- * to the requested ref. point but at its current size, without
- * applying any constraints. Then we constrain it with the top and
- * left edges as the edges that vary, with a dx/dy that are the delta
- * from the current size to the requested size.
- *
- * This method applies to any ConfigureRequest that does a simultaneous
- * move/resize.
- *
- * We use the same method to e.g. maximize a window; if the window is
- * maximized, we want to MOVE_VERTICAL/MOVE_HORIZONTAL to the top
- * center of the screen, then RESIZE_BOTTOM and
- * RESIZE_HORIZONTAL_CENTER. i.e. essentially NorthGravity.
- *
- */
-
-#define FLOOR(value, base) ( ((int) ((value) / (base))) * (base) )
-
-typedef struct
-{
- MetaWindow *window;
- MetaFrameGeometry fgeom;
- const MetaXineramaScreenInfo *xinerama;
- MetaRectangle work_area_xinerama;
- MetaRectangle work_area_screen;
- int nw_x, nw_y, se_x, se_y; /* these are whole-screen not xinerama */
-} ConstraintInfo;
-
-/* (FIXME instead of TITLEBAR_LENGTH_ONSCREEN, get the actual
- * size of the menu control?).
- */
-
-#define TITLEBAR_LENGTH_ONSCREEN 75
-
-typedef gboolean (* MetaConstraintAppliesFunc) (MetaWindow *window);
-
-/* There's a function for each case with a different "free variable" */
-typedef void (* MetaConstrainTopFunc) (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *y_delta);
-typedef void (* MetaConstrainBottomFunc) (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *y_delta);
-typedef void (* MetaConstrainVCenterFunc) (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *y_delta);
-typedef void (* MetaConstrainLeftFunc) (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *x_delta);
-typedef void (* MetaConstrainRightFunc) (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *x_delta);
-typedef void (* MetaConstrainHCenterFunc) (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *x_delta);
-typedef void (* MetaConstrainMoveFunc) (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *x_delta,
- int *y_delta);
-
-typedef struct
-{
- const char *name;
- MetaConstraintAppliesFunc applies_func;
- MetaConstrainTopFunc top_func;
- MetaConstrainBottomFunc bottom_func;
- MetaConstrainVCenterFunc vcenter_func;
- MetaConstrainLeftFunc left_func;
- MetaConstrainRightFunc right_func;
- MetaConstrainHCenterFunc hcenter_func;
- MetaConstrainMoveFunc move_func;
-} Constraint;
-
-/* "Is the desktop window" constraint:
- *
- * new_x = 0;
- * new_y = 0;
- * new_w = orig_width;
- * new_h = orig_height;
- *
- * Note that if we are applying a resize constraint,
- * e.g. constraint_desktop_top_func, this is kind of broken since we
- * end up resizing the window in order to get its position right. But
- * that case shouldn't happen in practice.
- */
-static gboolean
-constraint_desktop_applies_func (MetaWindow *window)
-{
- return window->type == META_WINDOW_DESKTOP;
-}
-
-static void
-constraint_desktop_top_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *y_delta)
-{
- *y_delta = 0 - orig->y;
-}
-
-static void
-constraint_desktop_bottom_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *y_delta)
+#include <stdlib.h>
+#include <math.h>
+
+#if 0
+ // This is the short and sweet version of how to hack on this file; see
+ // doc/how-constraints-works.txt for the gory details. The basics of
+ // understanding this file can be shown by the steps needed to add a new
+ // constraint, which are:
+ // 1) Add a new entry in the ConstraintPriority enum; higher values
+ // have higher priority
+ // 2) Write a new function following the format of the example below,
+ // "constrain_whatever".
+ // 3) Add your function to the all_constraints and all_constraint_names
+ // arrays (the latter of which is for debugging purposes)
+ //
+ // An example constraint function, constrain_whatever:
+ //
+ // /* constrain_whatever does the following:
+ // * Quits (returning true) if priority is higher than PRIORITY_WHATEVER
+ // * If check_only is TRUE
+ // * Returns whether the constraint is satisfied or not
+ // * otherwise
+ // * Enforces the constraint
+ // * Note that the value of PRIORITY_WHATEVER is centralized with the
+ // * priorities of other constraints in the definition of ConstrainPriority
+ // * for easier maintenance and shuffling of priorities.
+ // */
+ // static gboolean
+ // constrain_whatever (MetaWindow *window,
+ // ConstraintInfo *info,
+ // ConstraintPriority priority,
+ // gboolean check_only)
+ // {
+ // if (priority > PRIORITY_WHATEVER)
+ // return TRUE;
+ //
+ // /* Determine whether constraint applies; note that if the constraint
+ // * cannot possibly be satisfied, constraint_applies should be set to
+ // * false. If we don't do this, all constraints with a lesser priority
+ // * will be dropped along with this one, and we'd rather apply as many as
+ // * possible.
+ // */
+ // if (!constraint_applies)
+ // return TRUE;
+ //
+ // /* Determine whether constraint is already satisfied; if we're only
+ // * checking the status of whether the constraint is satisfied, we end
+ // * here.
+ // */
+ // if (check_only || constraint_already_satisfied)
+ // return constraint_already_satisfied;
+ //
+ // /* Enforce constraints */
+ // return TRUE; /* Note that we exited early if check_only is FALSE; also,
+ // * we know we can return TRUE here because we exited early
+ // * if the constraint could not be satisfied; not that the
+ // * return value is heeded in this case...
+ // */
+ // }
+#endif
+
+typedef enum
{
- /* nothing */
-}
-
-static void
-constraint_desktop_vcenter_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *y_delta)
+ PRIORITY_MINIMUM = 0, // Dummy value used for loop start = min(all priorities)
+ PRIORITY_ASPECT_RATIO = 0,
+ PRIORITY_ENTIRELY_VISIBLE_ON_SINGLE_XINERAMA = 0,
+ PRIORITY_ENTIRELY_VISIBLE_ON_WORKAREA = 1,
+ PRIORITY_SIZE_HINTS_INCREMENTS = 1,
+ PRIORITY_MAXIMIZATION = 2,
+ PRIORITY_FULLSCREEN = 2,
+ PRIORITY_SIZE_HINTS_LIMITS = 3,
+ PRIORITY_PARTIALLY_VISIBLE_ON_WORKAREA = 4,
+ PRIORITY_MAXIMUM = 4 // Dummy value used for loop end = max(all priorities)
+} ConstraintPriority;
+
+typedef enum
{
- *y_delta = 0 - orig->y;
-}
+ ACTION_MOVE,
+ ACTION_RESIZE,
+ ACTION_MOVE_AND_RESIZE
+} ActionType;
-static void
-constraint_desktop_left_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *x_delta)
+typedef struct
{
- *x_delta = 0 - orig->x;
-}
+ MetaRectangle orig;
+ MetaRectangle current;
+ MetaFrameGeometry *fgeom;
+ ActionType action_type;
+ gboolean is_user_action;
+
+ /* I know that these two things probably look similar at first, but they
+ * have much different uses. See doc/how-constraints-works.txt for for
+ * explanation of the differences and similarity between resize_gravity
+ * and fixed_directions
+ */
+ int resize_gravity;
+ FixedDirections fixed_directions;
-static void
-constraint_desktop_right_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *x_delta)
-{
- /* nothing */
-}
+ /* work_area_xinerama - current xinerama region minus struts
+ * entire_xinerama - current xienrama, including strut regions
+ */
+ MetaRectangle work_area_xinerama;
+ MetaRectangle entire_xinerama;
-static void
-constraint_desktop_hcenter_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *x_delta)
-{
- *x_delta = 0 - orig->x;
-}
+ /* Spanning rectangles for the non-covered (by struts) region of the
+ * screen and also for just the current xinerama
+ */
+ GList *usable_screen_region;
+ GList *usable_xinerama_region;
+} ConstraintInfo;
-static void
-constraint_desktop_move_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *x_delta,
- int *y_delta)
-{
- *x_delta = 0 - orig->x;
- *y_delta = 0 - orig->y;
-}
+static gboolean constrain_maximization (MetaWindow *window,
+ ConstraintInfo *info,
+ ConstraintPriority priority,
+ gboolean check_only);
+static gboolean constrain_fullscreen (MetaWindow *window,
+ ConstraintInfo *info,
+ ConstraintPriority priority,
+ gboolean check_only);
+static gboolean constrain_size_increments (MetaWindow *window,
+ ConstraintInfo *info,
+ ConstraintPriority priority,
+ gboolean check_only);
+static gboolean constrain_size_limits (MetaWindow *window,
+ ConstraintInfo *info,
+ ConstraintPriority priority,
+ gboolean check_only);
+static gboolean constrain_aspect_ratio (MetaWindow *window,
+ ConstraintInfo *info,
+ ConstraintPriority priority,
+ gboolean check_only);
+static gboolean constrain_to_single_xinerama (MetaWindow *window,
+ ConstraintInfo *info,
+ ConstraintPriority priority,
+ gboolean check_only);
+static gboolean constrain_fully_onscreen (MetaWindow *window,
+ ConstraintInfo *info,
+ ConstraintPriority priority,
+ gboolean check_only);
+static gboolean constrain_partially_onscreen (MetaWindow *window,
+ ConstraintInfo *info,
+ ConstraintPriority priority,
+ gboolean check_only);
+
+static void setup_constraint_info (ConstraintInfo *info,
+ MetaWindow *window,
+ MetaFrameGeometry *orig_fgeom,
+ MetaMoveResizeFlags flags,
+ int resize_gravity,
+ const MetaRectangle *orig,
+ MetaRectangle *new);
+static void place_window_if_needed (MetaWindow *window,
+ ConstraintInfo *info);
+static void update_onscreen_requirements (MetaWindow *window,
+ ConstraintInfo *info);
+static void extend_by_frame (MetaRectangle *rect,
+ const MetaFrameGeometry *fgeom);
+static void unextend_by_frame (MetaRectangle *rect,
+ const MetaFrameGeometry *fgeom);
+static inline void get_size_limits (const MetaWindow *window,
+ const MetaFrameGeometry *fgeom,
+ gboolean include_frame,
+ MetaRectangle *min_size,
+ MetaRectangle *max_size);
+
+typedef gboolean (* ConstraintFunc) (MetaWindow *window,
+ ConstraintInfo *info,
+ ConstraintPriority priority,
+ gboolean check_only);
+
+typedef struct {
+ ConstraintFunc func;
+ const char* name;
+} Constraint;
-static const Constraint constraint_desktop = {
- "Desktop",
- constraint_desktop_applies_func,
- constraint_desktop_top_func,
- constraint_desktop_bottom_func,
- constraint_desktop_vcenter_func,
- constraint_desktop_left_func,
- constraint_desktop_right_func,
- constraint_desktop_hcenter_func,
- constraint_desktop_move_func
+static const Constraint all_constraints[] = {
+ {constrain_maximization, "constrain_maximization"},
+ {constrain_fullscreen, "constrain_fullscreen"},
+ {constrain_size_increments, "constrain_size_increments"},
+ {constrain_size_limits, "constrain_size_limits"},
+ {constrain_aspect_ratio, "constrain_aspect_ratio"},
+ {constrain_to_single_xinerama, "constrain_to_single_xinerama"},
+ {constrain_fully_onscreen, "constrain_fully_onscreen"},
+ {constrain_partially_onscreen, "constrain_partially_onscreen"},
+ {NULL, NULL}
};
-/* Titlebar is onscreen constraint:
- *
- * Constants:
- * titlebar_width_onscreen = amount of titlebar width that has to be onscreen
- * nw_x, nw_y = left/top edges that titlebar can't go outside
- * se_x, se_y = right/bottom edges
- *
- * NW limit has priority over SE, since titlebar is on NW
- *
- * Left resize
- * ===
- *
- * new_width = orig_width - dx
- * new_x = orig_x + dx
- *
- * Amount of window+frame that doesn't fit in the work area:
- *
- * offscreen_width = left_width + new_width + right_width - (se_x - nw_x)
- *
- * If we keep the old metacity rule where a window can be offscreen by
- * offscreen_width, then the math works out that left/top resizes are not
- * constrained. If we instead have a rule where the window can never be offscreen,
- * you get the following:
- *
- * new_x >= nw_x + left_width + titlebar_width_offscreen
- * orig_x + dx >= nw_x + left_width + titlebar_width_onscreen
- * dx >= nw_x + left_width + titlebar_width_onscreen - orig_x
- *
- * i.e. the minimum dx is: nw_x + left_width + titlebar_width_onscreen - orig_x
- *
- * We could have a more complicated rule that constrains only if the current
- * offscreen width is positive, thus allowing something more like the old
- * behavior, but not doing that for now.
- *
- * Top resize works the same as left resize. Right/bottom resize don't have a limit
- * because the constraint is designed to keep the top left corner of the
- * window or its titlebar on the screen, and right/bottom resize will never move that
- * area. Center resize is almost like left/top but dx has the opposite sign
- * and new_width = orig_width + 2dx.
- *
- * For right/bottom we can try to handle windows that aren't in a valid
- * location to begin with:
- *
- * new_x <= se_x - titlebar_width_onscreen
- * dx <= se_x - titlebar_width_onscreen - orig_x
- *
- * but in principle this constraint is never triggered.
- *
- * Vertical move
- * ===
- *
- * new_height = orig_height
- * new_y = orig_y + dy
- *
- * new_y >= nw_y + top_height
- *
- * Min negative dy (nw_y + top_height - orig_y) just as with top resize.
- * Max positive dy has to be computed from se_y and given less priority than the
- * min negative:
- *
- * new_y < se_y
- * orig_y + dy = se_y
- * so max dy is (se_y - orig_y)
- *
- * Horizontal move is equivalent to vertical.
- *
- */
-
static gboolean
-constraint_onscreen_applies_func (MetaWindow *window)
+do_all_constraints (MetaWindow *window,
+ ConstraintInfo *info,
+ ConstraintPriority priority,
+ gboolean check_only)
{
- return
- !window->fullscreen &&
- window->type != META_WINDOW_DESKTOP &&
- window->type != META_WINDOW_DOCK;
-}
-
-static void
-get_outermost_onscreen_positions (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int delta_x,
- int delta_y,
- int *leftmost_x_p,
- int *rightmost_x_p,
- int *topmost_y_p,
- int *bottommost_y_p)
-{
- GList *workspaces;
- GList *tmp;
- GSList *stmp;
- MetaRectangle current;
- int bottommost_y;
-
- /* to handle struts, we get the list of workspaces for the window
- * and traverse all the struts in each of the cached strut lists for
- * the workspaces. Note that because the workarea has already been
- * computed, these strut lists should already be up to date. This function
- * should have good performance since we call it a lot.
- */
+ const Constraint *constraint;
+ gboolean satisfied;
- current = *orig;
- current.x += delta_x;
- current.y += delta_y;
-
- workspaces = meta_window_get_workspaces (window);
- tmp = workspaces;
-
- if (leftmost_x_p)
+ constraint = &all_constraints[0];
+ satisfied = TRUE;
+ while (constraint->func != NULL)
{
- *leftmost_x_p = info->nw_x;
- while (tmp)
- {
- stmp = ((MetaWorkspace*) tmp->data)->left_struts;
- while (stmp)
- {
- MetaRectangle *rect = (MetaRectangle*) stmp->data;
- /* the strut only matters if the title bar is
- * overlapping the strut rect.
- */
- if (((current.y - info->fgeom.top_height >= rect->y) &&
- (current.y - info->fgeom.top_height < rect->y + rect->height)) ||
- ((current.y >= rect->y) &&
- (current.y < rect->y + rect->height)))
- {
- *leftmost_x_p = MAX (*leftmost_x_p, rect->width);
- }
-
- stmp = stmp->next;
- }
-
- tmp = tmp->next;
- }
-
- *leftmost_x_p = *leftmost_x_p - current.width +
- MIN (TITLEBAR_LENGTH_ONSCREEN, current.width);
- }
-
- tmp = workspaces;
- if (rightmost_x_p)
- {
- *rightmost_x_p = info->se_x;
- while (tmp)
- {
- stmp = ((MetaWorkspace*) tmp->data)->right_struts;
- while (stmp)
- {
- MetaRectangle *rect = (MetaRectangle*) stmp->data;
- /* the strut only matters if the title bar is
- * overlapping the strut rect.
- */
- if (((current.y - info->fgeom.top_height >= rect->y) &&
- (current.y - info->fgeom.top_height < rect->y + rect->height)) ||
- ((current.y >= rect->y) &&
- (current.y < rect->y + rect->height)))
- {
- *rightmost_x_p = MIN (*rightmost_x_p, rect->x);
- }
-
- stmp = stmp->next;
- }
-
- tmp = tmp->next;
- }
-
- *rightmost_x_p = *rightmost_x_p -
- MIN (TITLEBAR_LENGTH_ONSCREEN, current.width);
- }
+ satisfied = satisfied &&
+ (*constraint->func) (window, info, priority, check_only);
- tmp = workspaces;
- if (topmost_y_p)
- {
- *topmost_y_p = info->nw_y;
- while (tmp)
+ if (!check_only)
{
- stmp = ((MetaWorkspace*) tmp->data)->top_struts;
- while (stmp)
- {
- MetaRectangle *rect = (MetaRectangle*) stmp->data;
- /* here the strut matters if the titlebar is overlapping
- * the window horizontally
- */
- if ((current.x < rect->x + rect->width) &&
- (current.x + current.width > rect->x))
- {
- *topmost_y_p = MAX (*topmost_y_p, rect->height);
- }
-
- stmp = stmp->next;
- }
-
- tmp = tmp->next;
+ /* Log how the constraint modified the position */
+ meta_topic (META_DEBUG_GEOMETRY,
+ "info->current is %d,%d +%d,%d after %s\n",
+ info->current.x, info->current.y,
+ info->current.width, info->current.height,
+ constraint->name);
}
-
- *topmost_y_p = *topmost_y_p + info->fgeom.top_height;
- }
-
- tmp = workspaces;
- bottommost_y = G_MAXUSHORT;
- if (bottommost_y_p || topmost_y_p)
- {
- bottommost_y = info->se_y;
- while (tmp)
+ else if (!satisfied)
{
- stmp = ((MetaWorkspace*) tmp->data)->bottom_struts;
- while (stmp)
- {
- MetaRectangle *rect = (MetaRectangle*) stmp->data;
- /* here the strut matters if the titlebar is overlapping
- * the window horizontally
- */
- if ((current.x < rect->x + rect->width) &&
- (current.x + current.width > rect->x))
- {
- bottommost_y = MIN (bottommost_y, rect->y);
- }
-
- stmp = stmp->next;
- }
-
- tmp = tmp->next;
+ /* Log which constraint was not satisfied */
+ meta_topic (META_DEBUG_GEOMETRY,
+ "constraint %s not satisfied.\n",
+ constraint->name);
+ return FALSE;
}
+ ++constraint;
}
- if (bottommost_y_p)
- {
- *bottommost_y_p = bottommost_y;
-
- /* If no frame, keep random TITLEBAR_LENGTH_ONSCREEN pixels on the
- * screen.
- */
- if (!window->frame)
- *bottommost_y_p = *bottommost_y_p -
- MIN (TITLEBAR_LENGTH_ONSCREEN, current.height);
- }
-
- /* if the window has a minimum size too big for the "effective" work
- * area let it "cheat" a little by allowing a user to move it up so
- * that you can see the bottom of the window.
- */
- if (topmost_y_p)
- {
- int minheight;
-
- if (window->frame)
- {
- /* this is the "normal" case of, e.g. a dialog that's
- * just too big for the work area
- */
- minheight = window->frame->bottom_height +
- window->size_hints.min_height;
- }
- else
- {
- /* let frameless windows move offscreen is too large for the
- * effective work area. This may include windows that try
- * to make themselves full screen by removing the
- * decorations and repositioning themselves.
- */
- minheight = orig->height;
- }
-
- if (minheight > (bottommost_y - *topmost_y_p))
- *topmost_y_p = bottommost_y - minheight;
- }
-}
-
-static void
-constraint_onscreen_top_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *y_delta)
-{
- int min_dy;
- int topmost_y;
-
- get_outermost_onscreen_positions (window, info, orig, 0, *y_delta,
- NULL, NULL, &topmost_y, NULL);
-
- min_dy = topmost_y - orig->y;
-
- if (*y_delta < min_dy)
- *y_delta = min_dy;
-}
-
-static void
-constraint_onscreen_bottom_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *y_delta)
-{
- /* no way to resize off the bottom so that constraints are
- violated */
- return;
+ return TRUE;
}
-static void
-constraint_onscreen_vcenter_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *y_delta)
+void
+meta_window_constrain (MetaWindow *window,
+ MetaFrameGeometry *orig_fgeom,
+ MetaMoveResizeFlags flags,
+ int resize_gravity,
+ const MetaRectangle *orig,
+ MetaRectangle *new)
{
- int max_dy;
- int topmost_y;
-
- get_outermost_onscreen_positions (window, info, orig, 0, *y_delta,
- NULL, NULL, &topmost_y, NULL);
-
- max_dy = orig->y - topmost_y;
-
- if (*y_delta > max_dy)
- *y_delta = max_dy;
-}
+ ConstraintInfo info;
-static void
-constraint_onscreen_left_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *x_delta)
-{
- /* no way to resize off the sides so that constraints are violated
+ /* WARNING: orig and new specify positions and sizes of the inner window,
+ * not the outer. This is a common gotcha since half the constraints
+ * deal with inner window position/size and half deal with outer. See
+ * doc/how-constraints-works.txt for more information.
*/
- return;
-}
-
-static void
-constraint_onscreen_right_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *x_delta)
-{
- /* no way to resize off the sides so that constraints are violated
+ meta_topic (META_DEBUG_GEOMETRY,
+ "Constraining %s in move from %d,%d %dx%d to %d,%d %dx%d\n",
+ window->desc,
+ orig->x, orig->y, orig->width, orig->height,
+ new->x, new->y, new->width, new->height);
+
+ setup_constraint_info (&info,
+ window,
+ orig_fgeom,
+ flags,
+ resize_gravity,
+ orig,
+ new);
+ place_window_if_needed (window, &info);
+
+ ConstraintPriority priority = PRIORITY_MINIMUM;
+ gboolean satisfied = FALSE;
+ while (!satisfied && priority <= PRIORITY_MAXIMUM) {
+ gboolean check_only = TRUE;
+
+ /* Individually enforce all the high-enough priority constraints */
+ do_all_constraints (window, &info, priority, !check_only);
+
+ /* Check if all high-enough priority constraints are simultaneously
+ * satisfied
+ */
+ satisfied = do_all_constraints (window, &info, priority, check_only);
+
+ /* Drop the least important constraints if we can't satisfy them all */
+ priority++;
+ }
+
+ /* Make sure we use the constrained position */
+ *new = info.current;
+
+ /* We may need to update window->require_fully_onscreen,
+ * window->require_on_single_xinerama, and perhaps other quantities
+ * if this was a user move or user move-and-resize operation.
*/
- return;
-}
+ update_onscreen_requirements (window, &info);
-static void
-constraint_onscreen_hcenter_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *x_delta)
-{
- /* no way to resize off the sides so that constraints are violated
+ /* Ew, what an ugly way to do things. Destructors (in a real OOP language,
+ * not gobject-style--gobject would be more pain than it's worth) or
+ * smart pointers would be so much nicer here. *shrug*
*/
- return;
+ if (!orig_fgeom)
+ g_free (info.fgeom);
}
static void
-constraint_onscreen_move_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *x_delta,
- int *y_delta)
+setup_constraint_info (ConstraintInfo *info,
+ MetaWindow *window,
+ MetaFrameGeometry *orig_fgeom,
+ MetaMoveResizeFlags flags,
+ int resize_gravity,
+ const MetaRectangle *orig,
+ MetaRectangle *new)
{
- int min_delta;
- int max_delta;
- int leftmost_x, rightmost_x, topmost_y, bottommost_y;
-
- get_outermost_onscreen_positions (window, info, orig, *x_delta, *y_delta,
- &leftmost_x, &rightmost_x,
- &topmost_y, &bottommost_y);
-
- min_delta = topmost_y - orig->y;
- max_delta = bottommost_y - orig->y;
+ info->orig = *orig;
+ info->current = *new;
- /* Note that min delta (top left) has priority over
- * max delta (bottom right) to facilitate keeping
- * titlebar on the screen
- */
- if (*y_delta > max_delta)
- *y_delta = max_delta;
- if (*y_delta < min_delta)
- *y_delta = min_delta;
-
- min_delta = leftmost_x - orig->x;
- max_delta = rightmost_x - orig->x;
-
- if (*x_delta > max_delta)
- *x_delta = max_delta;
- if (*x_delta < min_delta)
- *x_delta = min_delta;
-}
-
-static const Constraint constraint_onscreen = {
- "Onscreen",
- constraint_onscreen_applies_func,
- constraint_onscreen_top_func,
- constraint_onscreen_bottom_func,
- constraint_onscreen_vcenter_func,
- constraint_onscreen_left_func,
- constraint_onscreen_right_func,
- constraint_onscreen_hcenter_func,
- constraint_onscreen_move_func
-};
-
-
-/* Size hints constraints:
- *
- * For min/max size we just clamp to those, and for resize increment
- * we clamp to the one at or below the requested place.
- *
- * For aspect ratio, we special-case it at the end of
- * meta_window_constrain, because it involves both dimensions, and
- * thus messes up our generic framework.
- *
- * Left resize can be solved for dx like this:
- * new_width = orig_width - dx
- * new_x = orig_x + dx
- *
- * new_width >= min_width
- * orig_width - dx >= min_width
- * - dx >= min_width - orig_width
- * dx <= orig_width - min_width
- *
- * new_width <= max_width
- * orig_width - dx <= max_width
- * - dx <= max_width - orig_width
- * dx >= orig_width - max_width
- *
- */
-
-#define USE_HINTS_FOR_WINDOW_STATE(window) (!((window)->fullscreen || (window)->maximized))
+ /* Create a fake frame geometry if none really exists */
+ if (orig_fgeom && !window->fullscreen)
+ info->fgeom = orig_fgeom;
+ else
+ info->fgeom = g_new0 (MetaFrameGeometry, 1);
+
+ if (flags & META_IS_MOVE_ACTION && flags & META_IS_RESIZE_ACTION)
+ info->action_type = ACTION_MOVE_AND_RESIZE;
+ else if (flags & META_IS_RESIZE_ACTION)
+ info->action_type = ACTION_RESIZE;
+ else if (flags & META_IS_MOVE_ACTION)
+ info->action_type = ACTION_MOVE_AND_RESIZE;
+ else
+ g_error ("BAD, BAD developer! No treat for you! (Fix your calls to "
+ "meta_window_move_resize_internal()).\n");
-static gboolean
-constraint_hints_applies_func (MetaWindow *window)
-{
- return USE_HINTS_FOR_WINDOW_STATE (window);
-}
+ info->is_user_action = (flags & META_IS_USER_ACTION);
-static void
-constraint_hints_top_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *y_delta)
-{
- int min_dy;
- int max_dy;
- int height;
+ info->resize_gravity = resize_gravity;
- max_dy = orig->height - window->size_hints.min_height;
- min_dy = orig->height - window->size_hints.max_height;
+ info->fixed_directions = 0;
+ /* If x directions don't change but either y direction does */
+ if ( orig->x == new->x && orig->x + orig->width == new->x + new->width &&
+ (orig->y != new->y || orig->y + orig->height != new->y + new->height))
+ {
+ info->fixed_directions = FIXED_DIRECTION_X;
+ }
+ /* If y directions don't change but either x direction does */
+ if ( orig->y == new->y && orig->y + orig->height == new->y + new->height &&
+ (orig->x != new->x || orig->x + orig->width != new->x + new->width ))
+ {
+ info->fixed_directions = FIXED_DIRECTION_Y;
+ }
- g_assert (max_dy >= min_dy);
+ meta_window_get_work_area_current_xinerama (window, &info->work_area_xinerama);
- if (*y_delta > max_dy)
- *y_delta = max_dy;
- if (*y_delta < min_dy)
- *y_delta = min_dy;
+ const MetaXineramaScreenInfo *xinerama_info =
+ meta_screen_get_xinerama_for_window (window->screen, window);
+ info->entire_xinerama = xinerama_info->rect;
- /* shrink to base + N * inc
- */
- height = orig->height - *y_delta;
- height = window->size_hints.base_height +
- FLOOR (height - window->size_hints.base_height, window->size_hints.height_inc);
+ MetaWorkspace *cur_workspace = window->screen->active_workspace;
+ info->usable_screen_region =
+ meta_workspace_get_onscreen_region (cur_workspace);
+ info->usable_xinerama_region =
+ meta_workspace_get_onxinerama_region (cur_workspace,
+ xinerama_info->number);
- *y_delta = orig->height - height;
+ /* Log all this information for debugging */
+ meta_topic (META_DEBUG_GEOMETRY,
+ "Setting up constraint info:\n"
+ " orig: %d,%d +%d,%d\n"
+ " new : %d,%d +%d,%d\n"
+ " fgeom: %d,%d,%d,%d\n"
+ " action_type : %s\n"
+ " is_user_action : %s\n"
+ " resize_gravity : %s\n"
+ " fixed_directions: %s\n"
+ " work_area_xinerama: %d,%d +%d,%d\n"
+ " entire_xinerama : %d,%d +%d,%d\n",
+ info->orig.x, info->orig.y, info->orig.width, info->orig.height,
+ info->current.x, info->current.y,
+ info->current.width, info->current.height,
+ info->fgeom->left_width, info->fgeom->right_width,
+ info->fgeom->top_height, info->fgeom->bottom_height,
+ (info->action_type == ACTION_MOVE) ? "Move" :
+ (info->action_type == ACTION_RESIZE) ? "Resize" :
+ (info->action_type == ACTION_MOVE_AND_RESIZE) ? "Move&Resize" :
+ "Freakin' Invalid Stupid",
+ (info->is_user_action) ? "true" : "false",
+ meta_gravity_to_string (info->resize_gravity),
+ (info->fixed_directions == 0) ? "None" :
+ (info->fixed_directions == FIXED_DIRECTION_X) ? "X fixed" :
+ (info->fixed_directions == FIXED_DIRECTION_Y) ? "Y fixed" :
+ "Freakin' Invalid Stupid",
+ info->work_area_xinerama.x, info->work_area_xinerama.y,
+ info->work_area_xinerama.width,
+ info->work_area_xinerama.height,
+ info->entire_xinerama.x, info->entire_xinerama.y,
+ info->entire_xinerama.width, info->entire_xinerama.height);
}
static void
-constraint_hints_bottom_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *y_delta)
+place_window_if_needed(MetaWindow *window,
+ ConstraintInfo *info)
{
- int min_dy;
- int max_dy;
- int height;
-
- min_dy = window->size_hints.min_height - orig->height;
- max_dy = window->size_hints.max_height - orig->height;
-
- g_assert (max_dy >= min_dy);
-
- if (*y_delta > max_dy)
- *y_delta = max_dy;
- if (*y_delta < min_dy)
- *y_delta = min_dy;
+ gboolean did_placement;
- /* shrink to base + N * inc
+ /* Do placement if any, so we go ahead and apply position
+ * constraints in a move-only context. Don't place
+ * maximized/fullscreen windows until they are unmaximized
+ * and unfullscreened
*/
- height = orig->height + *y_delta;
- height = window->size_hints.base_height +
- FLOOR (height - window->size_hints.base_height, window->size_hints.height_inc);
-
- *y_delta = height - orig->height;
-}
+ did_placement = FALSE;
+ if (!window->placed &&
+ window->calc_placement &&
+ !META_WINDOW_MAXIMIZED (window) &&
+ !window->fullscreen)
+ {
+ MetaRectangle placed_rect = info->orig;
-static void
-constraint_hints_vcenter_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *y_delta)
-{
- int min_dy;
- int max_dy;
- int height;
+ meta_window_place (window, info->fgeom, info->orig.x, info->orig.y,
+ &placed_rect.x, &placed_rect.y);
+ did_placement = TRUE;
- /* Remember our delta is negative to shrink window, positive to
- * grow it, and the actual resize is y_delta * 2 (which is broken,
- * but that's how it currently is)
- */
+ /* placing the window may have changed the xinerama. Find the
+ * new xinerama and update the ConstraintInfo
+ */
+ const MetaXineramaScreenInfo *xinerama_info =
+ meta_screen_get_xinerama_for_rect (window->screen, &placed_rect);
+ info->entire_xinerama = xinerama_info->rect;
+ meta_window_get_work_area_for_xinerama (window,
+ xinerama_info->number,
+ &info->work_area_xinerama);
- min_dy = (window->size_hints.min_height - orig->height) / 2;
- max_dy = (window->size_hints.max_height - orig->height) / 2;
+ info->current.x = placed_rect.x;
+ info->current.y = placed_rect.y;
- g_assert (max_dy >= min_dy);
+ /* Since we just barely placed the window, there's no reason to
+ * consider any of the directions fixed.
+ */
+ info->fixed_directions = 0;
+ }
- if (*y_delta > max_dy)
- *y_delta = max_dy;
- if (*y_delta < min_dy)
- *y_delta = min_dy;
+ if ((window->maximize_horizontally_after_placement ||
+ window->maximize_vertically_after_placement) &&
+ (window->placed || did_placement))
+ {
+ /* define a sane saved_rect so that the user can unmaximize to
+ * something reasonable.
+ */
+ if (info->current.width >= info->work_area_xinerama.width)
+ {
+ info->current.width = .75 * info->work_area_xinerama.width;
+ info->current.x = info->work_area_xinerama.x +
+ .125 * info->work_area_xinerama.width;
+ }
+ if (info->current.height >= info->work_area_xinerama.height)
+ {
+ info->current.height = .75 * info->work_area_xinerama.height;
+ info->current.y = info->work_area_xinerama.y +
+ .083 * info->work_area_xinerama.height;
+ }
- /* shrink to base + N * inc
- */
- height = orig->height + *y_delta * 2;
- height = window->size_hints.base_height +
- FLOOR (height - window->size_hints.base_height, window->size_hints.height_inc);
+ /* maximization may have changed frame geometry */
+ if (window->frame && !window->fullscreen)
+ meta_frame_calc_geometry (window->frame, info->fgeom);
+
+ if (window->maximize_horizontally_after_placement &&
+ window->maximize_vertically_after_placement)
+ meta_window_maximize_internal (window,
+ META_MAXIMIZE_HORIZONTAL |
+ META_MAXIMIZE_VERTICAL,
+ &info->current);
+ else if (window->maximize_horizontally_after_placement)
+ {
+ info->current.x = info->work_area_xinerama.x
+ + info->fgeom->left_width;
+ info->current.width = info->work_area_xinerama.width
+ - info->fgeom->left_width - info->fgeom->right_width;
+ }
+ else if (window->maximize_vertically_after_placement);
+ {
+ info->current.y = info->work_area_xinerama.y
+ + info->fgeom->top_height;
+ info->current.height = info->work_area_xinerama.height
+ - info->fgeom->top_height - info->fgeom->bottom_height;
+ }
- *y_delta = (height - orig->height) / 2;
+ window->maximize_horizontally_after_placement = FALSE;
+ window->maximize_vertically_after_placement = FALSE;
+ }
}
static void
-constraint_hints_left_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *x_delta)
+update_onscreen_requirements (MetaWindow *window,
+ ConstraintInfo *info)
{
- int min_dx;
- int max_dx;
- int width;
-
- max_dx = orig->width - window->size_hints.min_width;
- min_dx = orig->width - window->size_hints.max_width;
-
- g_assert (max_dx >= min_dx);
-
- if (*x_delta > max_dx)
- *x_delta = max_dx;
- if (*x_delta < min_dx)
- *x_delta = min_dx;
-
- /* shrink to base + N * inc
+ gboolean old;
+
+ /* We only apply the various onscreen requirements to normal windows */
+ if (window->type == META_WINDOW_DESKTOP ||
+ window->type == META_WINDOW_DOCK)
+ return;
+
+ /* USABILITY NOTE: Naturally, I only want the require_fully_onscreen and
+ * require_on_single_xinerama flags to *become false* due to user
+ * interactions (which is allowed since certain constraints are ignored
+ * for user interactions regardless of the setting of these flags).
+ * However, whether to make these flags *become true* due to just an
+ * application interaction is a little trickier. It's possible that
+ * users may find not doing that strange since two application
+ * interactions that resize in opposite ways don't necessarily end up
+ * cancelling--but it may also be strange for the user to have an
+ * application resize the window so that it's onscreen, the user forgets
+ * about it, and then later the app is able to resize itself off the
+ * screen. Anyway, for now, I'm think the latter is the more problematic
+ * case but this may need to be revisited.
*/
- width = orig->width - *x_delta;
- width = window->size_hints.base_width +
- FLOOR (width - window->size_hints.base_width, window->size_hints.width_inc);
-
- *x_delta = orig->width - width;
-}
-
-static void
-constraint_hints_right_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *x_delta)
-{
- int min_dx;
- int max_dx;
- int width;
-
- min_dx = window->size_hints.min_width - orig->width;
- max_dx = window->size_hints.max_width - orig->width;
-
- g_assert (max_dx >= min_dx);
- if (*x_delta > max_dx)
- *x_delta = max_dx;
- if (*x_delta < min_dx)
- *x_delta = min_dx;
-
- /* shrink to base + N * inc
+ /* The require onscreen/on-single-xinerama stuff is relative to the
+ * outer window, not the inner
*/
- width = orig->width + *x_delta;
- width = window->size_hints.base_width +
- FLOOR (width - window->size_hints.base_width, window->size_hints.width_inc);
-
- *x_delta = width - orig->width;
-}
+ extend_by_frame (&info->current, info->fgeom);
-static void
-constraint_hints_hcenter_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *x_delta)
-{
- int min_dx;
- int max_dx;
- int width;
-
- /* Remember our delta is negative to shrink window, positive to
- * grow it, and the actual resize is x_delta * 2 (which is broken,
- * but that's how it currently is)
+ /* Update whether we want future constraint runs to require the
+ * window to be on fully onscreen.
*/
+ old = window->require_fully_onscreen;
+ window->require_fully_onscreen =
+ meta_rectangle_contained_in_region (info->usable_screen_region,
+ &info->current);
+ if (old ^ window->require_fully_onscreen)
+ meta_topic (META_DEBUG_GEOMETRY,
+ "require_fully_onscreen for %s toggled to %s\n",
+ window->desc,
+ window->require_fully_onscreen ? "TRUE" : "FALSE");
- min_dx = (window->size_hints.min_width - orig->width) / 2;
- max_dx = (window->size_hints.max_width - orig->width) / 2;
-
- g_assert (max_dx >= min_dx);
-
- if (*x_delta > max_dx)
- *x_delta = max_dx;
- if (*x_delta < min_dx)
- *x_delta = min_dx;
-
- /* shrink to base + N * inc
+ /* Update whether we want future constraint runs to require the
+ * window to be on a single xinerama.
*/
- width = orig->width + *x_delta * 2;
- width = window->size_hints.base_width +
- FLOOR (width - window->size_hints.base_width, window->size_hints.width_inc);
+ old = window->require_on_single_xinerama;
+ window->require_on_single_xinerama =
+ meta_rectangle_contained_in_region (info->usable_xinerama_region,
+ &info->current);
+ if (old ^ window->require_on_single_xinerama)
+ meta_topic (META_DEBUG_GEOMETRY,
+ "require_on_single_xinerama for %s toggled to %s\n",
+ window->desc,
+ window->require_on_single_xinerama ? "TRUE" : "FALSE");
- *x_delta = (width - orig->width) / 2;
+ /* Don't forget to restore the position of the window */
+ unextend_by_frame (&info->current, info->fgeom);
}
static void
-constraint_hints_move_func (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int *x_delta,
- int *y_delta)
+extend_by_frame (MetaRectangle *rect,
+ const MetaFrameGeometry *fgeom)
{
- /* nothing */
+ rect->x -= fgeom->left_width;
+ rect->y -= fgeom->top_height;
+ rect->width += fgeom->left_width + fgeom->right_width;
+ rect->height += fgeom->top_height + fgeom->bottom_height;
}
-static const Constraint constraint_hints = {
- "Hints",
- constraint_hints_applies_func,
- constraint_hints_top_func,
- constraint_hints_bottom_func,
- constraint_hints_vcenter_func,
- constraint_hints_left_func,
- constraint_hints_right_func,
- constraint_hints_hcenter_func,
- constraint_hints_move_func
-};
-
-/* Array of all constraints at once */
-static const Constraint *all_constraints[] = {
- &constraint_desktop,
- &constraint_onscreen,
- &constraint_hints,
- NULL
-};
-
-/* Move with no accompanying change to window size */
static void
-constrain_move (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int x_delta,
- int y_delta,
- MetaRectangle *new)
+unextend_by_frame (MetaRectangle *rect,
+ const MetaFrameGeometry *fgeom)
{
- const Constraint **cp;
- int old_x, old_y;
- int paranoia;
-
- /* Evidence that we can't actually prove this algorithm is right */
-#define MAX_ITERATIONS 10
- paranoia = 0;
-
- do {
- old_x = x_delta;
- old_y = y_delta;
- cp = &all_constraints[0];
-
- while (*cp)
- {
- meta_topic (META_DEBUG_GEOMETRY,
- "Before: %d %d (Move constraint '%s')\n",
- x_delta, y_delta, (*cp)->name);
-
- if ((* (*cp)->applies_func) (window))
- (* (*cp)->move_func) (window, info, orig,
- &x_delta, &y_delta);
-
- meta_topic (META_DEBUG_GEOMETRY,
- "After: %d %d (Move constraint '%s')\n",
- x_delta, y_delta, (*cp)->name);
-
- ++cp;
- }
-
- ++paranoia;
- } while (((old_x != x_delta) || (old_y != y_delta)) && paranoia < MAX_ITERATIONS);
-
- new->x = orig->x + x_delta;
- new->y = orig->y + y_delta;
-
- if (paranoia >= MAX_ITERATIONS)
- meta_topic (META_DEBUG_GEOMETRY,
- "Constraints were never satisfied for window %s\n",
- window->desc);
+ rect->x += fgeom->left_width;
+ rect->y += fgeom->top_height;
+ rect->width -= fgeom->left_width + fgeom->right_width;
+ rect->height -= fgeom->top_height + fgeom->bottom_height;
}
-static void
-constrain_resize_left (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int x_delta,
- MetaRectangle *new)
+static inline void
+get_size_limits (const MetaWindow *window,
+ const MetaFrameGeometry *fgeom,
+ gboolean include_frame,
+ MetaRectangle *min_size,
+ MetaRectangle *max_size)
{
- const Constraint **cp;
-
- cp = &all_constraints[0];
+ /* We pack the results into MetaRectangle structs just for convienience; we
+ * don't actually use the position of those rects.
+ */
+ min_size->width = window->size_hints.min_width;
+ min_size->height = window->size_hints.min_height;
+ max_size->width = window->size_hints.max_width;
+ max_size->height = window->size_hints.max_height;
- while (*cp)
+ if (include_frame)
{
- meta_topic (META_DEBUG_GEOMETRY,
- "Before: %d (Left constraint '%s')\n",
- x_delta, (*cp)->name);
-
- if ((* (*cp)->applies_func) (window))
- (* (*cp)->left_func) (window, info, orig,
- &x_delta);
-
- meta_topic (META_DEBUG_GEOMETRY,
- "After: %d (Left constraint '%s')\n",
- x_delta, (*cp)->name);
-
- ++cp;
- }
+ int fw = fgeom->left_width + fgeom->right_width;
+ int fh = fgeom->top_height + fgeom->bottom_height;
- /* Moving mouse from 10 to 5 means current - orig means 5 - 10 means
- * a delta of -5
- */
- new->x = orig->x + x_delta;
- new->width = orig->width - x_delta;
+ min_size->width += fw;
+ min_size->height += fh;
+ max_size->width += fw;
+ max_size->height += fh;
+ }
}
-static void
-constrain_resize_hcenter (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int x_delta,
- MetaRectangle *new)
+static gboolean
+constrain_maximization (MetaWindow *window,
+ ConstraintInfo *info,
+ ConstraintPriority priority,
+ gboolean check_only)
{
- const Constraint **cp;
-
- cp = &all_constraints[0];
-
- while (*cp)
+ if (priority > PRIORITY_MAXIMIZATION)
+ return TRUE;
+
+ /* Determine whether constraint applies; exit if it doesn't */
+ if (!window->maximized_horizontally && !window->maximized_vertically)
+ return TRUE;
+
+ MetaRectangle min_size, max_size;
+ MetaRectangle work_area = info->work_area_xinerama;
+ unextend_by_frame (&work_area, info->fgeom);
+ get_size_limits (window, info->fgeom, FALSE, &min_size, &max_size);
+
+ gboolean hminbad, vminbad, hmaxbad, vmaxbad;
+ hminbad = work_area.width < min_size.width && window->maximized_horizontally;
+ vminbad = work_area.height < min_size.height && window->maximized_vertically;
+ hmaxbad = work_area.width > max_size.width && window->maximized_horizontally;
+ vmaxbad = work_area.height > max_size.height && window->maximized_vertically;
+ if (hminbad || vminbad || hmaxbad || vmaxbad)
+ return TRUE;
+
+ /* Determine whether constraint is already satisfied; exit if it is */
+ gboolean horiz_equal, vert_equal;
+ horiz_equal = work_area.x == info->current.x &&
+ work_area.width == info->current.width;
+ vert_equal = work_area.y == info->current.y &&
+ work_area.height == info->current.height;
+ gboolean constraint_already_satisfied =
+ (horiz_equal || !window->maximized_horizontally) &&
+ (vert_equal || !window->maximized_vertically);
+ if (check_only || constraint_already_satisfied)
+ return constraint_already_satisfied;
+
+ /*** Enforce constraint ***/
+ if (window->maximized_horizontally)
{
- meta_topic (META_DEBUG_GEOMETRY,
- "Before: %d (HCenter constraint '%s')\n",
- x_delta, (*cp)->name);
-
- if ((* (*cp)->applies_func) (window))
- (* (*cp)->hcenter_func) (window, info, orig,
- &x_delta);
-
- meta_topic (META_DEBUG_GEOMETRY,
- "After: %d (HCenter constraint '%s')\n",
- x_delta, (*cp)->name);
-
- ++cp;
+ info->current.x = work_area.x;
+ info->current.width = work_area.width;
}
-
- /* center deltas are positive to grow the window and negative to
- * shrink it.
- */
- new->x = orig->x - x_delta;
- new->width = orig->width + x_delta * 2;
- /* FIXME above implies that with center gravity you have to grow
- * in increments of two
- */
-}
-
-static void
-constrain_resize_right (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int x_delta,
- MetaRectangle *new)
-{
- const Constraint **cp;
-
- cp = &all_constraints[0];
-
- while (*cp)
+ if (window->maximized_vertically)
{
- meta_topic (META_DEBUG_GEOMETRY,
- "Before: %d (Right constraint '%s')\n",
- x_delta, (*cp)->name);
-
- if ((* (*cp)->applies_func) (window))
- (* (*cp)->right_func) (window, info, orig,
- &x_delta);
-
- meta_topic (META_DEBUG_GEOMETRY,
- "After: %d (Right constraint '%s')\n",
- x_delta, (*cp)->name);
-
- ++cp;
+ info->current.y = work_area.y;
+ info->current.height = work_area.height;
}
-
- new->width = orig->width + x_delta;
+ return TRUE;
}
-static void
-constrain_resize_top (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int y_delta,
- MetaRectangle *new)
+static gboolean
+constrain_fullscreen (MetaWindow *window,
+ ConstraintInfo *info,
+ ConstraintPriority priority,
+ gboolean check_only)
{
- const Constraint **cp;
+ if (priority > PRIORITY_FULLSCREEN)
+ return TRUE;
- cp = &all_constraints[0];
-
- while (*cp)
- {
- meta_topic (META_DEBUG_GEOMETRY,
- "Before: %d (Top constraint '%s')\n",
- y_delta, (*cp)->name);
-
- if ((* (*cp)->applies_func) (window))
- (* (*cp)->top_func) (window, info, orig,
- &y_delta);
-
- meta_topic (META_DEBUG_GEOMETRY,
- "After: %d (Top constraint '%s')\n",
- y_delta, (*cp)->name);
-
- ++cp;
- }
-
- new->y = orig->y + y_delta;
- new->height = orig->height - y_delta;
+ /* Determine whether constraint applies; exit if it doesn't */
+ if (!window->fullscreen)
+ return TRUE;
+ MetaRectangle min_size, max_size;
+ MetaRectangle xinerama = info->entire_xinerama;
+ get_size_limits (window, info->fgeom, FALSE, &min_size, &max_size);
+ gboolean too_big = !meta_rectangle_could_fit_rect (&xinerama, &min_size);
+ gboolean too_small = !meta_rectangle_could_fit_rect (&max_size, &xinerama);
+ if (too_big || too_small)
+ return TRUE;
+
+ /* Determine whether constraint is already satisfied; exit if it is */
+ gboolean constraint_already_satisfied =
+ meta_rectangle_equal (&info->current, &xinerama);
+ if (check_only || constraint_already_satisfied)
+ return constraint_already_satisfied;
+
+ /*** Enforce constraint ***/
+ info->current = xinerama;
+ return TRUE;
}
-static void
-constrain_resize_vcenter (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int y_delta,
- MetaRectangle *new)
+static gboolean
+constrain_size_increments (MetaWindow *window,
+ ConstraintInfo *info,
+ ConstraintPriority priority,
+ gboolean check_only)
{
- const Constraint **cp;
-
- cp = &all_constraints[0];
-
- while (*cp)
- {
- meta_topic (META_DEBUG_GEOMETRY,
- "Before: %d (VCenter constraint '%s')\n",
- y_delta, (*cp)->name);
-
- if ((* (*cp)->applies_func) (window))
- (* (*cp)->vcenter_func) (window, info, orig,
- &y_delta);
-
- meta_topic (META_DEBUG_GEOMETRY,
- "After: %d (VCenter constraint '%s')\n",
- y_delta, (*cp)->name);
-
- ++cp;
- }
-
- /* center deltas are positive to grow the window and negative to
- * shrink it.
- */
- new->y = orig->y - y_delta;
- new->height = orig->height + y_delta * 2;
- /* FIXME above implies that with center gravity you have to grow
- * in increments of two
- */
+ if (priority > PRIORITY_SIZE_HINTS_INCREMENTS)
+ return TRUE;
+
+ /* Determine whether constraint applies; exit if it doesn't */
+ if (META_WINDOW_MAXIMIZED (window) || window->fullscreen ||
+ info->action_type == ACTION_MOVE)
+ return TRUE;
+
+ /* Determine whether constraint is already satisfied; exit if it is */
+ int bh, hi, bw, wi, extra_height, extra_width;
+ bh = window->size_hints.base_height;
+ hi = window->size_hints.height_inc;
+ bw = window->size_hints.base_width;
+ wi = window->size_hints.width_inc;
+ extra_height = (info->current.height - bh) % hi;
+ extra_width = (info->current.width - bw) % wi;
+ if (window->maximized_horizontally)
+ extra_width *= 0;
+ if (window->maximized_vertically)
+ extra_height *= 0;
+ gboolean constraint_already_satisfied =
+ (extra_height == 0 && extra_width == 0);
+
+ if (check_only || constraint_already_satisfied)
+ return constraint_already_satisfied;
+
+ /*** Enforce constraint ***/
+ /* Shrink to base + N * inc */
+ meta_rectangle_resize_with_gravity (&info->orig,
+ &info->current,
+ info->resize_gravity,
+ info->current.width - extra_width,
+ info->current.height - extra_height);
+ return TRUE;
}
-static void
-constrain_resize_bottom (MetaWindow *window,
- const ConstraintInfo *info,
- const MetaRectangle *orig,
- int y_delta,
- MetaRectangle *new)
+static gboolean
+constrain_size_limits (MetaWindow *window,
+ ConstraintInfo *info,
+ ConstraintPriority priority,
+ gboolean check_only)
{
- const Constraint **cp;
-
- cp = &all_constraints[0];
+ if (priority > PRIORITY_SIZE_HINTS_LIMITS)
+ return TRUE;
- while (*cp)
- {
- meta_topic (META_DEBUG_GEOMETRY,
- "Before: %d (Bottom constraint '%s')\n",
- y_delta, (*cp)->name);
-
- if ((* (*cp)->applies_func) (window))
- (* (*cp)->bottom_func) (window, info, orig,
- &y_delta);
-
- meta_topic (META_DEBUG_GEOMETRY,
- "After: %d (Bottom constraint '%s')\n",
- y_delta, (*cp)->name);
-
- ++cp;
- }
-
- new->height = orig->height + y_delta;
+ /* Determine whether constraint applies; exit if it doesn't.
+ *
+ * Note: The old code didn't apply this constraint for fullscreen or
+ * maximized windows--but that seems odd to me. *shrug*
+ */
+ if (info->action_type == ACTION_MOVE)
+ return TRUE;
+
+ /* Determine whether constraint is already satisfied; exit if it is */
+ MetaRectangle min_size, max_size;
+ get_size_limits (window, info->fgeom, FALSE, &min_size, &max_size);
+ gboolean too_big =
+ !meta_rectangle_could_fit_rect (&info->current, &min_size);
+ gboolean too_small =
+ !meta_rectangle_could_fit_rect (&max_size, &info->current);
+ gboolean constraint_already_satisfied = !too_big && !too_small;
+ if (check_only || constraint_already_satisfied)
+ return constraint_already_satisfied;
+
+ /*** Enforce constraint ***/
+ int new_width, new_height;
+ new_width = CLAMP (info->current.width, min_size.width, max_size.width);
+ new_height = CLAMP (info->current.height, min_size.height, max_size.height);
+ meta_rectangle_resize_with_gravity (&info->orig,
+ &info->current,
+ info->resize_gravity,
+ new_width,
+ new_height);
+ return TRUE;
}
-static void
-update_position_limits (MetaWindow *window,
- ConstraintInfo *info)
+static gboolean
+constrain_aspect_ratio (MetaWindow *window,
+ ConstraintInfo *info,
+ ConstraintPriority priority,
+ gboolean check_only)
{
- int nw_x, nw_y;
- int se_x, se_y;
-
- /* For maximized windows the limits are the work area, for
- * other windows we see which struts apply based on the
- * window's position later on
+ if (priority > PRIORITY_ASPECT_RATIO)
+ return TRUE;
+
+ /* Determine whether constraint applies; exit if it doesn't. */
+ double minr, maxr;
+ minr = window->size_hints.min_aspect.x /
+ (double)window->size_hints.min_aspect.y;
+ maxr = window->size_hints.max_aspect.x /
+ (double)window->size_hints.max_aspect.y;
+ gboolean constraints_are_inconsistent = minr > maxr;
+ if (constraints_are_inconsistent ||
+ META_WINDOW_MAXIMIZED (window) || window->fullscreen ||
+ info->action_type == ACTION_MOVE)
+ return TRUE;
+
+ /* Determine whether constraint is already satisfied; exit if it is. We
+ * need the following to hold:
+ *
+ * width
+ * minr <= ------ <= maxr
+ * height
+ *
+ * But we need to allow for some slight fudging since width and height
+ * are integers instead of floating point numbers (this is particularly
+ * important when minr == maxr), so we allow width and height to be off
+ * a little bit from strictly satisfying these equations. For just one
+ * sided resizing, we have to make the fudge factor a little bigger
+ * because of how meta_rectangle_resize_with_gravity treats those as
+ * being a resize increment (FIXME: I should handle real resize
+ * increments better here...)
*/
- if (window->maximized)
+ int fudge;
+ switch (info->resize_gravity)
{
- nw_x = MIN (info->work_area_xinerama.x, info->work_area_screen.x);
- nw_y = MIN (info->work_area_xinerama.y, info->work_area_screen.y);
-
- /* find bottom-right corner of workarea */
- se_x = MAX (info->work_area_xinerama.x + info->work_area_xinerama.width,
- info->work_area_screen.x + info->work_area_screen.width);
- se_y = MAX (info->work_area_xinerama.y + info->work_area_xinerama.height,
- info->work_area_screen.y + info->work_area_screen.height);
- }
- else
- {
- nw_x = 0;
- nw_y = 0;
- se_x = window->screen->width;
- se_y = window->screen->height;
- }
-
- /* If we have a micro-screen or huge frames maybe nw/se got
- * swapped
- */
- if (nw_x > se_x)
- {
- int tmp = nw_x;
- nw_x = se_x;
- se_x = tmp;
- }
+ case WestGravity:
+ case NorthGravity:
+ case SouthGravity:
+ case EastGravity:
+ fudge = 2;
+ break;
- if (nw_y > se_y)
- {
- int tmp = nw_y;
- nw_y = se_y;
- se_y = tmp;
+ case NorthWestGravity:
+ case SouthWestGravity:
+ case CenterGravity:
+ case NorthEastGravity:
+ case SouthEastGravity:
+ case StaticGravity:
+ default:
+ fudge = 1;
+ break;
}
-
- info->nw_x = nw_x;
- info->nw_y = nw_y;
- info->se_x = se_x;
- info->se_y = se_y;
-}
-
-/* The delta values are the mouse motion distance deltas,
- * i.e. mouse_current_pos - mouse_orig_pos, for resizing on
- * the sides, or moving. For center resize, the delta
- * value is positive to grow the window and negative to
- * shrink it (while the sign of the mouse delta
- * depends on which side of the window you are center resizing
- * from)
- */
-void
-meta_window_constrain (MetaWindow *window,
- MetaFrameGeometry *orig_fgeom,
- const MetaRectangle *orig,
- int x_move_delta,
- int y_move_delta,
- MetaResizeDirection x_direction,
- int x_delta,
- MetaResizeDirection y_direction,
- int y_delta,
- MetaRectangle *new)
-{
- ConstraintInfo info;
- MetaRectangle current;
- gboolean did_placement;
-
-#define OUTER_WIDTH(rect) ((rect).width + info.fgeom.left_width + info.fgeom.right_width)
-#define OUTER_HEIGHT(rect) ((rect).height + info.fgeom.top_height + info.fgeom.bottom_height)
-
- meta_topic (META_DEBUG_GEOMETRY,
- "Constraining %s x_move_delta = %d y_move_delta = %d x_direction = %d y_direction = %d x_delta = %d y_delta = %d orig %d,%d %dx%d\n",
- window->desc, x_move_delta, y_move_delta,
- x_direction, y_direction, x_delta, y_delta,
- orig->x, orig->y, orig->width, orig->height);
-
- /* Create a fake frame geometry if none really exists */
- if (orig_fgeom && !window->fullscreen)
- info.fgeom = *orig_fgeom;
- else
+ gboolean constraint_already_satisfied =
+ info->current.width - (info->current.height * minr ) > -minr*fudge &&
+ info->current.width - (info->current.height * maxr ) < maxr*fudge;
+ if (check_only || constraint_already_satisfied)
+ return constraint_already_satisfied;
+
+ /*** Enforce constraint ***/
+ int new_width, new_height;
+ double best_width, best_height;
+ double alt_width, alt_height;
+ new_width = info->current.width;
+ new_height = info->current.height;
+
+ switch (info->resize_gravity)
{
- info.fgeom.top_height = 0;
- info.fgeom.bottom_height = 0;
- info.fgeom.left_width = 0;
- info.fgeom.right_width = 0;
- }
+ case WestGravity:
+ case EastGravity:
+ /* Yeah, I suck for doing implicit rounding -- sue me */
+ new_height = CLAMP (new_height, new_width / maxr, new_width / minr);
+ break;
- meta_window_get_work_area_current_xinerama (window, &info.work_area_xinerama);
- meta_window_get_work_area_all_xineramas (window, &info.work_area_screen);
+ case NorthGravity:
+ case SouthGravity:
+ /* Yeah, I suck for doing implicit rounding -- sue me */
+ new_width = CLAMP (new_width, new_height * minr, new_height * maxr);
+ break;
- info.window = window;
- info.xinerama = meta_screen_get_xinerama_for_window (window->screen,
- window);
- /* Init info->nw_x etc. */
- update_position_limits (window, &info);
+ case NorthWestGravity:
+ case SouthWestGravity:
+ case CenterGravity:
+ case NorthEastGravity:
+ case SouthEastGravity:
+ case StaticGravity:
+ default:
+ /* Find what width would correspond to new_height, and what height would
+ * correspond to new_width */
+ alt_width = CLAMP (new_width, new_height * minr, new_height * maxr);
+ alt_height = CLAMP (new_height, new_width / maxr, new_width / minr);
+
+ /* The line connecting the points (alt_width, new_height) and
+ * (new_width, alt_height) provide a range of
+ * valid-for-the-aspect-ratio-constraint sizes. We want the
+ * size in that range closest to the value requested, i.e. the
+ * point on the line which is closest to the point (new_width,
+ * new_height)
+ */
+ meta_rectangle_find_linepoint_closest_to_point (alt_width, new_height,
+ new_width, alt_height,
+ new_width, new_height,
+ &best_width, &best_height);
- current = *orig;
- *new = current;
+ /* Yeah, I suck for doing implicit rounding -- sue me */
+ new_width = best_width;
+ new_height = best_height;
- /* Do placement if any, so we go ahead and apply position
- * constraints in a move-only context. Don't place
- * maximized/fullscreen windows until they are unmaximized
- * and unfullscreened
- */
- did_placement = FALSE;
- if (!window->placed &&
- window->calc_placement &&
- !window->maximized &&
- !window->fullscreen)
- {
- MetaRectangle placed_rect = current;
+ break;
+ }
- meta_window_place (window, orig_fgeom, current.x, current.y,
- &placed_rect.x, &placed_rect.y);
- did_placement = TRUE;
+ meta_rectangle_resize_with_gravity (&info->orig,
+ &info->current,
+ info->resize_gravity,
+ new_width,
+ new_height);
- /* placing the window may have changed the xinerama. Find the
- * new xinerama and update the ConstraintInfo
- */
- info.xinerama = meta_screen_get_xinerama_for_rect (window->screen,
- &placed_rect);
- meta_window_get_work_area_for_xinerama (window,
- info.xinerama->number,
- &info.work_area_xinerama);
- update_position_limits (window, &info);
+ return TRUE;
+}
- constrain_move (window, &info, &current,
- placed_rect.x - current.x,
- placed_rect.y - current.y,
- new);
- current = *new;
+static gboolean
+do_screen_and_xinerama_relative_constraints (
+ MetaWindow *window,
+ GList *region_spanning_rectangles,
+ ConstraintInfo *info,
+ gboolean check_only)
+{
+ gboolean exit_early = FALSE;
- /* Ignore any non-placement movement */
- x_move_delta = 0;
- y_move_delta = 0;
+ /* First, log some debugging information */
+ char spanning_region[1 + 28 * g_list_length (region_spanning_rectangles)];
+ (void) spanning_region; /* Avoid stupid & incorrect compiler warnings... */
+ meta_topic (META_DEBUG_GEOMETRY,
+ "screen/xinerama constraint; region_spanning_rectangles: %s\n",
+ meta_rectangle_region_to_string (region_spanning_rectangles, ", ",
+ spanning_region));
- }
+ /* Determine whether constraint applies; exit if it doesn't */
+ MetaRectangle how_far_it_can_be_smushed, min_size, max_size;
+ how_far_it_can_be_smushed = info->current;
+ get_size_limits (window, info->fgeom, TRUE, &min_size, &max_size);
+ extend_by_frame (&info->current, info->fgeom);
- if (window->maximize_after_placement &&
- (window->placed || did_placement))
+ if (info->action_type != ACTION_MOVE)
{
- window->maximize_after_placement = FALSE;
-
- if (OUTER_WIDTH (*new) >= info.work_area_xinerama.width &&
- OUTER_HEIGHT (*new) >= info.work_area_xinerama.height)
- {
- /* define a sane saved_rect so that the user can unmaximize
- * to something reasonable.
- */
- new->width = .75 * info.work_area_xinerama.width;
- new->height = .75 * info.work_area_xinerama.height;
- new->x = info.work_area_xinerama.x + .125 * info.work_area_xinerama.width;
- new->y = info.work_area_xinerama.y + .083 * info.work_area_xinerama.height;
- }
-
- meta_window_maximize_internal (window, new);
+ if (!(info->fixed_directions & FIXED_DIRECTION_X))
+ how_far_it_can_be_smushed.width = min_size.width;
- /* maximization may have changed frame geometry */
- if (orig_fgeom && !window->fullscreen)
- {
- meta_frame_calc_geometry (window->frame,
- orig_fgeom);
- info.fgeom = *orig_fgeom;
- }
+ if (!(info->fixed_directions & FIXED_DIRECTION_Y))
+ how_far_it_can_be_smushed.height = min_size.height;
}
-
- /* Maximization, fullscreen, etc. are defined as a resize followed by
- * a move, as explained in one of the big comments at the top of
- * this file.
- */
- if (window->fullscreen)
+ if (!meta_rectangle_could_fit_in_region (region_spanning_rectangles,
+ &how_far_it_can_be_smushed))
+ exit_early = TRUE;
+
+ /* Determine whether constraint is already satisfied; exit if it is */
+ gboolean constraint_satisfied =
+ meta_rectangle_contained_in_region (region_spanning_rectangles,
+ &info->current);
+ if (exit_early || constraint_satisfied || check_only)
{
- current = *new;
- constrain_resize_bottom (window, &info, &current,
- (info.xinerama->height - OUTER_HEIGHT (current)),
- new);
-
- current = *new;
-
- constrain_resize_right (window, &info, &current,
- info.xinerama->width - OUTER_WIDTH (current),
- new);
- current = *new;
-
- constrain_move (window, &info, &current,
- info.xinerama->x_origin - current.x + info.fgeom.left_width,
- info.xinerama->y_origin - current.y + info.fgeom.top_height,
- new);
+ unextend_by_frame (&info->current, info->fgeom);
+ return constraint_satisfied;
}
- else if (window->maximized)
- {
- constrain_resize_bottom (window, &info, &current,
- (info.work_area_xinerama.height - OUTER_HEIGHT (current)),
- new);
-
- current = *new;
- constrain_resize_right (window, &info, &current,
- info.work_area_xinerama.width - OUTER_WIDTH (current),
- new);
- current = *new;
+ /* Enforce constraint */
- constrain_move (window, &info, &current,
- info.work_area_xinerama.x - current.x + info.fgeom.left_width,
- info.work_area_xinerama.y - current.y + info.fgeom.top_height,
- new);
+ /* Clamp rectangle size for resize or move+resize actions */
+ if (info->action_type != ACTION_MOVE)
+ meta_rectangle_clamp_to_fit_into_region (region_spanning_rectangles,
+ info->fixed_directions,
+ &info->current,
+ &min_size);
- current = *new;
- }
+ if (info->is_user_action && info->action_type == ACTION_RESIZE)
+ /* For user resize, clip to the relevant region */
+ meta_rectangle_clip_to_region (region_spanning_rectangles,
+ info->fixed_directions,
+ &info->current);
else
- {
- switch (x_direction)
- {
- case META_RESIZE_LEFT_OR_TOP:
- constrain_resize_left (window, &info, &current,
- x_delta, new);
- break;
- case META_RESIZE_CENTER:
- constrain_resize_hcenter (window, &info, &current,
- x_delta, new);
- break;
- case META_RESIZE_RIGHT_OR_BOTTOM:
- constrain_resize_right (window, &info, &current,
- x_delta, new);
- break;
- }
-
- switch (y_direction)
- {
- case META_RESIZE_LEFT_OR_TOP:
- constrain_resize_top (window, &info, &current,
- y_delta, new);
- break;
- case META_RESIZE_CENTER:
- constrain_resize_vcenter (window, &info, &current,
- y_delta, new);
- break;
- case META_RESIZE_RIGHT_OR_BOTTOM:
- constrain_resize_bottom (window, &info, &current,
- y_delta, new);
- break;
- }
+ /* For everything else, shove the rectangle into the relevant region */
+ meta_rectangle_shove_into_region (region_spanning_rectangles,
+ info->fixed_directions,
+ &info->current);
- current = *new;
-
- constrain_move (window, &info, &current,
- x_move_delta, y_move_delta,
- new);
-
- current = *new;
- }
+ unextend_by_frame (&info->current, info->fgeom);
+ return TRUE;
+}
- /* Now we have to sort out the aspect ratio */
- if (!window->fullscreen)
- {
- /*
- * width
- * min_aspect <= -------- <= max_aspect
- * height
- */
- double min_aspect, max_aspect;
- int width, height;
-
- min_aspect = window->size_hints.min_aspect.x / (double) window->size_hints.min_aspect.y;
- max_aspect = window->size_hints.max_aspect.x / (double) window->size_hints.max_aspect.y;
-
- width = current.width;
- height = current.height;
-
- if (min_aspect * height > width)
- {
- int delta;
-
- if (y_direction == META_RESIZE_CENTER)
- {
- delta = FLOOR (height * min_aspect - width, window->size_hints.width_inc);
- if (width + delta <= window->size_hints.max_width)
- width += delta;
- else
- {
- delta = FLOOR (height - width / min_aspect, window->size_hints.height_inc);
- if (height - delta >= window->size_hints.min_height)
- height -= delta;
- }
- }
- else
- {
- delta = FLOOR (height - width / min_aspect, window->size_hints.height_inc);
- if (height - delta >= window->size_hints.min_height)
- height -= delta;
- else
- {
- delta = FLOOR (height * min_aspect - width, window->size_hints.width_inc);
- if (width + delta <= window->size_hints.max_width)
- width += delta;
- }
- }
- }
-
- if (max_aspect * height < width)
- {
- int delta;
-
- if (x_direction == META_RESIZE_CENTER)
- {
- delta = FLOOR (width / max_aspect - height, window->size_hints.height_inc);
- if (height + delta <= window->size_hints.max_height)
- height += delta;
- else
- {
- delta = FLOOR (width - height * max_aspect, window->size_hints.width_inc);
- if (width - delta >= window->size_hints.min_width)
- width -= delta;
- }
- }
- else
- {
- delta = FLOOR (width - height * max_aspect, window->size_hints.width_inc);
- if (width - delta >= window->size_hints.min_width)
- width -= delta;
- else
- {
- delta = FLOOR (width / max_aspect - height, window->size_hints.height_inc);
- if (height + delta <= window->size_hints.max_height)
- height += delta;
- }
- }
- }
-
- /* Convert into terms of the direction of resize and reapply the
- * earlier constraints; this means aspect ratio becomes the
- * least-important of the constraints. If we wanted aspect to be
- * the most important, we could just not do this next bit.
- */
-
- if (current.width != width)
- {
- x_delta = width - current.width; /* positive delta to increase width */
- switch (x_direction)
- {
- case META_RESIZE_LEFT_OR_TOP:
- constrain_resize_left (window, &info, &current,
- - x_delta, new);
- break;
- case META_RESIZE_CENTER:
- constrain_resize_hcenter (window, &info, &current,
- x_delta, new);
- break;
- case META_RESIZE_RIGHT_OR_BOTTOM:
- constrain_resize_right (window, &info, &current,
- x_delta, new);
- break;
- }
- }
-
- if (current.height != height)
- {
- y_delta = height - current.height; /* positive to increase height */
-
- switch (y_direction)
- {
- case META_RESIZE_LEFT_OR_TOP:
- constrain_resize_top (window, &info, &current,
- - y_delta, new);
- break;
- case META_RESIZE_CENTER:
- constrain_resize_vcenter (window, &info, &current,
- y_delta, new);
- break;
- case META_RESIZE_RIGHT_OR_BOTTOM:
- constrain_resize_bottom (window, &info, &current,
- y_delta, new);
- break;
- }
- }
-
- current = *new;
- }
+static gboolean
+constrain_to_single_xinerama (MetaWindow *window,
+ ConstraintInfo *info,
+ ConstraintPriority priority,
+ gboolean check_only)
+{
+ if (priority > PRIORITY_ENTIRELY_VISIBLE_ON_SINGLE_XINERAMA)
+ return TRUE;
- meta_topic (META_DEBUG_GEOMETRY,
- "Constrained %s new %d,%d %dx%d old %d,%d %dx%d\n",
- window->desc,
- new->x, new->y, new->width, new->height,
- orig->x, orig->y, orig->width, orig->height);
+ /* Exit early if we know the constraint won't apply--note that this constraint
+ * is only meant for normal windows (e.g. we don't want docks to be shoved
+ * "onscreen" by their own strut) and we can't apply it to frameless windows
+ * or else users will be unable to move windows such as XMMS across xineramas.
+ */
+ if (window->type == META_WINDOW_DESKTOP ||
+ window->type == META_WINDOW_DOCK ||
+ window->screen->n_xinerama_infos == 1 ||
+ !window->require_on_single_xinerama ||
+ !window->frame ||
+ info->is_user_action)
+ return TRUE;
+
+ /* Have a helper function handle the constraint for us */
+ gboolean retval =
+ do_screen_and_xinerama_relative_constraints (window,
+ info->usable_xinerama_region,
+ info,
+ check_only);
+
+ /* Free up the data we allocated */
+ return retval;
}
-MetaResizeDirection
-meta_x_direction_from_gravity (int gravity)
+static gboolean
+constrain_fully_onscreen (MetaWindow *window,
+ ConstraintInfo *info,
+ ConstraintPriority priority,
+ gboolean check_only)
{
- switch (gravity)
- {
- case EastGravity:
- case NorthEastGravity:
- case SouthEastGravity:
- return META_RESIZE_LEFT_OR_TOP;
- break;
+ if (priority > PRIORITY_ENTIRELY_VISIBLE_ON_WORKAREA)
+ return TRUE;
- case WestGravity:
- case NorthWestGravity:
- case SouthWestGravity:
- case StaticGravity:
- return META_RESIZE_RIGHT_OR_BOTTOM;
- break;
-
- default:
- return META_RESIZE_CENTER;
- break;
- }
+ /* Exit early if we know the constraint won't apply--note that this constraint
+ * is only meant for normal windows (e.g. we don't want docks to be shoved
+ * "onscreen" by their own strut).
+ */
+ if (window->type == META_WINDOW_DESKTOP ||
+ window->type == META_WINDOW_DOCK ||
+ !window->require_fully_onscreen ||
+ info->is_user_action)
+ return TRUE;
+
+ /* Have a helper function handle the constraint for us */
+ gboolean retval =
+ do_screen_and_xinerama_relative_constraints (window,
+ info->usable_screen_region,
+ info,
+ check_only);
+
+ return retval;
}
-MetaResizeDirection
-meta_y_direction_from_gravity (int gravity)
+static gboolean
+constrain_partially_onscreen (MetaWindow *window,
+ ConstraintInfo *info,
+ ConstraintPriority priority,
+ gboolean check_only)
{
- switch (gravity)
- {
- case SouthGravity:
- case SouthWestGravity:
- case SouthEastGravity:
- return META_RESIZE_LEFT_OR_TOP;
- break;
+ if (priority > PRIORITY_PARTIALLY_VISIBLE_ON_WORKAREA)
+ return TRUE;
- case NorthGravity:
- case NorthWestGravity:
- case NorthEastGravity:
- case StaticGravity:
- return META_RESIZE_RIGHT_OR_BOTTOM;
- break;
-
- default:
- return META_RESIZE_CENTER;
- }
+ /* Exit early if we know the constraint won't apply--note that this constraint
+ * is only meant for normal windows (e.g. we don't want docks to be shoved
+ * "onscreen" by their own strut).
+ */
+ if (window->type == META_WINDOW_DESKTOP ||
+ window->type == META_WINDOW_DOCK)
+ return TRUE;
+
+ /* Determine how much offscreen things are allowed. We first need to
+ * figure out how much must remain on the screen. For that, we use 25%
+ * window width/height but clamp to the range of (10,75) pixels. This is
+ * somewhat of a seat of my pants random guess at what might look good.
+ * Then, the amount that is allowed off is just the window size minus
+ * this amount.
+ */
+ int horiz_amount = info->current.width / 4;
+ int vert_amount = info->current.height / 4;
+ horiz_amount = CLAMP (horiz_amount, 10, 75);
+ vert_amount = CLAMP (vert_amount, 10, 75);
+ horiz_amount = info->current.width - horiz_amount;
+ vert_amount = info->current.height - vert_amount;
+
+ /* Extend the region, have a helper function handle the constraint,
+ * then return the region to its original size.
+ */
+ meta_rectangle_expand_region (info->usable_screen_region,
+ horiz_amount,
+ horiz_amount,
+ vert_amount,
+ vert_amount);
+ gboolean retval =
+ do_screen_and_xinerama_relative_constraints (window,
+ info->usable_screen_region,
+ info,
+ check_only);
+ meta_rectangle_expand_region (info->usable_screen_region,
+ -horiz_amount,
+ -horiz_amount,
+ -vert_amount,
+ -vert_amount);
+
+ return retval;
}
diff --git a/src/constraints.h b/src/constraints.h
index de8555b..031b950 100644
--- a/src/constraints.h
+++ b/src/constraints.h
@@ -2,6 +2,7 @@
/*
* Copyright (C) 2002 Red Hat, Inc.
+ * Copyright (C) 2005 Elijah Newren
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -28,32 +29,18 @@
typedef enum
{
- META_RESIZE_LEFT_OR_TOP,
- META_RESIZE_CENTER,
- META_RESIZE_RIGHT_OR_BOTTOM
-} MetaResizeDirection;
+ META_IS_CONFIGURE_REQUEST = 1 << 0,
+ META_DO_GRAVITY_ADJUST = 1 << 1,
+ META_IS_USER_ACTION = 1 << 2,
+ META_IS_MOVE_ACTION = 1 << 3,
+ META_IS_RESIZE_ACTION = 1 << 4
+} MetaMoveResizeFlags;
void meta_window_constrain (MetaWindow *window,
- MetaFrameGeometry *fgeom,
+ MetaFrameGeometry *orig_fgeom,
+ MetaMoveResizeFlags flags,
+ int resize_gravity,
const MetaRectangle *orig,
- int x_move_delta,
- int y_move_delta,
- MetaResizeDirection x_direction,
- int x_delta,
- MetaResizeDirection y_direction,
- int y_delta,
MetaRectangle *new);
-MetaResizeDirection meta_x_direction_from_gravity (int gravity);
-MetaResizeDirection meta_y_direction_from_gravity (int gravity);
-
#endif /* META_CONSTRAINTS_H */
-
-
-
-
-
-
-
-
-
diff --git a/src/core.c b/src/core.c
index b3f6cd7..b4b6a5f 100644
--- a/src/core.c
+++ b/src/core.c
@@ -3,7 +3,7 @@
/*
* Copyright (C) 2001 Havoc Pennington
* Copyright (C) 2003 Rob Adams
- * Copyright (C) 2004 Elijah Newren
+ * Copyright (C) 2004, 2005 Elijah Newren
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -48,6 +48,23 @@ meta_core_get_client_size (Display *xdisplay,
*height = window->rect.height;
}
+gboolean
+meta_core_titlebar_is_onscreen (Display *xdisplay,
+ Window frame_xwindow)
+{
+ MetaDisplay *display;
+ MetaWindow *window;
+
+ display = meta_display_for_x_display (xdisplay);
+ window = meta_display_lookup_x_window (display, frame_xwindow);
+
+ if (window == NULL || window->frame == NULL)
+ meta_bug ("No such frame window 0x%lx!\n", frame_xwindow);
+
+ return meta_window_titlebar_is_onscreen (window);
+}
+
+
Window
meta_core_get_client_xwindow (Display *xdisplay,
Window frame_xwindow)
@@ -371,7 +388,8 @@ meta_core_maximize (Display *xdisplay,
if (window == NULL || window->frame == NULL)
meta_bug ("No such frame window 0x%lx!\n", frame_xwindow);
- meta_window_maximize (window);
+ meta_window_maximize (window,
+ META_MAXIMIZE_HORIZONTAL | META_MAXIMIZE_VERTICAL);
}
void
@@ -387,10 +405,12 @@ meta_core_toggle_maximize (Display *xdisplay,
if (window == NULL || window->frame == NULL)
meta_bug ("No such frame window 0x%lx!\n", frame_xwindow);
- if (window->maximized)
- meta_window_unmaximize (window);
+ if (META_WINDOW_MAXIMIZED (window))
+ meta_window_unmaximize (window,
+ META_MAXIMIZE_HORIZONTAL | META_MAXIMIZE_VERTICAL);
else
- meta_window_maximize (window);
+ meta_window_maximize (window,
+ META_MAXIMIZE_HORIZONTAL | META_MAXIMIZE_VERTICAL);
}
void
@@ -406,7 +426,8 @@ meta_core_unmaximize (Display *xdisplay,
if (window == NULL || window->frame == NULL)
meta_bug ("No such frame window 0x%lx!\n", frame_xwindow);
- meta_window_unmaximize (window);
+ meta_window_unmaximize (window,
+ META_MAXIMIZE_HORIZONTAL | META_MAXIMIZE_VERTICAL);
}
void
@@ -691,6 +712,9 @@ meta_core_get_menu_accelerator (MetaMenuOp menu_op,
case META_MENU_OP_MOVE_DOWN:
name = META_KEYBINDING_MOVE_WORKSPACE_DOWN;
break;
+ case META_MENU_OP_RECOVER:
+ /* No keybinding for this one */
+ break;
}
if (name)
@@ -852,9 +876,9 @@ meta_core_get_screen_size (Display *xdisplay,
meta_bug ("No such frame window 0x%lx!\n", frame_on_screen);
if (width)
- *width = window->screen->width;
+ *width = window->screen->rect.width;
if (height)
- *height = window->screen->height;
+ *height = window->screen->rect.height;
}
void
diff --git a/src/core.h b/src/core.h
index 2c7c4e6..b361550 100644
--- a/src/core.h
+++ b/src/core.h
@@ -2,6 +2,7 @@
/*
* Copyright (C) 2001 Havoc Pennington
+ * Copyright (C) 2005 Elijah Newren
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -31,6 +32,9 @@ void meta_core_get_client_size (Display *xdisplay,
int *width,
int *height);
+gboolean meta_core_titlebar_is_onscreen (Display *xdisplay,
+ Window frame_xwindow);
+
Window meta_core_get_client_xwindow (Display *xdisplay,
Window frame_xwindow);
diff --git a/src/display.c b/src/display.c
index 3ede1f1..2d2d20f 100644
--- a/src/display.c
+++ b/src/display.c
@@ -519,6 +519,8 @@ meta_display_open (const char *name)
display->grab_screen = NULL;
display->grab_resize_popup = NULL;
+ display->grab_edge_resistance_data = NULL;
+
#ifdef HAVE_XSYNC
{
int major, minor;
@@ -1336,6 +1338,15 @@ handle_net_moveresize_window (MetaDisplay* display,
if (window)
{
+ /* FIXME!!!! I'm pretty sure this is wrong except _maybe_ for the
+ * resize-only case; see comment at beginning of
+ * meta_window_move_resize_internal(). Basically, this should act
+ * like a configure request--meaning that it should count as an app
+ * specified change instead of a user one, and the position needs to
+ * be fixed up with adjust_for_gravity(). In particular,
+ * meta_window_resize_with_gravity(), meta_window_resize(), and
+ * meta_window_move_resize() should probably NOT be called.
+ */
meta_window_get_gravity_position (window, &x, &y);
width = window->rect.width;
height = window->rect.height;
@@ -1391,10 +1402,14 @@ handle_net_restack_window (MetaDisplay* display,
if (window)
{
- /*
- * The EWMH includes a sibling for the restack request, but we
- * don't currently support these types of raises.
+ /* FIXME: The EWMH includes a sibling for the restack request, but we
+ * (stupidly) don't currently support these types of raises.
*
+ * Also, unconditionally following these is REALLY stupid--we should
+ * combine this code with the stuff in
+ * meta_window_configure_request() which is smart about whether to
+ * follow the request or do something else (though not smart enough
+ * and is also too stupid to handle the sibling stuff).
*/
switch (event->xclient.data.l[2])
{
@@ -1670,36 +1685,49 @@ event_callback (XEvent *event,
{
if (window->has_resize_func)
{
- gboolean north;
- gboolean west;
+ gboolean north, south;
+ gboolean west, east;
int root_x, root_y;
MetaGrabOp op;
meta_window_get_position (window, &root_x, &root_y);
- west = event->xbutton.x_root < (root_x + window->rect.width / 2);
- north = event->xbutton.y_root < (root_y + window->rect.height / 2);
+ west = event->xbutton.x_root < (root_x + 1 * window->rect.width / 3);
+ east = event->xbutton.x_root > (root_x + 2 * window->rect.width / 3);
+ north = event->xbutton.y_root < (root_y + 1 * window->rect.height / 3);
+ south = event->xbutton.y_root > (root_y + 2 * window->rect.height / 3);
- if (west && north)
+ if (north && west)
op = META_GRAB_OP_RESIZING_NW;
- else if (west)
- op = META_GRAB_OP_RESIZING_SW;
- else if (north)
+ else if (north && east)
op = META_GRAB_OP_RESIZING_NE;
- else
+ else if (south && west)
+ op = META_GRAB_OP_RESIZING_SW;
+ else if (south && east)
op = META_GRAB_OP_RESIZING_SE;
+ else if (north)
+ op = META_GRAB_OP_RESIZING_N;
+ else if (west)
+ op = META_GRAB_OP_RESIZING_W;
+ else if (east)
+ op = META_GRAB_OP_RESIZING_E;
+ else if (south)
+ op = META_GRAB_OP_RESIZING_S;
+ else /* Middle region is no-op to avoid user triggering wrong action */
+ op = META_GRAB_OP_NONE;
- meta_display_begin_grab_op (display,
- window->screen,
- window,
- op,
- TRUE,
- event->xbutton.serial,
- event->xbutton.button,
- 0,
- event->xbutton.time,
- event->xbutton.x_root,
- event->xbutton.y_root);
+ if (op != META_GRAB_OP_NONE)
+ meta_display_begin_grab_op (display,
+ window->screen,
+ window,
+ op,
+ TRUE,
+ event->xbutton.serial,
+ event->xbutton.button,
+ 0,
+ event->xbutton.time,
+ event->xbutton.x_root,
+ event->xbutton.y_root);
}
}
else if (event->xbutton.button == 3)
@@ -3244,6 +3272,7 @@ meta_display_begin_grab_op (MetaDisplay *display,
display->grab_old_window_stacking = NULL;
#ifdef HAVE_XSYNC
display->grab_sync_request_alarm = None;
+ display->grab_last_user_action_was_snap = FALSE;
#endif
display->grab_was_cancelled = FALSE;
@@ -3335,6 +3364,18 @@ meta_display_begin_grab_op (MetaDisplay *display,
g_assert (display->grab_window != NULL || display->grab_screen != NULL);
g_assert (display->grab_op != META_GRAB_OP_NONE);
+ /* If this is a move or resize, cache the window edges for
+ * resistance/snapping
+ */
+ if (meta_grab_op_is_resizing (display->grab_op) ||
+ meta_grab_op_is_moving (display->grab_op))
+ {
+ meta_topic (META_DEBUG_WINDOW_OPS,
+ "Computing edges to resist-movement or snap-to for %s.\n",
+ window->desc);
+ meta_display_compute_resistance_and_snapping_edges (display);
+ }
+
/* Save the old stacking */
if (GRAB_OP_IS_WINDOW_SWITCH (display->grab_op))
{
@@ -3408,6 +3449,15 @@ meta_display_end_grab_op (MetaDisplay *display,
display->ungrab_should_not_cause_focus_window = display->grab_xwindow;
}
+ /* If this was a move or resize clear out the edge cache */
+ if (meta_grab_op_is_resizing (display->grab_op) ||
+ meta_grab_op_is_moving (display->grab_op))
+ {
+ meta_topic (META_DEBUG_WINDOW_OPS,
+ "Clearing out the edges for resistance/snapping");
+ meta_display_cleanup_edges (display);
+ }
+
if (display->grab_old_window_stacking != NULL)
{
meta_topic (META_DEBUG_WINDOW_OPS,
@@ -4276,53 +4326,6 @@ meta_resize_gravity_from_grab_op (MetaGrabOp op)
return gravity;
}
-gboolean
-meta_rectangle_intersect (MetaRectangle *src1,
- MetaRectangle *src2,
- MetaRectangle *dest)
-{
- int dest_x, dest_y;
- int dest_w, dest_h;
- int return_val;
-
- g_return_val_if_fail (src1 != NULL, FALSE);
- g_return_val_if_fail (src2 != NULL, FALSE);
- g_return_val_if_fail (dest != NULL, FALSE);
-
- return_val = FALSE;
-
- dest_x = MAX (src1->x, src2->x);
- dest_y = MAX (src1->y, src2->y);
- dest_w = MIN (src1->x + src1->width, src2->x + src2->width) - dest_x;
- dest_h = MIN (src1->y + src1->height, src2->y + src2->height) - dest_y;
-
- if (dest_w > 0 && dest_h > 0)
- {
- dest->x = dest_x;
- dest->y = dest_y;
- dest->width = dest_w;
- dest->height = dest_h;
- return_val = TRUE;
- }
- else
- {
- dest->width = 0;
- dest->height = 0;
- }
-
- return return_val;
-}
-
-gboolean
-meta_rectangle_equal (const MetaRectangle *src1,
- const MetaRectangle *src2)
-{
- return ((src1->x == src2->x) &&
- (src1->y == src2->y) &&
- (src1->width == src2->width) &&
- (src1->height == src2->height));
-}
-
static MetaScreen*
find_screen_for_selection (MetaDisplay *display,
Window owner,
diff --git a/src/display.h b/src/display.h
index 6031533..c651c5e 100644
--- a/src/display.h
+++ b/src/display.h
@@ -33,6 +33,7 @@
#include <X11/Xlib.h>
#include "eventqueue.h"
#include "common.h"
+#include "boxes.h"
#ifdef HAVE_STARTUP_NOTIFICATION
#include <libsn/sn.h>
@@ -44,17 +45,6 @@
#define meta_XFree(p) do { if ((p)) XFree ((p)); } while (0)
-/* this doesn't really belong here, oh well. */
-typedef struct _MetaRectangle MetaRectangle;
-
-struct _MetaRectangle
-{
- int x;
- int y;
- int width;
- int height;
-};
-
typedef struct MetaCompositor MetaCompositor;
typedef struct _MetaDisplay MetaDisplay;
typedef struct _MetaFrame MetaFrame;
@@ -68,6 +58,8 @@ typedef struct _MetaWorkspace MetaWorkspace;
typedef struct _MetaWindowPropHooks MetaWindowPropHooks;
typedef struct _MetaGroupPropHooks MetaGroupPropHooks;
+typedef struct MetaEdgeResistanceData MetaEdgeResistanceData;
+
typedef void (* MetaWindowPingFunc) (MetaDisplay *display,
Window xwindow,
Time timestamp,
@@ -280,6 +272,7 @@ struct _MetaDisplay
int grab_wireframe_last_display_width;
int grab_wireframe_last_display_height;
GList* grab_old_window_stacking;
+ MetaEdgeResistanceData *grab_edge_resistance_data;
/* we use property updates as sentinels for certain window focus events
* to avoid some race conditions on EnterNotify events
@@ -350,6 +343,7 @@ struct _MetaDisplay
int render_error_base;
#endif
#ifdef HAVE_XSYNC
+ unsigned int grab_last_user_action_was_snap;
unsigned int have_xsync : 1;
#define META_DISPLAY_HAS_XSYNC(display) ((display)->have_xsync)
#else
@@ -459,6 +453,10 @@ void meta_display_grab_focus_window_button (MetaDisplay *display,
void meta_display_ungrab_focus_window_button (MetaDisplay *display,
MetaWindow *window);
+/* Next two functions are defined in edge-resistance.c */
+void meta_display_compute_resistance_and_snapping_edges (MetaDisplay *display);
+void meta_display_cleanup_edges (MetaDisplay *display);
+
/* make a request to ensure the event serial has changed */
void meta_display_increment_event_serial (MetaDisplay *display);
@@ -490,8 +488,6 @@ typedef enum
{
META_TAB_LIST_NORMAL,
META_TAB_LIST_DOCKS
-
-
} MetaTabList;
GList* meta_display_get_tab_list (MetaDisplay *display,
@@ -516,12 +512,6 @@ int meta_resize_gravity_from_grab_op (MetaGrabOp op);
gboolean meta_grab_op_is_moving (MetaGrabOp op);
gboolean meta_grab_op_is_resizing (MetaGrabOp op);
-gboolean meta_rectangle_intersect (MetaRectangle *src1,
- MetaRectangle *src2,
- MetaRectangle *dest);
-gboolean meta_rectangle_equal (const MetaRectangle *src1,
- const MetaRectangle *src2);
-
void meta_display_devirtualize_modifiers (MetaDisplay *display,
MetaVirtualModifier modifiers,
unsigned int *mask);
diff --git a/src/edge-resistance.c b/src/edge-resistance.c
new file mode 100644
index 0000000..784581d
--- /dev/null
+++ b/src/edge-resistance.c
@@ -0,0 +1,1278 @@
+/* Edge resistance for move/resize operations */
+
+/*
+ * Copyright (C) 2005 Elijah Newren
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "edge-resistance.h"
+#include "boxes.h"
+#include "display.h"
+#include "workspace.h"
+
+/* A simple macro for whether a given window's edges are potentially
+ * relevant for resistance/snapping during a move/resize operation
+ */
+#define WINDOW_EDGES_RELEVANT(window, display) \
+ meta_window_should_be_showing (window) && \
+ window->screen == display->grab_screen && \
+ window != display->grab_window && \
+ window->type != META_WINDOW_DESKTOP && \
+ window->type != META_WINDOW_MENU && \
+ window->type != META_WINDOW_SPLASHSCREEN
+
+struct ResistanceDataForAnEdge
+{
+ gboolean timeout_setup;
+ guint timeout_id;
+ int timeout_edge_pos;
+ gboolean timeout_over;
+ GSourceFunc timeout_func;
+ MetaWindow *window;
+ int keyboard_buildup;
+ gboolean allow_past_screen_edge;
+};
+typedef struct ResistanceDataForAnEdge ResistanceDataForAnEdge;
+
+struct MetaEdgeResistanceData
+{
+ GArray *left_edges;
+ GArray *right_edges;
+ GArray *top_edges;
+ GArray *bottom_edges;
+
+ ResistanceDataForAnEdge left_data;
+ ResistanceDataForAnEdge right_data;
+ ResistanceDataForAnEdge top_data;
+ ResistanceDataForAnEdge bottom_data;
+};
+
+static int
+find_index_of_edge_near_position (const GArray *edges,
+ int position,
+ gboolean want_interval_min,
+ gboolean horizontal)
+{
+ /* This is basically like a binary search, except that we're trying to
+ * find a range instead of an exact value. So, if we have in our array
+ * Value: 3 27 316 316 316 505 522 800 1213
+ * Index: 0 1 2 3 4 5 6 7 8
+ * and we call this function with position=500 & want_interval_min=TRUE
+ * then we should get 5 (because 505 is the first value bigger than 500).
+ * If we call this function with position=805 and want_interval_min=FALSE
+ * then we should get 7 (because 800 is the last value smaller than 800).
+ * A couple more, to make things clear:
+ * position want_interval_min correct_answer
+ * 316 TRUE 2
+ * 316 FALSE 4
+ * 2 FALSE -1
+ * 2000 TRUE 9
+ */
+ int low, high, mid;
+ int compare;
+ MetaEdge *edge;
+
+ /* Initialize mid, edge, & compare in the off change that the array only
+ * has one element.
+ */
+ mid = 0;
+ edge = g_array_index (edges, MetaEdge*, mid);
+ compare = horizontal ? edge->rect.x : edge->rect.y;
+
+ /* Begin the search... */
+ low = 0;
+ high = edges->len - 1;
+ while (low < high)
+ {
+ mid = low + (high - low)/2;
+ edge = g_array_index (edges, MetaEdge*, mid);
+ compare = horizontal ? edge->rect.x : edge->rect.y;
+
+ if (compare == position)
+ break;
+
+ if (compare > position)
+ high = mid - 1;
+ else
+ low = mid + 1;
+ }
+
+ /* mid should now be _really_ close to the index we want, so we start
+ * linearly searching. However, note that we don't know if mid is less
+ * than or greater than what we need and it's possible that there are
+ * several equal values equal to what we were searching for and we ended
+ * up in the middle of them instead of at the end. So we may need to
+ * move mid multiple locations over.
+ */
+ if (want_interval_min)
+ {
+ while (compare >= position && mid > 0)
+ {
+ mid--;
+ edge = g_array_index (edges, MetaEdge*, mid);
+ compare = horizontal ? edge->rect.x : edge->rect.y;
+ }
+ while (compare < position && mid < (int)edges->len - 1)
+ {
+ mid++;
+ edge = g_array_index (edges, MetaEdge*, mid);
+ compare = horizontal ? edge->rect.x : edge->rect.y;
+ }
+
+ /* Special case for no values in array big enough */
+ if (compare < position)
+ return edges->len;
+
+ /* Return the found value */
+ return mid;
+ }
+ else
+ {
+ while (compare <= position && mid < (int)edges->len - 1)
+ {
+ mid++;
+ edge = g_array_index (edges, MetaEdge*, mid);
+ compare = horizontal ? edge->rect.x : edge->rect.y;
+ }
+ while (compare > position && mid > 0)
+ {
+ mid--;
+ edge = g_array_index (edges, MetaEdge*, mid);
+ compare = horizontal ? edge->rect.x : edge->rect.y;
+ }
+
+ /* Special case for no values in array small enough */
+ if (compare > position)
+ return -1;
+
+ /* Return the found value */
+ return mid;
+ }
+}
+
+static gboolean
+points_on_same_side (int ref, int pt1, int pt2)
+{
+ return (pt1 - ref) * (pt2 - ref) > 0;
+}
+
+static int
+find_nearest_position (const GArray *edges,
+ int position,
+ int old_position,
+ const MetaRectangle *new_rect,
+ gboolean horizontal,
+ gboolean only_forward)
+{
+ /* This is basically just a binary search except that we're looking
+ * for the value closest to position, rather than finding that
+ * actual value. Also, we ignore any edges that aren't relevant
+ * given the horizontal/vertical position of new_rect.
+ */
+ int low, high, mid;
+ int compare;
+ MetaEdge *edge;
+ int best, best_dist, i;
+
+ /* Initialize mid, edge, & compare in the off change that the array only
+ * has one element.
+ */
+ mid = 0;
+ edge = g_array_index (edges, MetaEdge*, mid);
+ compare = horizontal ? edge->rect.x : edge->rect.y;
+
+ /* Begin the search... */
+ low = 0;
+ high = edges->len - 1;
+ while (low < high)
+ {
+ mid = low + (high - low)/2;
+ edge = g_array_index (edges, MetaEdge*, mid);
+ compare = horizontal ? edge->rect.x : edge->rect.y;
+
+ if (compare == position)
+ break;
+
+ if (compare > position)
+ high = mid - 1;
+ else
+ low = mid + 1;
+ }
+
+ /* mid should now be _really_ close to the index we want, so we
+ * start searching nearby for something that overlaps and is closer
+ * than the original position.
+ */
+ best = old_position;
+ best_dist = INT_MAX;
+
+ /* Start the search at mid */
+ edge = g_array_index (edges, MetaEdge*, mid);
+ compare = horizontal ? edge->rect.x : edge->rect.y;
+ gboolean edges_align = horizontal ?
+ meta_rectangle_vert_overlap (&edge->rect, new_rect) :
+ meta_rectangle_horiz_overlap (&edge->rect, new_rect);
+ if (edges_align &&
+ (!only_forward || !points_on_same_side (position, compare, old_position)))
+ {
+ int dist = ABS (compare - position);
+ if (dist < best_dist)
+ {
+ best = compare;
+ best_dist = dist;
+ }
+ }
+
+ /* Now start searching higher than mid */
+ for (i = mid + 1; i < (int)edges->len; i++)
+ {
+ edge = g_array_index (edges, MetaEdge*, i);
+ compare = horizontal ? edge->rect.x : edge->rect.y;
+
+ gboolean edges_align = horizontal ?
+ meta_rectangle_vert_overlap (&edge->rect, new_rect) :
+ meta_rectangle_horiz_overlap (&edge->rect, new_rect);
+
+ if (edges_align &&
+ (!only_forward ||
+ !points_on_same_side (position, compare, old_position)))
+ {
+ int dist = ABS (compare - position);
+ if (dist < best_dist)
+ {
+ best = compare;
+ best_dist = dist;
+ }
+ break;
+ }
+ }
+
+ /* Now start searching lower than mid */
+ for (i = mid-1; i >= 0; i--)
+ {
+ edge = g_array_index (edges, MetaEdge*, i);
+ compare = horizontal ? edge->rect.x : edge->rect.y;
+
+ gboolean edges_align = horizontal ?
+ meta_rectangle_vert_overlap (&edge->rect, new_rect) :
+ meta_rectangle_horiz_overlap (&edge->rect, new_rect);
+
+ if (edges_align &&
+ (!only_forward ||
+ !points_on_same_side (position, compare, old_position)))
+ {
+ int dist = ABS (compare - position);
+ if (dist < best_dist)
+ {
+ best = compare;
+ best_dist = dist;
+ }
+ break;
+ }
+ }
+
+ /* Return the best one found */
+ return best;
+}
+
+static gboolean
+movement_towards_edge (MetaDirection side, int increment)
+{
+ switch (side)
+ {
+ case META_DIRECTION_LEFT:
+ case META_DIRECTION_TOP:
+ return increment < 0;
+ case META_DIRECTION_RIGHT:
+ case META_DIRECTION_BOTTOM:
+ return increment > 0;
+ }
+
+ g_assert_not_reached ();
+}
+
+static gboolean
+edge_resistance_timeout (gpointer data)
+{
+ ResistanceDataForAnEdge *resistance_data = data;
+
+ resistance_data->timeout_over = TRUE;
+ resistance_data->timeout_id = 0;
+ (*resistance_data->timeout_func)(resistance_data->window);
+
+ return FALSE;
+}
+
+static int
+apply_edge_resistance (MetaWindow *window,
+ int old_pos,
+ int new_pos,
+ const MetaRectangle *new_rect,
+ GArray *edges,
+ ResistanceDataForAnEdge *resistance_data,
+ GSourceFunc timeout_func,
+ gboolean xdir,
+ gboolean keyboard_op)
+{
+ int i, begin, end;
+ gboolean okay_to_clear_keyboard_buildup = FALSE;
+ int keyboard_buildup_edge = G_MAXINT;
+ gboolean increasing = new_pos > old_pos;
+ int increment = increasing ? 1 : -1;
+
+ const int PIXEL_DISTANCE_THRESHOLD_WINDOW = 16;
+ const int PIXEL_DISTANCE_THRESHOLD_XINERAMA = 32;
+ const int PIXEL_DISTANCE_THRESHOLD_SCREEN = 32;
+ const int TIMEOUT_RESISTANCE_LENGTH_MS_WINDOW = 0;
+ const int TIMEOUT_RESISTANCE_LENGTH_MS_XINERAMA = 100;
+ const int TIMEOUT_RESISTANCE_LENGTH_MS_SCREEN = 750;
+ const int KEYBOARD_BUILDUP_THRESHOLD_WINDOW = 16;
+ const int KEYBOARD_BUILDUP_THRESHOLD_XINERAMA = 24;
+ const int KEYBOARD_BUILDUP_THRESHOLD_SCREEN = 32;
+
+ /* Quit if no movement was specified */
+ if (old_pos == new_pos)
+ return new_pos;
+
+ /* Remove the old timeout if it's no longer relevant */
+ if (resistance_data->timeout_setup &&
+ ((resistance_data->timeout_edge_pos > old_pos &&
+ resistance_data->timeout_edge_pos > new_pos) ||
+ (resistance_data->timeout_edge_pos < old_pos &&
+ resistance_data->timeout_edge_pos < new_pos)))
+ {
+ resistance_data->timeout_setup = FALSE;
+ if (resistance_data->timeout_id != 0)
+ {
+ g_source_remove (resistance_data->timeout_id);
+ resistance_data->timeout_id = 0;
+ }
+ }
+
+ /* Get the range of indices in the edge array that we move past/to. */
+ begin = find_index_of_edge_near_position (edges, old_pos, increasing, xdir);
+ end = find_index_of_edge_near_position (edges, new_pos, !increasing, xdir);
+
+ /* Loop over all these edges we're moving past/to. */
+ i = begin;
+ while ((increasing && i <= end) ||
+ (!increasing && i >= end))
+ {
+ gboolean edges_align;
+ MetaEdge *edge = g_array_index (edges, MetaEdge*, i);
+ int compare = xdir ? edge->rect.x : edge->rect.y;
+
+ /* Find out if this edge is relevant */
+ edges_align = xdir ?
+ meta_rectangle_vert_overlap (&edge->rect, new_rect) :
+ meta_rectangle_horiz_overlap (&edge->rect, new_rect);
+
+ /* Nothing to do unless the edges align */
+ if (!edges_align)
+ {
+ /* Go to the next edge in the range */
+ i += increment;
+ continue;
+ }
+
+ /* Rest is easier to read if we split on keyboard vs. mouse op */
+ if (keyboard_op)
+ {
+ /* KEYBOARD ENERGY BUILDUP RESISTANCE: If the user has is moving
+ * fast enough or has already built up enough "energy", then let
+ * the user past the edge, otherwise stop at this edge. If the
+ * user was previously stopped at this edge, add movement amount
+ * to the built up energy.
+ */
+
+ /* First, determine the amount of the resistance */
+ int resistance = 0;
+ switch (edge->edge_type)
+ {
+ case META_EDGE_WINDOW:
+ resistance = KEYBOARD_BUILDUP_THRESHOLD_WINDOW;
+ break;
+ case META_EDGE_XINERAMA:
+ resistance = KEYBOARD_BUILDUP_THRESHOLD_XINERAMA;
+ break;
+ case META_EDGE_SCREEN:
+ resistance = KEYBOARD_BUILDUP_THRESHOLD_SCREEN;
+ break;
+ }
+
+ /* Clear any previous buildup if we've run into an edge at a
+ * different location than what we were building up on before.
+ * See below for more details where these get set.
+ */
+ if (okay_to_clear_keyboard_buildup &&
+ compare != keyboard_buildup_edge)
+ {
+ okay_to_clear_keyboard_buildup = FALSE;
+ resistance_data->keyboard_buildup = 0;
+ }
+
+ /* Determine the threshold */
+ int threshold = resistance - resistance_data->keyboard_buildup;
+
+ /* See if threshold hasn't been met yet or not */
+ if (ABS (compare - new_pos) < threshold)
+ {
+ if (resistance_data->keyboard_buildup != 0)
+ resistance_data->keyboard_buildup += ABS (new_pos - compare);
+ else
+ resistance_data->keyboard_buildup = 1; /* 0 causes stuckage */
+ return compare;
+ }
+ else
+ {
+ /* It may be the case that there are two windows with edges
+ * at the same location. If so, the buildup ought to count
+ * towards both edges. So we just not that it's okay to
+ * clear the buildup once we find an edge at a different
+ * location.
+ */
+ okay_to_clear_keyboard_buildup = TRUE;
+ keyboard_buildup_edge = compare;
+ }
+ }
+ else /* mouse op */
+ {
+ /* INFINITE RESISTANCE for screen edges under certain cases; If
+ * the edge is relevant and we're moving towards it and it's a
+ * screen edge and infinite resistance has been requested for
+ * this particular grab op then don't allow movement past it.
+ */
+ if (edge->edge_type == META_EDGE_SCREEN &&
+ !resistance_data->allow_past_screen_edge &&
+ movement_towards_edge (edge->side_type, increment))
+ {
+ return compare;
+ }
+
+ /* TIMEOUT RESISTANCE: If the edge is relevant and we're moving
+ * towards it, then we may want to have some kind of time delay
+ * before the user can move past this edge.
+ */
+ if (movement_towards_edge (edge->side_type, increment))
+ {
+ /* First, determine the length of time for the resistance */
+ int timeout_length_ms = 0;
+ switch (edge->edge_type)
+ {
+ case META_EDGE_WINDOW:
+ timeout_length_ms = TIMEOUT_RESISTANCE_LENGTH_MS_WINDOW;
+ break;
+ case META_EDGE_XINERAMA:
+ if (window->require_on_single_xinerama)
+ timeout_length_ms = TIMEOUT_RESISTANCE_LENGTH_MS_XINERAMA;
+ break;
+ case META_EDGE_SCREEN:
+ if (window->require_fully_onscreen)
+ timeout_length_ms = TIMEOUT_RESISTANCE_LENGTH_MS_SCREEN;
+ break;
+ }
+
+ if (!resistance_data->timeout_setup &&
+ timeout_length_ms != 0)
+ {
+ resistance_data->timeout_id =
+ g_timeout_add (timeout_length_ms,
+ edge_resistance_timeout,
+ resistance_data);
+ resistance_data->timeout_setup = TRUE;
+ resistance_data->timeout_edge_pos = compare;
+ resistance_data->timeout_over = FALSE;
+ resistance_data->timeout_func = timeout_func;
+ resistance_data->window = window;
+ }
+ if (!resistance_data->timeout_over &&
+ timeout_length_ms != 0)
+ return compare;
+ }
+
+ /* PIXEL DISTANCE MOUSE RESISTANCE: If the edge matters and the
+ * user hasn't moved at least threshold pixels past this edge,
+ * stop movement at this edge. (Note that this is different from
+ * keyboard resistance precisely because keyboard move ops are
+ * relative to previous positions, whereas mouse move ops are
+ * relative to differences in mouse position and mouse position
+ * is an absolute quantity rather than a relative quantity)
+ */
+
+ /* First, determine the threshold */
+ int threshold = 0;
+ switch (edge->edge_type)
+ {
+ case META_EDGE_WINDOW:
+ threshold = PIXEL_DISTANCE_THRESHOLD_WINDOW;
+ break;
+ case META_EDGE_XINERAMA:
+ threshold = PIXEL_DISTANCE_THRESHOLD_XINERAMA;
+ break;
+ case META_EDGE_SCREEN:
+ threshold = PIXEL_DISTANCE_THRESHOLD_SCREEN;
+ break;
+ }
+
+ if (ABS (compare - new_pos) < threshold)
+ return compare;
+ }
+
+ /* Go to the next edge in the range */
+ i += increment;
+ }
+
+ /* If we didn't run into any new edges in keyboard buildup but had moved
+ * far enough to get past the last one, clear the buildup
+ */
+ if (okay_to_clear_keyboard_buildup && new_pos != keyboard_buildup_edge)
+ resistance_data->keyboard_buildup = 0;
+
+ return new_pos;
+}
+
+static int
+apply_edge_snapping (int old_pos,
+ int new_pos,
+ const MetaRectangle *new_rect,
+ GArray *edges1,
+ GArray *edges2,
+ gboolean xdir,
+ gboolean keyboard_op)
+{
+ int pos1, pos2;
+ int best;
+
+ if (old_pos == new_pos)
+ return new_pos;
+
+ /* We look at two sets of edges (e.g. left and right) individually
+ * finding the nearest position among each set of edges and then later
+ * finding the better of these two bests.
+ */
+ pos1 = find_nearest_position (edges1,
+ new_pos,
+ old_pos,
+ new_rect,
+ xdir,
+ keyboard_op);
+ pos2 = find_nearest_position (edges2,
+ new_pos,
+ old_pos,
+ new_rect,
+ xdir,
+ keyboard_op);
+
+ /* For keyboard snapping, ignore either pos1 or pos2 if they aren't in the
+ * right direction.
+ */
+ if (keyboard_op)
+ {
+ if (!points_on_same_side (old_pos, pos1, new_pos))
+ return pos2;
+ if (!points_on_same_side (old_pos, pos2, new_pos))
+ return pos1;
+ }
+
+ /* Find the better of pos1 and pos2 and return it */
+ if (ABS (pos1 - new_pos) < ABS (pos2 - new_pos))
+ best = pos1;
+ else
+ best = pos2;
+
+ /* If mouse snap-moving, the user could easily accidentally move just a
+ * couple pixels in a direction they didn't mean to move; so ignore snap
+ * movement in those cases unless it's only a small number of pixels
+ * anyway.
+ */
+ if (!keyboard_op &&
+ ABS (best - old_pos) >= 8 &&
+ ABS (new_pos - old_pos) < 8)
+ return old_pos;
+ else
+ /* Otherwise, return the best of the snapping positions found */
+ return best;
+}
+
+/* This function takes the position (including any frame) of the window and
+ * a proposed new position (ignoring edge resistance/snapping), and then
+ * applies edge resistance to EACH edge (separately) updating new_outer.
+ * It returns true if new_outer is modified, false otherwise.
+ *
+ * display->grab_edge_resistance_data MUST already be setup or calling this
+ * function will cause a crash.
+ */
+static gboolean
+apply_edge_resistance_to_each_side (MetaDisplay *display,
+ MetaWindow *window,
+ const MetaRectangle *old_outer,
+ MetaRectangle *new_outer,
+ GSourceFunc timeout_func,
+ gboolean auto_snap,
+ gboolean keyboard_op)
+{
+ MetaEdgeResistanceData *edge_data;
+ MetaRectangle modified_rect;
+ gboolean modified;
+ int new_left, new_right, new_top, new_bottom;
+
+ g_assert (display->grab_edge_resistance_data != NULL);
+ edge_data = display->grab_edge_resistance_data;
+
+ if (auto_snap)
+ {
+ /* Do the auto snapping instead of normal edge resistance; in all
+ * cases, we allow snapping to opposite kinds of edges (e.g. left
+ * sides of windows to both left and right edges.
+ */
+
+ new_left = apply_edge_snapping (BOX_LEFT (*old_outer),
+ BOX_LEFT (*new_outer),
+ new_outer,
+ edge_data->left_edges,
+ edge_data->right_edges,
+ TRUE,
+ keyboard_op);
+
+ new_right = apply_edge_snapping (BOX_RIGHT (*old_outer),
+ BOX_RIGHT (*new_outer),
+ new_outer,
+ edge_data->left_edges,
+ edge_data->right_edges,
+ TRUE,
+ keyboard_op);
+
+ new_top = apply_edge_snapping (BOX_TOP (*old_outer),
+ BOX_TOP (*new_outer),
+ new_outer,
+ edge_data->top_edges,
+ edge_data->bottom_edges,
+ FALSE,
+ keyboard_op);
+
+ new_bottom = apply_edge_snapping (BOX_BOTTOM (*old_outer),
+ BOX_BOTTOM (*new_outer),
+ new_outer,
+ edge_data->top_edges,
+ edge_data->bottom_edges,
+ FALSE,
+ keyboard_op);
+ }
+ else
+ {
+ /* Now, apply the normal edge resistance */
+ new_left = apply_edge_resistance (window,
+ BOX_LEFT (*old_outer),
+ BOX_LEFT (*new_outer),
+ new_outer,
+ edge_data->left_edges,
+ &edge_data->left_data,
+ timeout_func,
+ TRUE,
+ keyboard_op);
+ new_right = apply_edge_resistance (window,
+ BOX_RIGHT (*old_outer),
+ BOX_RIGHT (*new_outer),
+ new_outer,
+ edge_data->right_edges,
+ &edge_data->right_data,
+ timeout_func,
+ TRUE,
+ keyboard_op);
+ new_top = apply_edge_resistance (window,
+ BOX_TOP (*old_outer),
+ BOX_TOP (*new_outer),
+ new_outer,
+ edge_data->top_edges,
+ &edge_data->top_data,
+ timeout_func,
+ FALSE,
+ keyboard_op);
+ new_bottom = apply_edge_resistance (window,
+ BOX_BOTTOM (*old_outer),
+ BOX_BOTTOM (*new_outer),
+ new_outer,
+ edge_data->bottom_edges,
+ &edge_data->bottom_data,
+ timeout_func,
+ FALSE,
+ keyboard_op);
+ }
+
+ /* Determine whether anything changed, and save the changes */
+ modified_rect = meta_rect (new_left,
+ new_top,
+ new_right - new_left,
+ new_bottom - new_top);
+ modified = !meta_rectangle_equal (new_outer, &modified_rect);
+ *new_outer = modified_rect;
+ return modified;
+}
+
+void
+meta_display_cleanup_edges (MetaDisplay *display)
+{
+ MetaEdgeResistanceData *edge_data = display->grab_edge_resistance_data;
+ g_assert (edge_data != NULL);
+ guint i,j;
+
+ /* We first need to clean out any window edges */
+ for (i = 0; i < 4; i++)
+ {
+ GArray *tmp = NULL;
+ switch (i)
+ {
+ case 0:
+ tmp = edge_data->left_edges;
+ break;
+ case 1:
+ tmp = edge_data->right_edges;
+ break;
+ case 2:
+ tmp = edge_data->top_edges;
+ break;
+ case 3:
+ tmp = edge_data->bottom_edges;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ for (j = 0; j < tmp->len; j++)
+ {
+ MetaEdge *edge = g_array_index (tmp, MetaEdge*, j);
+ if (edge->edge_type == META_EDGE_WINDOW)
+ g_free (edge);
+ }
+ }
+
+ /* Now free the arrays and data */
+ g_array_free (edge_data->left_edges, TRUE);
+ g_array_free (edge_data->right_edges, TRUE);
+ g_array_free (edge_data->top_edges, TRUE);
+ g_array_free (edge_data->bottom_edges, TRUE);
+
+ /* Cleanup the timeouts */
+ if (edge_data->left_data.timeout_setup &&
+ edge_data->left_data.timeout_id != 0)
+ g_source_remove (edge_data->left_data.timeout_id);
+ if (edge_data->right_data.timeout_setup &&
+ edge_data->right_data.timeout_id != 0)
+ g_source_remove (edge_data->right_data.timeout_id);
+ if (edge_data->top_data.timeout_setup &&
+ edge_data->top_data.timeout_id != 0)
+ g_source_remove (edge_data->top_data.timeout_id);
+ if (edge_data->bottom_data.timeout_setup &&
+ edge_data->bottom_data.timeout_id != 0)
+ g_source_remove (edge_data->bottom_data.timeout_id);
+
+ g_free (display->grab_edge_resistance_data);
+ display->grab_edge_resistance_data = NULL;
+}
+
+static int
+stupid_sort_requiring_extra_pointer_dereference (gconstpointer a,
+ gconstpointer b)
+{
+ const MetaEdge * const *a_edge = a;
+ const MetaEdge * const *b_edge = b;
+ return meta_rectangle_edge_cmp (*a_edge, *b_edge);
+}
+
+static void
+cache_edges (MetaDisplay *display,
+ GList *window_edges,
+ GList *xinerama_edges,
+ GList *screen_edges)
+{
+ MetaEdgeResistanceData *edge_data;
+ GList *tmp;
+ int num_left, num_right, num_top, num_bottom;
+ int i;
+
+ /*
+ * 1st: Get the total number of each kind of edge
+ */
+ num_left = num_right = num_top = num_bottom = 0;
+ for (i = 0; i < 3; i++)
+ {
+ tmp = NULL;
+ switch (i)
+ {
+ case 0:
+ tmp = window_edges;
+ break;
+ case 1:
+ tmp = xinerama_edges;
+ break;
+ case 2:
+ tmp = screen_edges;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ while (tmp)
+ {
+ MetaEdge *edge = tmp->data;
+ switch (edge->side_type)
+ {
+ case META_DIRECTION_LEFT:
+ num_left++;
+ break;
+ case META_DIRECTION_RIGHT:
+ num_right++;
+ break;
+ case META_DIRECTION_TOP:
+ num_top++;
+ break;
+ case META_DIRECTION_BOTTOM:
+ num_bottom++;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ tmp = tmp->next;
+ }
+ }
+
+ /*
+ * 2nd: Allocate the edges
+ */
+ g_assert (display->grab_edge_resistance_data == NULL);
+ display->grab_edge_resistance_data = g_new (MetaEdgeResistanceData, 1);
+ edge_data = display->grab_edge_resistance_data;
+ edge_data->left_edges = g_array_sized_new (FALSE,
+ FALSE,
+ sizeof(MetaEdge*),
+ num_left);
+ edge_data->right_edges = g_array_sized_new (FALSE,
+ FALSE,
+ sizeof(MetaEdge*),
+ num_right);
+ edge_data->top_edges = g_array_sized_new (FALSE,
+ FALSE,
+ sizeof(MetaEdge*),
+ num_top);
+ edge_data->bottom_edges = g_array_sized_new (FALSE,
+ FALSE,
+ sizeof(MetaEdge*),
+ num_bottom);
+
+ /*
+ * 3rd: Add the edges to the arrays
+ */
+ num_left = num_right = num_top = num_bottom = 0;
+ for (i = 0; i < 3; i++)
+ {
+ tmp = NULL;
+ switch (i)
+ {
+ case 0:
+ tmp = window_edges;
+ break;
+ case 1:
+ tmp = xinerama_edges;
+ break;
+ case 2:
+ tmp = screen_edges;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ while (tmp)
+ {
+ MetaEdge *edge = tmp->data;
+ switch (edge->side_type)
+ {
+ case META_DIRECTION_LEFT:
+ g_array_append_val (edge_data->left_edges, edge);
+ break;
+ case META_DIRECTION_RIGHT:
+ g_array_append_val (edge_data->right_edges, edge);
+ break;
+ case META_DIRECTION_TOP:
+ g_array_append_val (edge_data->top_edges, edge);
+ break;
+ case META_DIRECTION_BOTTOM:
+ g_array_append_val (edge_data->bottom_edges, edge);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ tmp = tmp->next;
+ }
+ }
+
+ /*
+ * 4th: Sort the arrays (FIXME: This is kinda dumb since the arrays were
+ * individually sorted earlier and we could have done this faster and
+ * avoided this sort by sticking them into the array with some simple
+ * merging of the lists).
+ */
+ g_array_sort (display->grab_edge_resistance_data->left_edges,
+ stupid_sort_requiring_extra_pointer_dereference);
+ g_array_sort (display->grab_edge_resistance_data->right_edges,
+ stupid_sort_requiring_extra_pointer_dereference);
+ g_array_sort (display->grab_edge_resistance_data->top_edges,
+ stupid_sort_requiring_extra_pointer_dereference);
+ g_array_sort (display->grab_edge_resistance_data->bottom_edges,
+ stupid_sort_requiring_extra_pointer_dereference);
+}
+
+static void
+initialize_grab_edge_resistance_data (MetaDisplay *display)
+{
+ MetaEdgeResistanceData *edge_data = display->grab_edge_resistance_data;
+
+ edge_data->left_data.timeout_setup = FALSE;
+ edge_data->right_data.timeout_setup = FALSE;
+ edge_data->top_data.timeout_setup = FALSE;
+ edge_data->bottom_data.timeout_setup = FALSE;
+
+ edge_data->left_data.keyboard_buildup = 0;
+ edge_data->right_data.keyboard_buildup = 0;
+ edge_data->top_data.keyboard_buildup = 0;
+ edge_data->bottom_data.keyboard_buildup = 0;
+
+ edge_data->left_data.allow_past_screen_edge = TRUE;
+ edge_data->right_data.allow_past_screen_edge = TRUE;
+ edge_data->bottom_data.allow_past_screen_edge = TRUE;
+ edge_data->top_data.allow_past_screen_edge =
+ display->grab_anchor_root_y >= display->grab_initial_window_pos.y;
+}
+
+void
+meta_display_compute_resistance_and_snapping_edges (MetaDisplay *display)
+{
+ GList *stacked_windows;
+ GList *cur_window_iter;
+ GList *edges;
+ /* Lists of window positions (rects) and their relative stacking positions */
+ int stack_position;
+ GSList *obscuring_windows, *window_stacking;
+ /* The portions of the above lists that still remain at the stacking position
+ * in the layer that we are working on
+ */
+ GSList *rem_windows, *rem_win_stacking;
+
+ /*
+ * 1st: Get the list of relevant windows, from bottom to top
+ */
+ stacked_windows =
+ meta_stack_list_windows (display->grab_screen->stack,
+ display->grab_screen->active_workspace);
+
+ /*
+ * 2nd: we need to separate that stacked list into a list of windows that
+ * can obscure other edges. To make sure we only have windows obscuring
+ * those below it instead of going both ways, we also need to keep a
+ * counter list. Messy, I know.
+ */
+ obscuring_windows = window_stacking = NULL;
+ cur_window_iter = stacked_windows;
+ stack_position = 0;
+ while (cur_window_iter != NULL)
+ {
+ MetaWindow *cur_window = cur_window_iter->data;
+ if (WINDOW_EDGES_RELEVANT (cur_window, display))
+ {
+ MetaRectangle *new_rect;
+ new_rect = g_new (MetaRectangle, 1);
+ meta_window_get_outer_rect (cur_window, new_rect);
+ obscuring_windows = g_slist_prepend (obscuring_windows, new_rect);
+ window_stacking =
+ g_slist_prepend (window_stacking, GINT_TO_POINTER (stack_position));
+ }
+
+ stack_position++;
+ cur_window_iter = cur_window_iter->next;
+ }
+ /* Put 'em in bottom to top order */
+ rem_windows = g_slist_reverse (obscuring_windows);
+ rem_win_stacking = g_slist_reverse (window_stacking);
+
+ /*
+ * 3rd: loop over the windows again, this time getting the edges from
+ * them and removing intersections with the relevant obscuring_windows &
+ * obscuring_docks.
+ */
+ edges = NULL;
+ stack_position = 0;
+ cur_window_iter = stacked_windows;
+ while (cur_window_iter != NULL)
+ {
+ MetaRectangle cur_rect;
+ MetaWindow *cur_window = cur_window_iter->data;
+ meta_window_get_outer_rect (cur_window, &cur_rect);
+
+ /* Check if we want to use this window's edges for edge
+ * resistance (note that dock edges are considered screen edges
+ * which are handled separately
+ */
+ if (WINDOW_EDGES_RELEVANT (cur_window, display) &&
+ cur_window->type != META_WINDOW_DOCK)
+ {
+ GList *new_edges;
+ MetaEdge *new_edge;
+ MetaRectangle reduced;
+
+ /* We don't care about snapping to any portion of the window that
+ * is offscreen (we also don't care about parts of edges covered
+ * by other windows or DOCKS, but that's handled below).
+ */
+ meta_rectangle_intersect (&cur_rect,
+ &display->grab_screen->rect,
+ &reduced);
+
+ new_edges = NULL;
+
+ /* Left side of this window is resistance for the right edge of
+ * the window being moved.
+ */
+ new_edge = g_new (MetaEdge, 1);
+ new_edge->rect = reduced;
+ new_edge->rect.width = 0;
+ new_edge->side_type = META_DIRECTION_RIGHT;
+ new_edge->edge_type = META_EDGE_WINDOW;
+ new_edges = g_list_prepend (new_edges, new_edge);
+
+ /* Right side of this window is resistance for the left edge of
+ * the window being moved.
+ */
+ new_edge = g_new (MetaEdge, 1);
+ new_edge->rect = reduced;
+ new_edge->rect.x += new_edge->rect.width;
+ new_edge->rect.width = 0;
+ new_edge->side_type = META_DIRECTION_LEFT;
+ new_edge->edge_type = META_EDGE_WINDOW;
+ new_edges = g_list_prepend (new_edges, new_edge);
+
+ /* Top side of this window is resistance for the bottom edge of
+ * the window being moved.
+ */
+ new_edge = g_new (MetaEdge, 1);
+ new_edge->rect = reduced;
+ new_edge->rect.height = 0;
+ new_edge->side_type = META_DIRECTION_BOTTOM;
+ new_edge->edge_type = META_EDGE_WINDOW;
+ new_edges = g_list_prepend (new_edges, new_edge);
+
+ /* Top side of this window is resistance for the bottom edge of
+ * the window being moved.
+ */
+ new_edge = g_new (MetaEdge, 1);
+ new_edge->rect = reduced;
+ new_edge->rect.y += new_edge->rect.height;
+ new_edge->rect.height = 0;
+ new_edge->side_type = META_DIRECTION_TOP;
+ new_edge->edge_type = META_EDGE_WINDOW;
+ new_edges = g_list_prepend (new_edges, new_edge);
+
+ /* Update the remaining windows to only those at a higher
+ * stacking position than this one.
+ */
+ while (rem_win_stacking &&
+ stack_position >= (int)rem_win_stacking->data)
+ {
+ rem_windows = rem_windows->next;
+ rem_win_stacking = rem_win_stacking->next;
+ }
+
+ /* Remove edge portions overlapped by rem_windows and rem_docks */
+ new_edges =
+ meta_rectangle_remove_intersections_with_boxes_from_edges (
+ new_edges,
+ rem_windows);
+
+ /* Save the new edges */
+ edges = g_list_concat (new_edges, edges);
+ }
+
+ stack_position++;
+ cur_window_iter = cur_window_iter->next;
+ }
+
+ /*
+ * 4th: Free the extra memory not needed and sort the list
+ */
+ /* Free the memory used by the obscuring windows/docks lists */
+ g_slist_free (window_stacking);
+ /* FIXME: Shouldn't there be a helper function to make this one line of code
+ * to free a list instead of four ugly ones?
+ */
+ g_slist_foreach (obscuring_windows,
+ (void (*)(gpointer,gpointer))&g_free, /* ew, for ugly */
+ NULL);
+ g_slist_free (obscuring_windows);
+
+ /* Sort the list. FIXME: Should I bother with this sorting? I just
+ * sort again later in cache_edges() anyway...
+ */
+ edges = g_list_sort (edges, meta_rectangle_edge_cmp);
+
+ /*
+ * 5th: Cache the combination of these edges with the onscreen and
+ * xinerama edges in an array for quick access. Free the edges since
+ * they've been cached elsewhere.
+ */
+ cache_edges (display,
+ edges,
+ display->grab_screen->active_workspace->xinerama_edges,
+ display->grab_screen->active_workspace->screen_edges);
+ g_list_free (edges);
+
+ /*
+ * 6th: Initialize the resistance timeouts and buildups
+ */
+ initialize_grab_edge_resistance_data (display);
+}
+
+/* Note that old_[xy] and new_[xy] are with respect to inner positions of
+ * the window.
+ */
+void
+meta_window_edge_resistance_for_move (MetaWindow *window,
+ int old_x,
+ int old_y,
+ int *new_x,
+ int *new_y,
+ GSourceFunc timeout_func,
+ gboolean snap,
+ gboolean is_keyboard_op)
+{
+ MetaRectangle old_outer, proposed_outer, new_outer;
+
+ if (window == window->display->grab_window &&
+ window->display->grab_wireframe_active)
+ {
+ meta_window_get_xor_rect (window,
+ &window->display->grab_wireframe_rect,
+ &old_outer);
+ }
+ else
+ {
+ meta_window_get_outer_rect (window, &old_outer);
+ }
+ proposed_outer = old_outer;
+ proposed_outer.x += (*new_x - old_x);
+ proposed_outer.y += (*new_y - old_y);
+ new_outer = proposed_outer;
+
+ window->display->grab_last_user_action_was_snap = snap;
+ if (apply_edge_resistance_to_each_side (window->display,
+ window,
+ &old_outer,
+ &new_outer,
+ timeout_func,
+ snap,
+ is_keyboard_op))
+ {
+ /* apply_edge_resistance_to_each_side independently applies
+ * resistance to both the right and left edges of new_outer as both
+ * could meet areas of resistance. But we don't want a resize, so we
+ * just have both edges move according to the stricter of the
+ * resistances. Same thing goes for top & bottom edges.
+ */
+ MetaRectangle *reference;
+ int left_change, right_change, smaller_x_change;
+ int top_change, bottom_change, smaller_y_change;
+
+ if (snap && !is_keyboard_op)
+ reference = &proposed_outer;
+ else
+ reference = &old_outer;
+
+ left_change = BOX_LEFT (new_outer) - BOX_LEFT (*reference);
+ right_change = BOX_RIGHT (new_outer) - BOX_RIGHT (*reference);
+ if ( snap && is_keyboard_op && left_change == 0)
+ smaller_x_change = right_change;
+ else if (snap && is_keyboard_op && right_change == 0)
+ smaller_x_change = left_change;
+ else if (ABS (left_change) < ABS (right_change))
+ smaller_x_change = left_change;
+ else
+ smaller_x_change = right_change;
+
+ top_change = BOX_TOP (new_outer) - BOX_TOP (*reference);
+ bottom_change = BOX_BOTTOM (new_outer) - BOX_BOTTOM (*reference);
+ if ( snap && is_keyboard_op && top_change == 0)
+ smaller_y_change = bottom_change;
+ else if (snap && is_keyboard_op && bottom_change == 0)
+ smaller_y_change = top_change;
+ else if (ABS (top_change) < ABS (bottom_change))
+ smaller_y_change = top_change;
+ else
+ smaller_y_change = bottom_change;
+
+ *new_x = old_x + smaller_x_change +
+ (BOX_LEFT (*reference) - BOX_LEFT (old_outer));
+ *new_y = old_y + smaller_y_change +
+ (BOX_TOP (*reference) - BOX_TOP (old_outer));
+ }
+}
+
+/* Note that old_(width|height) and new_(width|height) are with respect to
+ * sizes of the inner window.
+ */
+void
+meta_window_edge_resistance_for_resize (MetaWindow *window,
+ int old_width,
+ int old_height,
+ int *new_width,
+ int *new_height,
+ int gravity,
+ GSourceFunc timeout_func,
+ gboolean snap,
+ gboolean is_keyboard_op)
+{
+ MetaRectangle old_outer, new_outer;
+ int new_outer_width, new_outer_height;
+
+ if (window == window->display->grab_window &&
+ window->display->grab_wireframe_active)
+ {
+ meta_window_get_xor_rect (window,
+ &window->display->grab_wireframe_rect,
+ &old_outer);
+ }
+ else
+ {
+ meta_window_get_outer_rect (window, &old_outer);
+ }
+ new_outer_width = old_outer.width + (*new_width - old_width);
+ new_outer_height = old_outer.height + (*new_height - old_height);
+ meta_rectangle_resize_with_gravity (&old_outer,
+ &new_outer,
+ gravity,
+ new_outer_width,
+ new_outer_height);
+
+ window->display->grab_last_user_action_was_snap = snap;
+ if (apply_edge_resistance_to_each_side (window->display,
+ window,
+ &old_outer,
+ &new_outer,
+ timeout_func,
+ snap,
+ is_keyboard_op))
+ {
+ *new_width = old_width + (new_outer.width - old_outer.width);
+ *new_height = old_height + (new_outer.height - old_outer.height);
+ }
+}
diff --git a/src/edge-resistance.h b/src/edge-resistance.h
new file mode 100644
index 0000000..f327c83
--- /dev/null
+++ b/src/edge-resistance.h
@@ -0,0 +1,46 @@
+/* Edge resistance for move/resize operations */
+
+/*
+ * Copyright (C) 2005 Elijah Newren
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef META_EDGE_RESISTANCE_H
+#define META_EDGE_RESISTANCE_H
+
+#include "window.h"
+
+void meta_window_edge_resistance_for_move (MetaWindow *window,
+ int old_x,
+ int old_y,
+ int *new_x,
+ int *new_y,
+ GSourceFunc timeout_func,
+ gboolean snap,
+ gboolean is_keyboard_op);
+void meta_window_edge_resistance_for_resize (MetaWindow *window,
+ int old_width,
+ int old_height,
+ int *new_width,
+ int *new_height,
+ int gravity,
+ GSourceFunc timeout_func,
+ gboolean snap,
+ gboolean is_keyboard_op);
+
+#endif /* META_EDGE_RESISTANCE_H */
+
diff --git a/src/frame.c b/src/frame.c
index 76e6296..445f89c 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -3,6 +3,7 @@
/*
* Copyright (C) 2001 Havoc Pennington
* Copyright (C) 2003, 2004 Red Hat, Inc.
+ * Copyright (C) 2005 Elijah Newren
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -264,7 +265,10 @@ meta_frame_get_flags (MetaFrame *frame)
if (frame->window->on_all_workspaces)
flags |= META_FRAME_STUCK;
- if (frame->window->maximized)
+ /* FIXME: Should we have some kind of UI for windows that are just vertically
+ * maximized or just horizontally maximized?
+ */
+ if (META_WINDOW_MAXIMIZED (frame->window))
flags |= META_FRAME_MAXIMIZED;
if (frame->window->fullscreen)
diff --git a/src/frames.c b/src/frames.c
index 03e11d3..c5f1b9c 100644
--- a/src/frames.c
+++ b/src/frames.c
@@ -3,6 +3,7 @@
/*
* Copyright (C) 2001 Havoc Pennington
* Copyright (C) 2003 Red Hat, Inc.
+ * Copyright (C) 2005 Elijah Newren
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -1441,16 +1442,25 @@ meta_frames_button_press_event (GtkWidget *widget,
break;
}
- meta_core_begin_grab_op (gdk_display,
- frame->xwindow,
- op,
- TRUE,
- meta_ui_get_last_event_serial (gdk_display),
- event->button,
- 0,
- event->time,
- event->x_root,
- event->y_root);
+ if (!meta_core_titlebar_is_onscreen (gdk_display,
+ frame->xwindow))
+ meta_core_show_window_menu (gdk_display,
+ frame->xwindow,
+ event->x_root,
+ event->y_root,
+ event->button,
+ event->time);
+ else
+ meta_core_begin_grab_op (gdk_display,
+ frame->xwindow,
+ op,
+ TRUE,
+ meta_ui_get_last_event_serial (gdk_display),
+ event->button,
+ 0,
+ event->time,
+ event->x_root,
+ event->y_root);
}
else if (control == META_FRAME_CONTROL_TITLE &&
event->button == 1)
diff --git a/src/keybindings.c b/src/keybindings.c
index be448c2..e1872a0 100644
--- a/src/keybindings.c
+++ b/src/keybindings.c
@@ -26,6 +26,7 @@
#include "keybindings.h"
#include "workspace.h"
#include "errors.h"
+#include "edge-resistance.h"
#include "ui.h"
#include "frame.h"
#include "place.h"
@@ -1690,8 +1691,6 @@ process_keyboard_move_grab (MetaDisplay *display,
int x, y;
int incr;
gboolean smart_snap;
- int edge;
- int candidate_position;
handled = FALSE;
@@ -1719,7 +1718,7 @@ process_keyboard_move_grab (MetaDisplay *display,
#define NORMAL_INCREMENT 10
if (smart_snap)
- incr = 0;
+ incr = 1;
else if (event->xkey.state & ControlMask)
incr = SMALL_INCREMENT;
else
@@ -1754,18 +1753,6 @@ process_keyboard_move_grab (MetaDisplay *display,
case XK_Up:
case XK_KP_Up:
y -= incr;
-
- edge = meta_window_find_next_horizontal_edge (window,
- META_WINDOW_EDGE_TOP,
- FALSE);
- if (window->frame)
- candidate_position = edge + window->frame->child_y;
- else
- candidate_position = edge;
-
- if (smart_snap || ((candidate_position > y) && ABS (candidate_position - y) < incr))
- y = candidate_position;
-
handled = TRUE;
break;
case XK_KP_End:
@@ -1773,18 +1760,6 @@ process_keyboard_move_grab (MetaDisplay *display,
case XK_Down:
case XK_KP_Down:
y += incr;
-
- edge = meta_window_find_next_horizontal_edge (window,
- META_WINDOW_EDGE_BOTTOM,
- TRUE);
- if (window->frame)
- candidate_position = edge - window->frame->bottom_height - window->rect.height;
- else
- candidate_position = edge - window->rect.height;
-
- if (smart_snap || ((candidate_position < y) && ABS (candidate_position - y) < incr))
- y = candidate_position;
-
handled = TRUE;
break;
}
@@ -1796,19 +1771,6 @@ process_keyboard_move_grab (MetaDisplay *display,
case XK_Left:
case XK_KP_Left:
x -= incr;
-
- edge = meta_window_find_next_vertical_edge (window,
- META_WINDOW_EDGE_LEFT,
- FALSE);
- if (window->frame)
- candidate_position = edge + window->frame->child_x;
- else
- candidate_position = edge;
-
- if (smart_snap ||
- ((candidate_position > x) && ABS (candidate_position - x) < incr))
- x = candidate_position;
-
handled = TRUE;
break;
case XK_KP_Prior:
@@ -1816,27 +1778,34 @@ process_keyboard_move_grab (MetaDisplay *display,
case XK_Right:
case XK_KP_Right:
x += incr;
-
- edge = meta_window_find_next_vertical_edge (window,
- META_WINDOW_EDGE_RIGHT,
- TRUE);
- if (window->frame)
- candidate_position = edge - window->frame->right_width - window->rect.width;
- else
- candidate_position = edge - window->rect.width;
-
- if (smart_snap || ((candidate_position < x) && ABS (candidate_position - x) < incr))
- x = candidate_position;
-
handled = TRUE;
break;
}
if (handled)
{
+ MetaRectangle old_rect;
meta_topic (META_DEBUG_KEYBINDINGS,
"Computed new window location %d,%d due to keypress\n",
x, y);
+
+ if (display->grab_wireframe_active)
+ old_rect = display->grab_wireframe_rect;
+ else
+ {
+ old_rect = window->rect;
+ meta_window_get_position (window, &old_rect.x, &old_rect.y);
+ }
+
+ meta_window_edge_resistance_for_move (window,
+ old_rect.x,
+ old_rect.y,
+ &x,
+ &y,
+ NULL,
+ smart_snap,
+ TRUE);
+
if (display->grab_wireframe_active)
{
meta_window_update_wireframe (window, x, y,
@@ -1986,13 +1955,9 @@ process_keyboard_resize_grab (MetaDisplay *display,
gboolean handled;
int height_inc;
int width_inc;
- int x, y;
- int orig_x, orig_y;
int width, height;
gboolean smart_snap;
- int edge;
int gravity;
- int candidate_position;
handled = FALSE;
@@ -2029,21 +1994,15 @@ process_keyboard_resize_grab (MetaDisplay *display,
if (display->grab_wireframe_active)
{
- orig_x = display->grab_wireframe_rect.x;
- orig_y = display->grab_wireframe_rect.y;
width = display->grab_wireframe_rect.width;
height = display->grab_wireframe_rect.height;
}
else
{
- meta_window_get_position (window, &orig_x, &orig_y);
width = window->rect.width;
height = window->rect.height;
}
- x = orig_x;
- y = orig_y;
-
gravity = meta_resize_gravity_from_grab_op (display->grab_op);
smart_snap = (event->xkey.state & ShiftMask) != 0;
@@ -2053,40 +2012,28 @@ process_keyboard_resize_grab (MetaDisplay *display,
if (smart_snap)
{
- height_inc = 0;
- width_inc = 0;
+ height_inc = 1;
+ width_inc = 1;
}
else if (event->xkey.state & ControlMask)
{
- if (window->size_hints.width_inc > 1)
- width_inc = window->size_hints.width_inc;
- else
- width_inc = SMALL_INCREMENT;
-
- if (window->size_hints.height_inc > 1)
- height_inc = window->size_hints.height_inc;
- else
- height_inc = SMALL_INCREMENT;
+ width_inc = SMALL_INCREMENT;
+ height_inc = SMALL_INCREMENT;
}
else
{
- if (window->size_hints.width_inc > 1)
- width_inc = window->size_hints.width_inc;
- else
- width_inc = NORMAL_INCREMENT;
-
- if (window->size_hints.height_inc > 1)
- height_inc = window->size_hints.height_inc;
- else
- height_inc = NORMAL_INCREMENT;
+ width_inc = NORMAL_INCREMENT;
+ height_inc = NORMAL_INCREMENT;
}
-
- /* 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
- * people using just arrows shouldn't get too frustrated.
+
+ /* If this is a resize increment window, make the amount we resize
+ * the window by match that amount (well, unless snap resizing...)
*/
-
+ if (window->size_hints.width_inc > 1)
+ width_inc = window->size_hints.width_inc;
+ if (window->size_hints.height_inc > 1)
+ height_inc = window->size_hints.height_inc;
+
switch (keysym)
{
case XK_Up:
@@ -2097,49 +2044,14 @@ process_keyboard_resize_grab (MetaDisplay *display,
case NorthWestGravity:
case NorthEastGravity:
/* Move bottom edge up */
- edge = meta_window_find_next_horizontal_edge (window,
- META_WINDOW_EDGE_BOTTOM,
- FALSE);
-
- if (window->frame)
- candidate_position = edge - window->frame->bottom_height;
- else
- candidate_position = edge;
-
- if (smart_snap ||
- ((candidate_position > (y + (height - height_inc))) &&
- ABS (candidate_position - (y + (height - height_inc))) < height_inc))
- {
- if (candidate_position - y > 0)
- height = candidate_position - y;
- }
- else if (height - height_inc > 0)
- {
- height -= height_inc;
- }
-
- handled = TRUE;
+ height -= height_inc;
break;
case SouthGravity:
case SouthWestGravity:
case SouthEastGravity:
/* Move top edge up */
- y -= height_inc;
-
- edge = meta_window_find_next_horizontal_edge (window,
- META_WINDOW_EDGE_TOP,
- FALSE);
-
- if (window->frame)
- candidate_position = edge + window->frame->child_y;
- else
- candidate_position = edge;
-
- if (smart_snap || ((candidate_position > y) && ABS (candidate_position - y) < height_inc))
- y = candidate_position;
-
- height += (orig_y - y);
+ height += height_inc;
break;
case EastGravity:
@@ -2161,49 +2073,13 @@ process_keyboard_resize_grab (MetaDisplay *display,
case NorthEastGravity:
/* Move bottom edge down */
height += height_inc;
-
- edge = meta_window_find_next_horizontal_edge (window,
- META_WINDOW_EDGE_BOTTOM,
- TRUE);
-
- if (window->frame)
- candidate_position = edge - window->frame->bottom_height;
- else
- candidate_position = edge;
-
- if (smart_snap || ((candidate_position < (y+height)) &&
- ABS (candidate_position - (y+height)) < height_inc))
- height = candidate_position - y;
break;
case SouthGravity:
case SouthWestGravity:
case SouthEastGravity:
/* Move top edge down */
- edge = meta_window_find_next_horizontal_edge (window,
- META_WINDOW_EDGE_TOP,
- TRUE);
-
- if (window->frame)
- candidate_position = edge + window->frame->child_y;
- else
- candidate_position = edge;
-
- if (smart_snap ||
- ((candidate_position < (y + height_inc)) &&
- ABS (candidate_position - (y + height_inc)) < height_inc))
- {
- if (height - (candidate_position - orig_y) > 0)
- {
- y = candidate_position;
- height -= (y - orig_y);
- }
- }
- else if (height - ((y + height_inc) - orig_y) > 0)
- {
- y += height_inc;
- height -= (y - orig_y);
- }
+ height -= height_inc;
break;
case EastGravity:
@@ -2223,50 +2099,15 @@ process_keyboard_resize_grab (MetaDisplay *display,
case EastGravity:
case SouthEastGravity:
case NorthEastGravity:
- x -= width_inc;
-
/* Move left edge left */
- edge = meta_window_find_next_vertical_edge (window,
- META_WINDOW_EDGE_LEFT,
- FALSE);
-
- if (window->frame)
- candidate_position = edge + window->frame->child_x;
- else
- candidate_position = edge;
-
- if (smart_snap || ((candidate_position > x) && ABS (candidate_position - x) < width_inc))
- x = candidate_position;
-
- width += (orig_x - x);
+ width += width_inc;
break;
case WestGravity:
case SouthWestGravity:
case NorthWestGravity:
/* Move right edge left */
- edge = meta_window_find_next_vertical_edge (window,
- META_WINDOW_EDGE_RIGHT,
- FALSE);
-
- if (window->frame)
- candidate_position = edge - window->frame->right_width;
- else
- candidate_position = edge;
-
- if (smart_snap ||
- ((candidate_position > (x + (width - width_inc))) &&
- ABS (candidate_position - (x + (width - width_inc))) < width_inc))
- {
- if (candidate_position - x > 0)
- width = candidate_position - x;
- }
- else if (width - width_inc > 0)
- {
- width -= width_inc;
- }
-
- handled = TRUE;
+ width -= width_inc;
break;
case NorthGravity:
@@ -2287,30 +2128,7 @@ process_keyboard_resize_grab (MetaDisplay *display,
case SouthEastGravity:
case NorthEastGravity:
/* Move left edge right */
- edge = meta_window_find_next_vertical_edge (window,
- META_WINDOW_EDGE_LEFT,
- TRUE);
-
- if (window->frame)
- candidate_position = edge + window->frame->child_x;
- else
- candidate_position = edge;
-
- if (smart_snap ||
- ((candidate_position < (x + width_inc)) &&
- ABS (candidate_position - (x + width_inc)) < width_inc))
- {
- if (width - (candidate_position - orig_x) > 0)
- {
- x = candidate_position;
- width -= (x - orig_x);
- }
- }
- else if (width - ((x + width_inc) - orig_x) > 0)
- {
- x += width_inc;
- width -= (x - orig_x);
- }
+ width -= width_inc;
break;
case WestGravity:
@@ -2318,21 +2136,6 @@ process_keyboard_resize_grab (MetaDisplay *display,
case NorthWestGravity:
/* Move right edge right */
width += width_inc;
-
- edge = meta_window_find_next_vertical_edge (window,
- META_WINDOW_EDGE_RIGHT,
- TRUE);
-
- if (window->frame)
- candidate_position = edge - window->frame->right_width;
- else
- candidate_position = edge;
-
- if (smart_snap || ((candidate_position > (x+width)) &&
- ABS (candidate_position - (x+width)) < width_inc))
- width = candidate_position - x;
-
- handled = TRUE;
break;
case NorthGravity:
@@ -2357,17 +2160,53 @@ process_keyboard_resize_grab (MetaDisplay *display,
if (handled)
{
+ MetaRectangle old_rect;
meta_topic (META_DEBUG_KEYBINDINGS,
- "Computed new window location %d,%d %dx%d due to keypress\n",
- x, y, width, height);
+ "Computed new window size due to keypress: "
+ "%dx%d, gravity %s\n",
+ width, height, meta_gravity_to_string (gravity));
if (display->grab_wireframe_active)
+ old_rect = display->grab_wireframe_rect;
+ else
+ old_rect = window->rect; /* Don't actually care about x,y */
+
+ /* Do any edge resistance/snapping */
+ meta_window_edge_resistance_for_resize (window,
+ old_rect.width,
+ old_rect.height,
+ &width,
+ &height,
+ gravity,
+ NULL,
+ smart_snap,
+ TRUE);
+
+ if (display->grab_wireframe_active)
{
- meta_window_update_wireframe (window, x, y, width, height);
+ MetaRectangle new_position;
+ meta_rectangle_resize_with_gravity (&display->grab_wireframe_rect,
+ &new_position,
+ gravity,
+ width,
+ height);
+ meta_window_update_wireframe (window,
+ new_position.x,
+ new_position.y,
+ new_position.width,
+ new_position.height);
}
else
{
- meta_window_move_resize (window, TRUE, x, y, width, height);
+ /* We don't need to update unless the specified width and height
+ * are actually different from what we had before.
+ */
+ if (window->rect.width != width || window->rect.height != height)
+ meta_window_resize_with_gravity (window,
+ TRUE,
+ width,
+ height,
+ gravity);
}
meta_window_update_keyboard_resize (window, FALSE);
}
@@ -2782,10 +2621,12 @@ handle_maximize_vert (MetaDisplay *display,
XEvent *event,
MetaKeyBinding *binding)
{
- if (window)
+ if (window && window->has_resize_func)
{
- if (window->has_resize_func)
- meta_window_fill_vertical (window);
+ if (window->maximized_vertically)
+ meta_window_unmaximize (window, META_MAXIMIZE_VERTICAL);
+ else
+ meta_window_maximize (window, META_MAXIMIZE_VERTICAL);
}
}
@@ -2796,10 +2637,12 @@ handle_maximize_horiz (MetaDisplay *display,
XEvent *event,
MetaKeyBinding *binding)
{
- if (window)
+ if (window && window->has_resize_func)
{
- if (window->has_resize_func)
- meta_window_fill_horizontal (window);
+ if (window->maximized_horizontally)
+ meta_window_unmaximize (window, META_MAXIMIZE_HORIZONTAL);
+ else
+ meta_window_maximize (window, META_MAXIMIZE_HORIZONTAL);
}
}
@@ -3230,10 +3073,14 @@ handle_toggle_maximize (MetaDisplay *display,
{
if (window)
{
- if (window->maximized)
- meta_window_unmaximize (window);
+ if (META_WINDOW_MAXIMIZED (window))
+ meta_window_unmaximize (window,
+ META_MAXIMIZE_HORIZONTAL |
+ META_MAXIMIZE_VERTICAL);
else if (window->has_maximize_func)
- meta_window_maximize (window);
+ meta_window_maximize (window,
+ META_MAXIMIZE_HORIZONTAL |
+ META_MAXIMIZE_VERTICAL);
}
}
@@ -3247,7 +3094,9 @@ handle_maximize (MetaDisplay *display,
if (window)
{
if (window->has_maximize_func)
- meta_window_maximize (window);
+ meta_window_maximize (window,
+ META_MAXIMIZE_HORIZONTAL |
+ META_MAXIMIZE_VERTICAL);
}
}
@@ -3260,8 +3109,10 @@ handle_unmaximize (MetaDisplay *display,
{
if (window)
{
- if (window->maximized)
- meta_window_unmaximize (window);
+ if (window->maximized_vertically || window->maximized_horizontally)
+ meta_window_unmaximize (window,
+ META_MAXIMIZE_HORIZONTAL |
+ META_MAXIMIZE_VERTICAL);
}
}
diff --git a/src/menu.c b/src/menu.c
index 2d70314..65f3750 100644
--- a/src/menu.c
+++ b/src/menu.c
@@ -3,6 +3,7 @@
/*
* Copyright (C) 2001 Havoc Pennington
* Copyright (C) 2004 Rob Adams
+ * Copyright (C) 2005 Elijah Newren
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -60,6 +61,7 @@ static MenuItem menuitems[] = {
{ META_MENU_OP_UNABOVE, NULL, TRUE, N_("On _Top") },
{ META_MENU_OP_MOVE, NULL, FALSE, N_("_Move") },
{ META_MENU_OP_RESIZE, NULL, FALSE, N_("_Resize") },
+ { META_MENU_OP_RECOVER, NULL, FALSE, N_("Move Titlebar On_screen") },
{ 0, NULL, FALSE, NULL }, /* separator */
{ META_MENU_OP_DELETE, METACITY_STOCK_DELETE, FALSE, N_("_Close") },
{ META_MENU_OP_WORKSPACES, NULL, FALSE, NULL }, /* separator */
diff --git a/src/place.c b/src/place.c
index 05426f5..befcbe5 100644
--- a/src/place.c
+++ b/src/place.c
@@ -529,16 +529,6 @@ center_tile_rect_in_area (MetaRectangle *rect,
rect->y = work_area->y + fluff;
}
-static gboolean
-rect_fits_in_work_area (MetaRectangle *work_area,
- MetaRectangle *rect)
-{
- return ((rect->x >= work_area->x) &&
- (rect->y >= work_area->y) &&
- (rect->x + rect->width <= work_area->x + work_area->width) &&
- (rect->y + rect->height <= work_area->y + work_area->height));
-}
-
/* Find the leftmost, then topmost, empty area on the workspace
* that can contain the new window.
*
@@ -597,13 +587,15 @@ find_first_fit (MetaWindow *window,
for (i = 0; i < n_xineramas; i++)
{
+#ifdef WITH_VERBOSE_MODE
+ char xinerama_location_string[RECT_LENGTH];
+ meta_rectangle_to_string (&window->screen->xinerama_infos[xineramas_list[i]].rect,
+ xinerama_location_string);
meta_topic (META_DEBUG_XINERAMA,
- "Natural xinerama %d is %d,%d %dx%d\n",
+ "Natural xinerama %d is %s\n",
i,
- window->screen->xinerama_infos[xineramas_list[i]].x_origin,
- window->screen->xinerama_infos[xineramas_list[i]].y_origin,
- window->screen->xinerama_infos[xineramas_list[i]].width,
- window->screen->xinerama_infos[xineramas_list[i]].height);
+ xinerama_location_string);
+#endif
}
/* try each xinerama in the natural ordering in turn */
@@ -614,7 +606,7 @@ find_first_fit (MetaWindow *window,
center_tile_rect_in_area (&rect, &work_area);
- if (rect_fits_in_work_area (&work_area, &rect) &&
+ if (meta_rectangle_contains_rect (&work_area, &rect) &&
!rectangle_overlaps_some_window (&rect, windows))
{
*new_x = rect.x;
@@ -642,7 +634,7 @@ find_first_fit (MetaWindow *window,
rect.x = outer_rect.x;
rect.y = outer_rect.y + outer_rect.height;
- if (rect_fits_in_work_area (&work_area, &rect) &&
+ if (meta_rectangle_contains_rect (&work_area, &rect) &&
!rectangle_overlaps_some_window (&rect, below_sorted))
{
*new_x = rect.x;
@@ -673,7 +665,7 @@ find_first_fit (MetaWindow *window,
rect.x = outer_rect.x + outer_rect.width;
rect.y = outer_rect.y;
- if (rect_fits_in_work_area (&work_area, &rect) &&
+ if (meta_rectangle_contains_rect (&work_area, &rect) &&
!rectangle_overlaps_some_window (&rect, right_sorted))
{
*new_x = rect.x;
@@ -817,7 +809,6 @@ meta_window_place (MetaWindow *window,
if (parent)
{
int w;
- MetaRectangle area;
meta_window_get_position (parent, &x, &y);
w = parent->rect.width;
@@ -836,6 +827,16 @@ meta_window_place (MetaWindow *window,
if (fgeom)
y += fgeom->top_height;
+#if 0
+ /* FIXME: If no one has complained about this within a couple
+ * months, it means that the constraints.c rewrite fixed this
+ * correctly and this hack is no longer necessary. Feel free to
+ * submit a patch removing this #ifdef'd out code. (Comment
+ * written on 2005-10-28).
+ */
+
+ MetaRectangle area;
+
/* clip to xinerama of parent*/
meta_window_get_work_area_current_xinerama (parent, &area);
@@ -845,6 +846,7 @@ meta_window_place (MetaWindow *window,
y = area.y + area.height - window->rect.height;
if (x < area.x) x = area.x;
if (y < area.y) y = area.y;
+#endif
meta_topic (META_DEBUG_PLACEMENT, "Centered window %s over transient parent\n",
window->desc);
@@ -867,20 +869,20 @@ meta_window_place (MetaWindow *window,
window->type == META_WINDOW_MODAL_DIALOG ||
window->type == META_WINDOW_SPLASHSCREEN)
{
- /* Center on screen */
+ /* Center on current xinerama (i.e. on current monitor) */
int w, h;
/* Warning, this function is a round trip! */
xi = meta_screen_get_current_xinerama (window->screen);
- w = xi->width;
- h = xi->height;
+ w = xi->rect.width;
+ h = xi->rect.height;
x = (w - window->rect.width) / 2;
y = (h - window->rect.height) / 2;
- x += xi->x_origin;
- y += xi->y_origin;
+ x += xi->rect.x;
+ y += xi->rect.y;
meta_topic (META_DEBUG_PLACEMENT, "Centered window %s on screen %d xinerama %d\n",
window->desc, window->screen->number, xi->number);
@@ -919,8 +921,8 @@ meta_window_place (MetaWindow *window,
xi = meta_screen_get_current_xinerama (window->screen);
/* "Origin" placement algorithm */
- x = xi->x_origin;
- y = xi->y_origin;
+ x = xi->rect.x;
+ y = xi->rect.y;
if (find_first_fit (window, fgeom, windows,
xineramas_list, n_xineramas,
@@ -986,11 +988,11 @@ meta_window_place (MetaWindow *window,
&workarea);
meta_window_get_outer_rect (window, &outer);
- if (outer.width >= workarea.width &&
- outer.height >= workarea.height)
- {
- window->maximize_after_placement = TRUE;
- }
+ if (outer.width >= workarea.width)
+ window->maximize_horizontally_after_placement = TRUE;
+
+ if (outer.height >= workarea.height)
+ window->maximize_vertically_after_placement = TRUE;
}
done_check_denied_focus:
@@ -1022,8 +1024,8 @@ meta_window_place (MetaWindow *window,
focus_window_list = g_list_prepend (NULL, focus_window);
/* Reset x and y ("origin" placement algorithm) */
- x = xi->x_origin;
- y = xi->y_origin;
+ x = xi->rect.x;
+ y = xi->rect.y;
found_fit = find_first_fit (window, fgeom, focus_window_list,
xineramas_list, n_xineramas,
@@ -1094,33 +1096,6 @@ get_windows_showing_on_same_screen (MetaWindow *window,
return windows;
}
-static gboolean
-rects_overlap_vertically (const MetaRectangle *a,
- const MetaRectangle *b)
-{
- /* if they don't overlap, then either a is above b
- * or b is above a
- */
- if ((a->y + a->height) < b->y)
- return FALSE;
- else if ((b->y + b->height) < a->y)
- return FALSE;
- else
- return TRUE;
-}
-
-static gboolean
-rects_overlap_horizontally (const MetaRectangle *a,
- const MetaRectangle *b)
-{
- if ((a->x + a->width) < b->x)
- return FALSE;
- else if ((b->x + b->width) < a->x)
- return FALSE;
- else
- return TRUE;
-}
-
static void
get_vertical_edges (MetaWindow *window,
int **edges_p,
@@ -1149,13 +1124,13 @@ get_vertical_edges (MetaWindow *window,
g_array_append_val (edges, edge);
edge = 0;
g_array_append_val (edges, edge);
- g_array_append_val (edges, window->screen->width);
+ g_array_append_val (edges, window->screen->rect.width);
/* Now get the xinerama screen edges */
for (i = 0; i < window->screen->n_xinerama_infos - 1; i++)
{
- edge = window->screen->xinerama_infos[i].x_origin +
- window->screen->xinerama_infos[i].width;
+ edge = window->screen->xinerama_infos[i].rect.x +
+ window->screen->xinerama_infos[i].rect.width;
g_array_append_val (edges, edge);
}
@@ -1185,7 +1160,7 @@ get_vertical_edges (MetaWindow *window,
meta_window_get_outer_rect (w, &w_rect);
- if (rects_overlap_vertically (&rect, &w_rect))
+ if (meta_rectangle_vert_overlap (&rect, &w_rect))
{
g_array_append_val (edges, w_rect.x);
edge = w_rect.x + w_rect.width;
@@ -1231,13 +1206,13 @@ get_horizontal_edges (MetaWindow *window,
g_array_append_val (edges, edge);
edge = 0;
g_array_append_val (edges, edge);
- g_array_append_val (edges, window->screen->height);
+ g_array_append_val (edges, window->screen->rect.height);
/* Now get the xinerama screen edges */
for (i = 0; i < window->screen->n_xinerama_infos - 1; i++)
{
- edge = window->screen->xinerama_infos[i].y_origin +
- window->screen->xinerama_infos[i].height;
+ edge = window->screen->xinerama_infos[i].rect.y +
+ window->screen->xinerama_infos[i].rect.height;
g_array_append_val (edges, edge);
}
@@ -1266,7 +1241,7 @@ get_horizontal_edges (MetaWindow *window,
meta_window_get_outer_rect (w, &w_rect);
- if (rects_overlap_horizontally (&rect, &w_rect))
+ if (meta_rectangle_horiz_overlap (&rect, &w_rect))
{
g_array_append_val (edges, w_rect.y);
edge = w_rect.y + w_rect.height;
diff --git a/src/screen.c b/src/screen.c
index c59df37..62a0f87 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -6,7 +6,7 @@
* Some ICCCM manager selection code derived from fvwm2,
* Copyright (C) 2001 Dominik Vogt, Matthias Clasen, and fvwm2 team
* Copyright (C) 2003 Rob Adams
- * Copyright (C) 2004 Elijah Newren
+ * Copyright (C) 2004, 2005 Elijah Newren
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -221,18 +221,18 @@ reload_xinerama_infos (MetaScreen *screen)
while (i < n_infos)
{
screen->xinerama_infos[i].number = infos[i].screen_number;
- screen->xinerama_infos[i].x_origin = infos[i].x_org;
- screen->xinerama_infos[i].y_origin = infos[i].y_org;
- screen->xinerama_infos[i].width = infos[i].width;
- screen->xinerama_infos[i].height = infos[i].height;
+ screen->xinerama_infos[i].rect.x = infos[i].x_org;
+ screen->xinerama_infos[i].rect.y = infos[i].y_org;
+ screen->xinerama_infos[i].rect.width = infos[i].width;
+ screen->xinerama_infos[i].rect.height = infos[i].height;
meta_topic (META_DEBUG_XINERAMA,
"Xinerama %d is %d,%d %d x %d\n",
screen->xinerama_infos[i].number,
- screen->xinerama_infos[i].x_origin,
- screen->xinerama_infos[i].y_origin,
- screen->xinerama_infos[i].width,
- screen->xinerama_infos[i].height);
+ screen->xinerama_infos[i].rect.x,
+ screen->xinerama_infos[i].rect.y,
+ screen->xinerama_infos[i].rect.width,
+ screen->xinerama_infos[i].rect.height);
++i;
}
@@ -282,18 +282,18 @@ reload_xinerama_infos (MetaScreen *screen)
while (i < n_monitors)
{
screen->xinerama_infos[i].number = i;
- screen->xinerama_infos[i].x_origin = monitors[i].x;
- screen->xinerama_infos[i].y_origin = monitors[i].y;
- screen->xinerama_infos[i].width = monitors[i].width;
- screen->xinerama_infos[i].height = monitors[i].height;
+ screen->xinerama_infos[i].rect.x = monitors[i].x;
+ screen->xinerama_infos[i].rect.y = monitors[i].y;
+ screen->xinerama_infos[i].rect.width = monitors[i].width;
+ screen->xinerama_infos[i].rect.height = monitors[i].height;
meta_topic (META_DEBUG_XINERAMA,
"Xinerama %d is %d,%d %d x %d\n",
screen->xinerama_infos[i].number,
- screen->xinerama_infos[i].x_origin,
- screen->xinerama_infos[i].y_origin,
- screen->xinerama_infos[i].width,
- screen->xinerama_infos[i].height);
+ screen->xinerama_infos[i].rect.x,
+ screen->xinerama_infos[i].rect.y,
+ screen->xinerama_infos[i].rect.width,
+ screen->xinerama_infos[i].rect.height);
++i;
}
@@ -325,16 +325,13 @@ reload_xinerama_infos (MetaScreen *screen)
screen->n_xinerama_infos = 2;
screen->xinerama_infos[0].number = 0;
- screen->xinerama_infos[0].x_origin = 0;
- screen->xinerama_infos[0].y_origin = 0;
- screen->xinerama_infos[0].width = screen->width / 2;
- screen->xinerama_infos[0].height = screen->height;
+ screen->xinerama_infos[0].rect = screen->rect;
+ screen->xinerama_infos[0].rect.width = screen->rect.width / 2;
screen->xinerama_infos[1].number = 1;
- screen->xinerama_infos[1].x_origin = screen->width / 2;
- screen->xinerama_infos[1].y_origin = 0;
- screen->xinerama_infos[1].width = screen->width / 2 + screen->width % 2;
- screen->xinerama_infos[1].height = screen->height;
+ screen->xinerama_infos[1].rect = screen->rect;
+ screen->xinerama_infos[1].rect.x = screen->rect.width / 2;
+ screen->xinerama_infos[1].rect.width = screen->rect.width / 2;
}
else
{
@@ -345,10 +342,7 @@ reload_xinerama_infos (MetaScreen *screen)
screen->n_xinerama_infos = 1;
screen->xinerama_infos[0].number = 0;
- screen->xinerama_infos[0].x_origin = 0;
- screen->xinerama_infos[0].y_origin = 0;
- screen->xinerama_infos[0].width = screen->width;
- screen->xinerama_infos[0].height = screen->height;
+ screen->xinerama_infos[0].rect = screen->rect;
}
}
@@ -520,8 +514,9 @@ meta_screen_new (MetaDisplay *display,
screen->screen_name = get_screen_name (display, number);
screen->xscreen = ScreenOfDisplay (xdisplay, number);
screen->xroot = xroot;
- screen->width = WidthOfScreen (screen->xscreen);
- screen->height = HeightOfScreen (screen->xscreen);
+ screen->rect.x = screen->rect.y = 0;
+ screen->rect.width = WidthOfScreen (screen->xscreen);
+ screen->rect.height = HeightOfScreen (screen->xscreen);
screen->current_cursor = -1; /* invalid/unset */
screen->default_xvisual = DefaultVisualOfScreen (screen->xscreen);
screen->default_depth = DefaultDepthOfScreen (screen->xscreen);
@@ -1015,8 +1010,8 @@ set_desktop_geometry_hint (MetaScreen *screen)
if (screen->closing > 0)
return;
- data[0] = screen->width;
- data[1] = screen->height;
+ data[0] = screen->rect.width;
+ data[1] = screen->rect.height;
meta_verbose ("Setting _NET_DESKTOP_GEOMETRY to %ld, %ld\n", data[0], data[1]);
@@ -1212,10 +1207,7 @@ meta_screen_ensure_tab_popup (MetaScreen *screen,
if (!window->minimized || !meta_window_get_icon_geometry (window, &r))
meta_window_get_outer_rect (window, &r);
- entries[i].x = r.x;
- entries[i].y = r.y;
- entries[i].width = r.width;
- entries[i].height = r.height;
+ entries[i].rect = r;
/* Find inside of highlight rectangle to be used
* when window is outlined for tabbing.
@@ -1229,19 +1221,19 @@ meta_screen_ensure_tab_popup (MetaScreen *screen,
int south = window->frame->rect.height - window->frame->child_y -
window->rect.height;
int east = window->frame->child_x;
- entries[i].inner_x = east;
- entries[i].inner_y = south;
- entries[i].inner_width = window->rect.width;
- entries[i].inner_height = window->frame->rect.height - south * 2;
+ entries[i].inner_rect.x = east;
+ entries[i].inner_rect.y = south;
+ entries[i].inner_rect.width = window->rect.width;
+ entries[i].inner_rect.height = window->frame->rect.height - south * 2;
}
else
{
/* Use an arbitrary border size */
#define OUTLINE_WIDTH 5
- entries[i].inner_x = OUTLINE_WIDTH;
- entries[i].inner_y = OUTLINE_WIDTH;
- entries[i].inner_width = window->rect.width - OUTLINE_WIDTH * 2;
- entries[i].inner_height = window->rect.height - OUTLINE_WIDTH * 2;
+ entries[i].inner_rect.x = OUTLINE_WIDTH;
+ entries[i].inner_rect.y = OUTLINE_WIDTH;
+ entries[i].inner_rect.width = window->rect.width - OUTLINE_WIDTH * 2;
+ entries[i].inner_rect.height = window->rect.height - OUTLINE_WIDTH * 2;
}
++i;
@@ -1376,26 +1368,20 @@ meta_screen_get_xinerama_for_rect (MetaScreen *screen,
best_xinerama = 0;
xinerama_score = 0;
- i = 0;
- while (i < screen->n_xinerama_infos)
+ for (i = 0; i < screen->n_xinerama_infos; i++)
{
- MetaRectangle dest, screen_info;
-
- screen_info.x = screen->xinerama_infos[i].x_origin;
- screen_info.y = screen->xinerama_infos[i].y_origin;
- screen_info.width = screen->xinerama_infos[i].width;
- screen_info.height = screen->xinerama_infos[i].height;
-
- if (meta_rectangle_intersect (&screen_info, rect, &dest))
+ MetaRectangle dest;
+ if (meta_rectangle_intersect (&screen->xinerama_infos[i].rect,
+ rect,
+ &dest))
{
- if (dest.width * dest.height > xinerama_score)
+ int cur = meta_rectangle_area (&dest);
+ if (cur > xinerama_score)
{
- xinerama_score = dest.width * dest.height;
+ xinerama_score = cur;
best_xinerama = i;
}
}
-
- ++i;
}
return &screen->xinerama_infos[best_xinerama];
@@ -1425,22 +1411,18 @@ meta_screen_get_xinerama_neighbor (MetaScreen *screen,
{
current = screen->xinerama_infos + i;
- if (((direction == META_SCREEN_RIGHT) &&
- (current->x_origin == input->x_origin + input->width) &&
- (current->y_origin >= input->y_origin) &&
- (current->y_origin <= input->y_origin+input->height)) ||
- ((direction == META_SCREEN_LEFT) &&
- (input->x_origin == current->x_origin + current->width) &&
- (current->y_origin >= input->y_origin) &&
- (current->y_origin <= input->y_origin + input->height)) ||
- ((direction == META_SCREEN_UP) &&
- (input->y_origin == current->y_origin + current->height) &&
- (current->x_origin >= input->x_origin) &&
- (current->x_origin <= input->x_origin + input->width)) ||
- ((direction == META_SCREEN_DOWN) &&
- (current->y_origin == input->y_origin + input->height) &&
- (current->x_origin >= input->x_origin) &&
- (current->x_origin <= input->x_origin + input->width)))
+ if ((direction == META_SCREEN_RIGHT &&
+ current->rect.x == input->rect.x + input->rect.width &&
+ meta_rectangle_vert_overlap(&current->rect, &input->rect)) ||
+ (direction == META_SCREEN_LEFT &&
+ input->rect.x == current->rect.x + current->rect.width &&
+ meta_rectangle_vert_overlap(&current->rect, &input->rect)) ||
+ (direction == META_SCREEN_UP &&
+ input->rect.y == current->rect.y + current->rect.height &&
+ meta_rectangle_horiz_overlap(&current->rect, &input->rect)) ||
+ (direction == META_SCREEN_DOWN &&
+ current->rect.y == input->rect.y + input->rect.height &&
+ meta_rectangle_horiz_overlap(&current->rect, &input->rect)))
{
return current;
}
@@ -1544,24 +1526,6 @@ meta_screen_get_natural_xinerama_list (MetaScreen *screen,
g_queue_free (xinerama_queue);
}
-gboolean
-meta_screen_rect_intersects_xinerama (MetaScreen *screen,
- MetaRectangle *rect,
- int which_xinerama)
-{
- MetaRectangle dest, screen_rect;
-
- screen_rect.x = screen->xinerama_infos[which_xinerama].x_origin;
- screen_rect.y = screen->xinerama_infos[which_xinerama].y_origin;
- screen_rect.width = screen->xinerama_infos[which_xinerama].width;
- screen_rect.height = screen->xinerama_infos[which_xinerama].height;
-
- if (meta_rectangle_intersect (&screen_rect, rect, &dest))
- return TRUE;
-
- return FALSE;
-}
-
const MetaXineramaScreenInfo*
meta_screen_get_current_xinerama (MetaScreen *screen)
{
@@ -1574,39 +1538,33 @@ meta_screen_get_current_xinerama (MetaScreen *screen)
if (screen->display->xinerama_cache_invalidated)
{
Window root_return, child_return;
- int root_x_return, root_y_return;
int win_x_return, win_y_return;
unsigned int mask_return;
int i;
+ MetaRectangle pointer_position;
screen->display->xinerama_cache_invalidated = FALSE;
+ pointer_position.width = pointer_position.height = 1;
XQueryPointer (screen->display->xdisplay,
screen->xroot,
&root_return,
&child_return,
- &root_x_return,
- &root_y_return,
+ &pointer_position.x,
+ &pointer_position.y,
&win_x_return,
&win_y_return,
&mask_return);
screen->last_xinerama_index = 0;
- i = 0;
- while (i < screen->n_xinerama_infos)
+ for (i = 0; i < screen->n_xinerama_infos; i++)
{
- if ((root_x_return >= screen->xinerama_infos[i].x_origin &&
- root_x_return < (screen->xinerama_infos[i].x_origin +
- screen->xinerama_infos[i].width) &&
- root_y_return >= screen->xinerama_infos[i].y_origin &&
- root_y_return < (screen->xinerama_infos[i].y_origin +
- screen->xinerama_infos[i].height)))
- {
- screen->last_xinerama_index = i;
- break;
- }
-
- ++i;
+ if (meta_rectangle_contains_rect (&screen->xinerama_infos[i].rect,
+ &pointer_position))
+ {
+ screen->last_xinerama_index = i;
+ break;
+ }
}
meta_topic (META_DEBUG_XINERAMA,
@@ -2201,8 +2159,8 @@ meta_screen_resize (MetaScreen *screen,
int width,
int height)
{
- screen->width = width;
- screen->height = height;
+ screen->rect.width = width;
+ screen->rect.height = height;
reload_xinerama_infos (screen);
set_desktop_geometry_hint (screen);
diff --git a/src/screen.h b/src/screen.h
index 96ad9c4..02c6807 100644
--- a/src/screen.h
+++ b/src/screen.h
@@ -3,7 +3,7 @@
/*
* Copyright (C) 2001 Havoc Pennington
* Copyright (C) 2003 Rob Adams
- * Copyright (C) 2004 Elijah Newren
+ * Copyright (C) 2004, 2005 Elijah Newren
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -33,10 +33,7 @@ typedef struct _MetaXineramaScreenInfo MetaXineramaScreenInfo;
struct _MetaXineramaScreenInfo
{
int number;
- int x_origin;
- int y_origin;
- int width;
- int height;
+ MetaRectangle rect;
};
typedef void (* MetaScreenWindowFunc) (MetaScreen *screen, MetaWindow *window,
@@ -69,8 +66,7 @@ struct _MetaScreen
Window xroot;
int default_depth;
Visual *default_xvisual;
- int width;
- int height;
+ MetaRectangle rect; /* Size of screen; rect.x & rect.y are always 0 */
MetaUI *ui;
MetaTabPopup *tab_popup;
@@ -158,9 +154,6 @@ const MetaXineramaScreenInfo* meta_screen_get_xinerama_for_rect (MetaScreen
const MetaXineramaScreenInfo* meta_screen_get_xinerama_for_window (MetaScreen *screen,
MetaWindow *window);
-gboolean meta_screen_rect_intersects_xinerama (MetaScreen *screen,
- MetaRectangle *window,
- int which_xinerama);
const MetaXineramaScreenInfo* meta_screen_get_xinerama_neighbor (MetaScreen *screen,
int which_xinerama,
diff --git a/src/session.c b/src/session.c
index 7550dc3..e4a08b3 100644
--- a/src/session.c
+++ b/src/session.c
@@ -3,7 +3,7 @@
/*
* Copyright (C) 2001 Havoc Pennington (some code in here from
* libgnomeui, (C) Tom Tromey, Carsten Schaar)
- * Copyright (C) 2004 Elijah Newren
+ * Copyright (C) 2004, 2005 Elijah Newren
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -728,47 +728,6 @@ window_type_from_string (const char *str)
return META_WINDOW_NORMAL;
}
-static const char*
-window_gravity_to_string (int gravity)
-{
- switch (gravity)
- {
- case NorthWestGravity:
- return "NorthWestGravity";
- break;
- case NorthGravity:
- return "NorthGravity";
- break;
- case NorthEastGravity:
- return "NorthEastGravity";
- break;
- case WestGravity:
- return "WestGravity";
- break;
- case CenterGravity:
- return "CenterGravity";
- break;
- case EastGravity:
- return "EastGravity";
- break;
- case SouthWestGravity:
- return "SouthWestGravity";
- break;
- case SouthGravity:
- return "SouthGravity";
- break;
- case SouthEastGravity:
- return "SouthEastGravity";
- break;
- case StaticGravity:
- return "StaticGravity";
- break;
- default:
- return "NorthWestGravity";
- break;
- }
-}
-
static int
window_gravity_from_string (const char *str)
{
@@ -995,7 +954,7 @@ save_state (void)
fputs (" <minimized/>\n", outfile);
/* Maximized */
- if (window->maximized)
+ if (META_WINDOW_MAXIMIZED (window))
{
fprintf (outfile,
" <maximized saved_x=\"%d\" saved_y=\"%d\" saved_width=\"%d\" saved_height=\"%d\"/>\n",
@@ -1021,7 +980,7 @@ save_state (void)
fprintf (outfile,
" <geometry x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" gravity=\"%s\"/>\n",
x, y, w, h,
- window_gravity_to_string (window->size_hints.win_gravity));
+ meta_gravity_to_string (window->size_hints.win_gravity));
}
fputs (" </window>\n", outfile);
@@ -1480,7 +1439,7 @@ start_element_handler (GMarkupParseContext *context,
pd->info->rect.y,
pd->info->rect.width,
pd->info->rect.height,
- window_gravity_to_string (pd->info->gravity));
+ meta_gravity_to_string (pd->info->gravity));
}
else
{
diff --git a/src/stack.c b/src/stack.c
index f9ab112..80ebfb9 100644
--- a/src/stack.c
+++ b/src/stack.c
@@ -201,8 +201,7 @@ window_is_fullscreen_size (MetaWindow *window)
{
int i;
- if (window->rect.width >= window->screen->width &&
- window->rect.height >= window->screen->height)
+ if (meta_rectangle_could_fit_rect (&window->rect, &window->screen->rect))
{
/* we use the work area since windows that try to
* position at 0,0 will get pushed down by menu panel
@@ -210,26 +209,20 @@ window_is_fullscreen_size (MetaWindow *window)
MetaRectangle workarea;
meta_window_get_work_area_current_xinerama (window, &workarea);
- if (window->rect.x <= workarea.x &&
- window->rect.y <= workarea.y &&
- window->rect.x + window->rect.width >= workarea.x + workarea.width &&
- window->rect.y + window->rect.height >= workarea.y + workarea.height)
+ if (meta_rectangle_contains_rect (&window->rect, &workarea))
return TRUE;
}
i = 0;
while (i < window->screen->n_xinerama_infos)
{
- if (window->rect.width >= window->screen->xinerama_infos[i].width &&
- window->rect.height >= window->screen->xinerama_infos[i].height)
+ if (meta_rectangle_could_fit_rect (&window->rect,
+ &window->screen->xinerama_infos[i].rect))
{
MetaRectangle workarea;
meta_window_get_work_area_current_xinerama (window, &workarea);
- if (window->rect.x <= workarea.x &&
- window->rect.y <= workarea.y &&
- window->rect.x + window->rect.width >= workarea.x + workarea.width &&
- window->rect.y + window->rect.height >= workarea.y + workarea.height)
+ if (meta_rectangle_contains_rect (&window->rect, &workarea))
return TRUE;
}
diff --git a/src/tabpopup.c b/src/tabpopup.c
index 427bb7f..f71e65d 100644
--- a/src/tabpopup.c
+++ b/src/tabpopup.c
@@ -241,15 +241,15 @@ meta_ui_tab_popup_new (const MetaTabEntry *entries,
if (outline)
{
- te->rect.x = entries[i].x;
- te->rect.y = entries[i].y;
- te->rect.width = entries[i].width;
- te->rect.height = entries[i].height;
-
- te->inner_rect.x = entries[i].inner_x;
- te->inner_rect.y = entries[i].inner_y;
- te->inner_rect.width = entries[i].inner_width;
- te->inner_rect.height = entries[i].inner_height;
+ te->rect.x = entries[i].rect.x;
+ te->rect.y = entries[i].rect.y;
+ te->rect.width = entries[i].rect.width;
+ te->rect.height = entries[i].rect.height;
+
+ te->inner_rect.x = entries[i].inner_rect.x;
+ te->inner_rect.y = entries[i].inner_rect.y;
+ te->inner_rect.width = entries[i].inner_rect.width;
+ te->inner_rect.height = entries[i].inner_rect.height;
}
tab_entries = g_list_prepend (tab_entries, te);
@@ -739,7 +739,8 @@ selectable_workspace_new (MetaWorkspace *workspace)
widget = g_object_new (meta_select_workspace_get_type (), NULL);
- screen_aspect = (double) workspace->screen->height / (double) workspace->screen->width;
+ screen_aspect = (double) workspace->screen->rect.height /
+ (double) workspace->screen->rect.width;
/* account for select rect */
gtk_widget_set_size_request (widget,
@@ -893,8 +894,8 @@ meta_select_workspace_expose_event (GtkWidget *widget,
SELECT_OUTLINE_WIDTH,
widget->allocation.width - SELECT_OUTLINE_WIDTH * 2,
widget->allocation.height - SELECT_OUTLINE_WIDTH * 2,
- workspace->screen->width,
- workspace->screen->height,
+ workspace->screen->rect.width,
+ workspace->screen->rect.height,
NULL,
(workspace->screen->active_workspace == workspace),
windows,
diff --git a/src/tabpopup.h b/src/tabpopup.h
index 74c883c..6695c9d 100644
--- a/src/tabpopup.h
+++ b/src/tabpopup.h
@@ -2,6 +2,7 @@
/*
* Copyright (C) 2001 Havoc Pennington
+ * Copyright (C) 2005 Elijah Newren
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -24,6 +25,7 @@
/* Don't include gtk.h or gdk.h here */
#include "common.h"
+#include "boxes.h"
#include <X11/Xlib.h>
#include <glib.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
@@ -37,8 +39,8 @@ struct _MetaTabEntry
MetaTabEntryKey key;
const char *title;
GdkPixbuf *icon;
- int x, y, width, height;
- int inner_x, inner_y, inner_width, inner_height;
+ MetaRectangle rect;
+ MetaRectangle inner_rect;
guint blank : 1;
guint hidden : 1;
guint demands_attention : 1;
diff --git a/src/testboxes.c b/src/testboxes.c
new file mode 100644
index 0000000..80425a6
--- /dev/null
+++ b/src/testboxes.c
@@ -0,0 +1,1400 @@
+/* Metacity box operation testing program */
+
+/*
+ * Copyright (C) 2005 Elijah Newren
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "boxes.h"
+#include <glib.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <X11/Xutil.h> /* Just for the definition of the various gravities */
+#include <time.h> /* To initialize random seed */
+
+#define NUM_RANDOM_RUNS 10000
+
+static void
+init_random_ness ()
+{
+ srand(time(NULL));
+}
+
+static void
+get_random_rect (MetaRectangle *rect)
+{
+ rect->x = rand () % 1600;
+ rect->y = rand () % 1200;
+ rect->width = rand () % 1600 + 1;
+ rect->height = rand () % 1200 + 1;
+}
+
+static MetaRectangle*
+new_meta_rect (int x, int y, int width, int height)
+{
+ MetaRectangle* temporary;
+ temporary = g_new (MetaRectangle, 1);
+ temporary->x = x;
+ temporary->y = y;
+ temporary->width = width;
+ temporary->height = height;
+
+ return temporary;
+}
+
+static MetaEdge*
+new_screen_edge (int x, int y, int width, int height, int side_type)
+{
+ MetaEdge* temporary;
+ temporary = g_new (MetaEdge, 1);
+ temporary->rect.x = x;
+ temporary->rect.y = y;
+ temporary->rect.width = width;
+ temporary->rect.height = height;
+ temporary->side_type = side_type;
+ temporary->edge_type = META_EDGE_SCREEN;
+
+ return temporary;
+}
+
+static MetaEdge*
+new_xinerama_edge (int x, int y, int width, int height, int side_type)
+{
+ MetaEdge* temporary;
+ temporary = g_new (MetaEdge, 1);
+ temporary->rect.x = x;
+ temporary->rect.y = y;
+ temporary->rect.width = width;
+ temporary->rect.height = height;
+ temporary->side_type = side_type;
+ temporary->edge_type = META_EDGE_XINERAMA;
+
+ return temporary;
+}
+
+static void
+test_area ()
+{
+ MetaRectangle temp;
+ int i;
+ for (i = 0; i < NUM_RANDOM_RUNS; i++)
+ {
+ get_random_rect (&temp);
+ g_assert (meta_rectangle_area (&temp) == temp.width * temp.height);
+ }
+
+ temp = meta_rect (0, 0, 5, 7);
+ g_assert (meta_rectangle_area (&temp) == 35);
+
+ printf ("%s passed.\n", __PRETTY_FUNCTION__);
+}
+
+static void
+test_intersect ()
+{
+ MetaRectangle a = {100, 200, 50, 40};
+ MetaRectangle b = { 0, 50, 110, 152};
+ MetaRectangle c = { 0, 0, 10, 10};
+ MetaRectangle d = {100, 100, 50, 50};
+ MetaRectangle b_intersect_d = {100, 100, 10, 50};
+ MetaRectangle temp;
+ MetaRectangle temp2;
+
+ meta_rectangle_intersect (&a, &b, &temp);
+ temp2 = meta_rect (100, 200, 10, 2);
+ g_assert (meta_rectangle_equal (&temp, &temp2));
+ g_assert (meta_rectangle_area (&temp) == 20);
+
+ meta_rectangle_intersect (&a, &c, &temp);
+ g_assert (meta_rectangle_area (&temp) == 0);
+
+ meta_rectangle_intersect (&a, &d, &temp);
+ g_assert (meta_rectangle_area (&temp) == 0);
+
+ meta_rectangle_intersect (&b, &d, &b);
+ g_assert (meta_rectangle_equal (&b, &b_intersect_d));
+
+ printf ("%s passed.\n", __PRETTY_FUNCTION__);
+}
+
+static void
+test_equal ()
+{
+ MetaRectangle a = {10, 12, 4, 18};
+ MetaRectangle b = a;
+ MetaRectangle c = {10, 12, 4, 19};
+ MetaRectangle d = {10, 12, 7, 18};
+ MetaRectangle e = {10, 62, 4, 18};
+ MetaRectangle f = {27, 12, 4, 18};
+
+ g_assert ( meta_rectangle_equal (&a, &b));
+ g_assert (!meta_rectangle_equal (&a, &c));
+ g_assert (!meta_rectangle_equal (&a, &d));
+ g_assert (!meta_rectangle_equal (&a, &e));
+ g_assert (!meta_rectangle_equal (&a, &f));
+
+ printf ("%s passed.\n", __PRETTY_FUNCTION__);
+}
+
+static void
+test_overlap_funcs ()
+{
+ MetaRectangle temp1, temp2;
+ int i;
+ for (i = 0; i < NUM_RANDOM_RUNS; i++)
+ {
+ get_random_rect (&temp1);
+ get_random_rect (&temp2);
+ g_assert (meta_rectangle_overlap (&temp1, &temp2) ==
+ (meta_rectangle_horiz_overlap (&temp1, &temp2) &&
+ meta_rectangle_vert_overlap (&temp1, &temp2)));
+ }
+
+ temp1 = meta_rect ( 0, 0, 10, 10);
+ temp2 = meta_rect (20, 0, 10, 5);
+ g_assert (!meta_rectangle_overlap (&temp1, &temp2));
+ g_assert (!meta_rectangle_horiz_overlap (&temp1, &temp2));
+ g_assert ( meta_rectangle_vert_overlap (&temp1, &temp2));
+
+ printf ("%s passed.\n", __PRETTY_FUNCTION__);
+}
+
+static void
+test_basic_fitting ()
+{
+ MetaRectangle temp1, temp2, temp3;
+ int i;
+ /* Four cases:
+ * case temp1 fits temp2 temp1 could fit temp2
+ * 1 Y Y
+ * 2 N Y
+ * 3 Y N
+ * 4 N N
+ * Of the four cases, case 3 is impossible. An alternate way of looking
+ * at this table is that either the middle column must be no, or the last
+ * column must be yes. So we test that. Also, we can repeat the test
+ * reversing temp1 and temp2.
+ */
+ for (i = 0; i < NUM_RANDOM_RUNS; i++)
+ {
+ get_random_rect (&temp1);
+ get_random_rect (&temp2);
+ g_assert (meta_rectangle_contains_rect (&temp1, &temp2) == FALSE ||
+ meta_rectangle_could_fit_rect (&temp1, &temp2) == TRUE);
+ g_assert (meta_rectangle_contains_rect (&temp2, &temp1) == FALSE ||
+ meta_rectangle_could_fit_rect (&temp2, &temp1) == TRUE);
+ }
+
+ temp1 = meta_rect ( 0, 0, 10, 10);
+ temp2 = meta_rect ( 5, 5, 5, 5);
+ temp3 = meta_rect ( 8, 2, 3, 7);
+ g_assert ( meta_rectangle_contains_rect (&temp1, &temp2));
+ g_assert (!meta_rectangle_contains_rect (&temp2, &temp1));
+ g_assert (!meta_rectangle_contains_rect (&temp1, &temp3));
+ g_assert ( meta_rectangle_could_fit_rect (&temp1, &temp3));
+ g_assert (!meta_rectangle_could_fit_rect (&temp3, &temp2));
+
+ printf ("%s passed.\n", __PRETTY_FUNCTION__);
+}
+
+static void
+free_strut_list (GSList *struts)
+{
+ GSList *tmp = struts;
+ while (tmp)
+ {
+ g_free (tmp->data);
+ tmp = tmp->next;
+ }
+ g_slist_free (struts);
+}
+
+static GSList*
+get_strut_list (int which)
+{
+ GSList *struts;
+
+ struts = NULL;
+
+ g_assert (which >=0 && which <= 6);
+ switch (which)
+ {
+ case 0:
+ break;
+ case 1:
+ struts = g_slist_prepend (struts, new_meta_rect ( 0, 0, 1600, 20));
+ struts = g_slist_prepend (struts, new_meta_rect ( 400, 1160, 1600, 40));
+ break;
+ case 2:
+ struts = g_slist_prepend (struts, new_meta_rect ( 0, 0, 1600, 20));
+ struts = g_slist_prepend (struts, new_meta_rect ( 800, 1100, 400, 100));
+ struts = g_slist_prepend (struts, new_meta_rect ( 300, 1150, 150, 50));
+ break;
+ case 3:
+ struts = g_slist_prepend (struts, new_meta_rect ( 0, 0, 1600, 20));
+ struts = g_slist_prepend (struts, new_meta_rect ( 800, 1100, 400, 100));
+ struts = g_slist_prepend (struts, new_meta_rect ( 300, 1150, 80, 50));
+ struts = g_slist_prepend (struts, new_meta_rect ( 700, 525, 200, 150));
+ break;
+ case 4:
+ struts = g_slist_prepend (struts, new_meta_rect ( 0, 0, 800, 1200));
+ struts = g_slist_prepend (struts, new_meta_rect ( 800, 0, 1600, 20));
+ break;
+ case 5:
+ struts = g_slist_prepend (struts, new_meta_rect ( 800, 0, 1600, 20));
+ struts = g_slist_prepend (struts, new_meta_rect ( 0, 0, 800, 1200));
+ struts = g_slist_prepend (struts, new_meta_rect ( 800, 10, 800, 1200));
+ break;
+ case 6:
+ struts = g_slist_prepend (struts, new_meta_rect ( 0, 0, 1600, 40));
+ struts = g_slist_prepend (struts, new_meta_rect ( 0, 0, 1600, 20));
+ break;
+ }
+
+ return struts;
+}
+
+static GList*
+get_screen_region (int which)
+{
+ GList *ret;
+ GSList *struts;
+ MetaRectangle basic_rect;
+
+ basic_rect = meta_rect (0, 0, 1600, 1200);
+ ret = NULL;
+
+ struts = get_strut_list (which);
+ ret = meta_rectangle_get_minimal_spanning_set_for_region (&basic_rect, struts);
+ free_strut_list (struts);
+
+ return ret;
+}
+
+static GList*
+get_screen_edges (int which)
+{
+ GList *ret;
+ GSList *struts;
+ MetaRectangle basic_rect;
+
+ basic_rect = meta_rect (0, 0, 1600, 1200);
+ ret = NULL;
+
+ struts = get_strut_list (which);
+ ret = meta_rectangle_find_onscreen_edges (&basic_rect, struts);
+ free_strut_list (struts);
+
+ return ret;
+}
+
+static GList*
+get_xinerama_edges (int which_xinerama_set, int which_strut_set)
+{
+ GList *ret;
+ GSList *struts;
+ GList *xins;
+
+ xins = NULL;
+ g_assert (which_xinerama_set >=0 && which_xinerama_set <= 3);
+ switch (which_xinerama_set)
+ {
+ case 0:
+ xins = g_list_prepend (xins, new_meta_rect ( 0, 0, 1600, 1200));
+ break;
+ case 1:
+ xins = g_list_prepend (xins, new_meta_rect ( 0, 0, 800, 1200));
+ xins = g_list_prepend (xins, new_meta_rect (800, 0, 800, 1200));
+ break;
+ case 2:
+ xins = g_list_prepend (xins, new_meta_rect ( 0, 0, 1600, 600));
+ xins = g_list_prepend (xins, new_meta_rect ( 0, 600, 1600, 600));
+ break;
+ case 3:
+ xins = g_list_prepend (xins, new_meta_rect ( 0, 0, 1600, 600));
+ xins = g_list_prepend (xins, new_meta_rect ( 0, 600, 800, 600));
+ xins = g_list_prepend (xins, new_meta_rect (800, 600, 800, 600));
+ break;
+ }
+
+ ret = NULL;
+
+ struts = get_strut_list (which_strut_set);
+ ret = meta_rectangle_find_nonintersected_xinerama_edges (xins, struts);
+
+ free_strut_list (struts);
+ meta_rectangle_free_list_and_elements (xins);
+
+ return ret;
+}
+
+#if 0
+static void
+test_merge_regions ()
+{
+ /* logarithmically distributed random number of struts (range?)
+ * logarithmically distributed random size of struts (up to screen size???)
+ * uniformly distributed location of center of struts (within screen)
+ * merge all regions that are possible
+ * print stats on problem setup
+ * number of (non-completely-occluded?) struts
+ * percentage of screen covered
+ * length of resulting non-minimal spanning set
+ * length of resulting minimal spanning set
+ * print stats on merged regions:
+ * number boxes merged
+ * number of those merges that were of the form A contains B
+ * number of those merges that were of the form A partially contains B
+ * number of those merges that were of the form A is adjacent to B
+ */
+
+ GList* region;
+ GList* compare;
+ int num_contains, num_merged, num_part_contains, num_adjacent;
+
+ num_contains = num_merged = num_part_contains = num_adjacent = 0;
+ compare = region = get_screen_region (2);
+ g_assert (region);
+
+ printf ("Merging stats:\n");
+ printf (" Length of initial list: %d\n", g_list_length (region));
+#ifdef PRINT_DEBUG
+ char rect1[RECT_LENGTH], rect2[RECT_LENGTH];
+ char region_list[(RECT_LENGTH + 2) * g_list_length (region)];
+ meta_rectangle_region_to_string (region, ", ", region_list);
+ printf (" Initial rectangles: %s\n", region_list);
+#endif
+
+ while (compare && compare->next)
+ {
+ MetaRectangle *a = compare->data;
+ GList *other = compare->next;
+
+ g_assert (a->width > 0 && a->height > 0);
+
+ while (other)
+ {
+ MetaRectangle *b = other->data;
+ GList *delete_me = NULL;
+
+ g_assert (b->width > 0 && b->height > 0);
+
+#ifdef PRINT_DEBUG
+ printf (" -- Comparing %s to %s --\n",
+ meta_rectangle_to_string (a, rect1),
+ meta_rectangle_to_string (b, rect2));
+#endif
+
+ /* If a contains b, just remove b */
+ if (meta_rectangle_contains_rect (a, b))
+ {
+ delete_me = other;
+ num_contains++;
+ num_merged++;
+ }
+ /* If b contains a, just remove a */
+ else if (meta_rectangle_contains_rect (a, b))
+ {
+ delete_me = compare;
+ num_contains++;
+ num_merged++;
+ }
+ /* If a and b might be mergeable horizontally */
+ else if (a->y == b->y && a->height == b->height)
+ {
+ /* If a and b overlap */
+ if (meta_rectangle_overlap (a, b))
+ {
+ int new_x = MIN (a->x, b->x);
+ a->width = MAX (a->x + a->width, b->x + b->width) - new_x;
+ a->x = new_x;
+ delete_me = other;
+ num_part_contains++;
+ num_merged++;
+ }
+ /* If a and b are adjacent */
+ else if (a->x + a->width == b->x || a->x == b->x + b->width)
+ {
+ int new_x = MIN (a->x, b->x);
+ a->width = MAX (a->x + a->width, b->x + b->width) - new_x;
+ a->x = new_x;
+ delete_me = other;
+ num_adjacent++;
+ num_merged++;
+ }
+ }
+ /* If a and b might be mergeable vertically */
+ else if (a->x == b->x && a->width == b->width)
+ {
+ /* If a and b overlap */
+ if (meta_rectangle_overlap (a, b))
+ {
+ int new_y = MIN (a->y, b->y);
+ a->height = MAX (a->y + a->height, b->y + b->height) - new_y;
+ a->y = new_y;
+ delete_me = other;
+ num_part_contains++;
+ num_merged++;
+ }
+ /* If a and b are adjacent */
+ else if (a->y + a->height == b->y || a->y == b->y + b->height)
+ {
+ int new_y = MIN (a->y, b->y);
+ a->height = MAX (a->y + a->height, b->y + b->height) - new_y;
+ a->y = new_y;
+ delete_me = other;
+ num_adjacent++;
+ num_merged++;
+ }
+ }
+
+ other = other->next;
+
+ /* Delete any rectangle in the list that is no longer wanted */
+ if (delete_me != NULL)
+ {
+#ifdef PRINT_DEBUG
+ MetaRectangle *bla = delete_me->data;
+ printf (" Deleting rect %s\n",
+ meta_rectangle_to_string (bla, rect1));
+#endif
+
+ /* Deleting the rect we're compare others to is a little tricker */
+ if (compare == delete_me)
+ {
+ compare = compare->next;
+ other = compare->next;
+ a = compare->data;
+ }
+
+ /* Okay, we can free it now */
+ g_free (delete_me->data);
+ region = g_list_delete_link (region, delete_me);
+ }
+
+#ifdef PRINT_DEBUG
+ char region_list[(RECT_LENGTH + 2) * g_list_length (region)];
+ meta_rectangle_region_to_string (region, ", ", region_list);
+ printf (" After comparison, new list is: %s\n", region_list);
+#endif
+ }
+
+ compare = compare->next;
+ }
+
+ printf (" Num rectangles contained in others : %d\n",
+ num_contains);
+ printf (" Num rectangles partially contained in others: %d\n",
+ num_part_contains);
+ printf (" Num rectangles adjacent to others : %d\n",
+ num_adjacent);
+ printf (" Num rectangles merged with others : %d\n",
+ num_merged);
+#ifdef PRINT_DEBUG
+ char region_list2[(RECT_LENGTH + 2) * g_list_length (region)];
+ meta_rectangle_region_to_string (region, ", ", region_list2);
+ printf (" Final rectangles: %s\n", region_list2);
+#endif
+
+ meta_rectangle_free_spanning_set (region);
+ region = NULL;
+
+ printf ("%s passed.\n", __PRETTY_FUNCTION__);
+}
+#endif
+
+static void
+verify_lists_are_equal (GList *code, GList *answer)
+{
+ int which = 0;
+
+ while (code && answer)
+ {
+ MetaRectangle *a = code->data;
+ MetaRectangle *b = answer->data;
+
+ if (a->x != b->x ||
+ a->y != b->y ||
+ a->width != b->width ||
+ a->height != b->height)
+ {
+ g_error ("%dth item in code answer answer lists do not match; "
+ "code rect: %d,%d + %d,%d; answer rect: %d,%d + %d,%d\n",
+ which,
+ a->x, a->y, a->width, a->height,
+ b->x, b->y, b->width, b->height);
+ }
+
+ code = code->next;
+ answer = answer->next;
+
+ which++;
+ }
+
+ /* Ought to be at the end of both lists; check if we aren't */
+ if (code)
+ {
+ MetaRectangle *tmp = code->data;
+ g_error ("code list longer than answer list by %d items; "
+ "first extra item: %d,%d +%d,%d\n",
+ g_list_length (code),
+ tmp->x, tmp->y, tmp->width, tmp->height);
+ }
+
+ if (answer)
+ {
+ MetaRectangle *tmp = answer->data;
+ g_error ("answer list longer than code list by %d items; "
+ "first extra item: %d,%d +%d,%d\n",
+ g_list_length (answer),
+ tmp->x, tmp->y, tmp->width, tmp->height);
+ }
+}
+
+static void
+test_regions_okay ()
+{
+ GList* region;
+ GList* tmp;
+
+ /*************************************************************/
+ /* Make sure test region 0 has the right spanning rectangles */
+ /*************************************************************/
+ region = get_screen_region (0);
+ tmp = NULL;
+ tmp = g_list_prepend (tmp, new_meta_rect (0, 0, 1600, 1200));
+ verify_lists_are_equal (region, tmp);
+ meta_rectangle_free_list_and_elements (tmp);
+ meta_rectangle_free_list_and_elements (region);
+
+ /*************************************************************/
+ /* Make sure test region 1 has the right spanning rectangles */
+ /*************************************************************/
+ region = get_screen_region (1);
+ tmp = NULL;
+ tmp = g_list_prepend (tmp, new_meta_rect (0, 20, 400, 1180));
+ tmp = g_list_prepend (tmp, new_meta_rect (0, 20, 1600, 1140));
+ verify_lists_are_equal (region, tmp);
+ meta_rectangle_free_list_and_elements (tmp);
+ meta_rectangle_free_list_and_elements (region);
+
+ /*************************************************************/
+ /* Make sure test region 2 has the right spanning rectangles */
+ /*************************************************************/
+ region = get_screen_region (2);
+ tmp = NULL;
+ tmp = g_list_prepend (tmp, new_meta_rect ( 0, 20, 300, 1180));
+ tmp = g_list_prepend (tmp, new_meta_rect ( 450, 20, 350, 1180));
+ tmp = g_list_prepend (tmp, new_meta_rect (1200, 20, 400, 1180));
+ tmp = g_list_prepend (tmp, new_meta_rect ( 0, 20, 800, 1130));
+ tmp = g_list_prepend (tmp, new_meta_rect ( 0, 20, 1600, 1080));
+ verify_lists_are_equal (region, tmp);
+ meta_rectangle_free_list_and_elements (tmp);
+ meta_rectangle_free_list_and_elements (region);
+
+ /*************************************************************/
+ /* Make sure test region 3 has the right spanning rectangles */
+ /*************************************************************/
+ region = get_screen_region (3);
+ tmp = NULL;
+ tmp = g_list_prepend (tmp, new_meta_rect ( 380, 675, 420, 525)); // 220500
+ tmp = g_list_prepend (tmp, new_meta_rect ( 0, 20, 300, 1180)); // 354000
+ tmp = g_list_prepend (tmp, new_meta_rect ( 380, 20, 320, 1180)); // 377600
+ tmp = g_list_prepend (tmp, new_meta_rect ( 0, 675, 800, 475)); // 380000
+ tmp = g_list_prepend (tmp, new_meta_rect (1200, 20, 400, 1180)); // 472000
+ tmp = g_list_prepend (tmp, new_meta_rect ( 0, 675, 1600, 425)); // 680000
+ tmp = g_list_prepend (tmp, new_meta_rect ( 900, 20, 700, 1080)); // 756000
+ tmp = g_list_prepend (tmp, new_meta_rect ( 0, 20, 700, 1130)); // 791000
+ tmp = g_list_prepend (tmp, new_meta_rect ( 0, 20, 1600, 505)); // 808000
+#if 0
+ printf ("Got to here...\n");
+ char region_list[(RECT_LENGTH+2) * g_list_length (region)];
+ char tmp_list[ (RECT_LENGTH+2) * g_list_length (tmp)];
+ meta_rectangle_region_to_string (region, ", ", region_list);
+ meta_rectangle_region_to_string (region, ", ", tmp_list);
+ printf ("%s vs. %s\n", region_list, tmp_list);
+#endif
+ verify_lists_are_equal (region, tmp);
+ meta_rectangle_free_list_and_elements (tmp);
+ meta_rectangle_free_list_and_elements (region);
+
+ /*************************************************************/
+ /* Make sure test region 4 has the right spanning rectangles */
+ /*************************************************************/
+ region = get_screen_region (4);
+ tmp = NULL;
+ tmp = g_list_prepend (tmp, new_meta_rect ( 800, 20, 800, 1180));
+ verify_lists_are_equal (region, tmp);
+ meta_rectangle_free_list_and_elements (tmp);
+ meta_rectangle_free_list_and_elements (region);
+
+ /*************************************************************/
+ /* Make sure test region 5 has the right spanning rectangles */
+ /*************************************************************/
+ region = get_screen_region (5);
+ verify_lists_are_equal (region, NULL);
+
+ /* FIXME: Still to do:
+ * - Create random struts and check the regions somehow
+ */
+
+ printf ("%s passed.\n", __PRETTY_FUNCTION__);
+}
+
+static void
+test_region_fitting ()
+{
+ GList* region;
+ MetaRectangle rect;
+
+ /* See test_basic_fitting() for how/why these automated random tests work */
+ int i;
+ region = get_screen_region (3);
+ for (i = 0; i < NUM_RANDOM_RUNS; i++)
+ {
+ get_random_rect (&rect);
+ g_assert (meta_rectangle_contained_in_region (region, &rect) == FALSE ||
+ meta_rectangle_could_fit_in_region (region, &rect) == TRUE);
+ }
+ meta_rectangle_free_list_and_elements (region);
+
+ /* Do some manual tests too */
+ region = get_screen_region (1);
+
+ rect = meta_rect (50, 50, 400, 400);
+ g_assert (meta_rectangle_could_fit_in_region (region, &rect));
+ g_assert (meta_rectangle_contained_in_region (region, &rect));
+
+ rect = meta_rect (250, 0, 500, 1150);
+ g_assert (!meta_rectangle_could_fit_in_region (region, &rect));
+ g_assert (!meta_rectangle_contained_in_region (region, &rect));
+
+ rect = meta_rect (250, 0, 400, 400);
+ g_assert (meta_rectangle_could_fit_in_region (region, &rect));
+ g_assert (!meta_rectangle_contained_in_region (region, &rect));
+
+ meta_rectangle_free_list_and_elements (region);
+
+ region = get_screen_region (2);
+ rect = meta_rect (1000, 50, 600, 1100);
+ g_assert (meta_rectangle_could_fit_in_region (region, &rect));
+ g_assert (!meta_rectangle_contained_in_region (region, &rect));
+
+ meta_rectangle_free_list_and_elements (region);
+
+ printf ("%s passed.\n", __PRETTY_FUNCTION__);
+}
+
+static void
+test_clamping_to_region ()
+{
+ GList* region;
+ MetaRectangle rect;
+ MetaRectangle min_size;
+ FixedDirections fixed_directions;
+
+ min_size.height = min_size.width = 1;
+ fixed_directions = 0;
+
+ int i;
+ region = get_screen_region (3);
+ for (i = 0; i < NUM_RANDOM_RUNS; i++)
+ {
+ MetaRectangle temp;
+ get_random_rect (&rect);
+ temp = rect;
+ meta_rectangle_clamp_to_fit_into_region (region,
+ fixed_directions,
+ &rect,
+ &min_size);
+ g_assert (meta_rectangle_could_fit_in_region (region, &rect) == TRUE);
+ g_assert (rect.x == temp.x && rect.y == temp.y);
+ }
+ meta_rectangle_free_list_and_elements (region);
+
+ /* Do some manual tests too */
+ region = get_screen_region (1);
+
+ rect = meta_rect (50, 50, 10000, 10000);
+ meta_rectangle_clamp_to_fit_into_region (region,
+ fixed_directions,
+ &rect,
+ &min_size);
+ g_assert (rect.width == 1600 && rect.height == 1140);
+
+ rect = meta_rect (275, -50, 410, 10000);
+ meta_rectangle_clamp_to_fit_into_region (region,
+ fixed_directions,
+ &rect,
+ &min_size);
+ g_assert (rect.width == 400 && rect.height == 1180);
+
+ rect = meta_rect (50, 50, 10000, 10000);
+ min_size.height = 1170;
+ meta_rectangle_clamp_to_fit_into_region (region,
+ fixed_directions,
+ &rect,
+ &min_size);
+ g_assert (rect.width == 400 && rect.height == 1180);
+
+ rect = meta_rect (50, 50, 10000, 10000);
+ min_size.width = 600; min_size.height = 1170;
+ meta_rectangle_clamp_to_fit_into_region (region,
+ fixed_directions,
+ &rect,
+ &min_size);
+ g_assert (rect.width == 600 && rect.height == 1170);
+
+ rect = meta_rect (350, 50, 100, 1100);
+ min_size.width = 1; min_size.height = 1;
+ fixed_directions = FIXED_DIRECTION_X;
+ meta_rectangle_clamp_to_fit_into_region (region,
+ fixed_directions,
+ &rect,
+ &min_size);
+ g_assert (rect.width == 100 && rect.height == 1100);
+
+ rect = meta_rect (300, 70, 500, 1100);
+ min_size.width = 1; min_size.height = 1;
+ fixed_directions = FIXED_DIRECTION_Y;
+ meta_rectangle_clamp_to_fit_into_region (region,
+ fixed_directions,
+ &rect,
+ &min_size);
+ g_assert (rect.width == 400 && rect.height == 1100);
+
+ rect = meta_rect (300, 70, 999999, 999999);
+ min_size.width = 100; min_size.height = 200;
+ fixed_directions = FIXED_DIRECTION_Y;
+ meta_rectangle_clamp_to_fit_into_region (region,
+ fixed_directions,
+ &rect,
+ &min_size);
+ g_assert (rect.width == 100 && rect.height == 999999);
+
+ meta_rectangle_free_list_and_elements (region);
+
+ printf ("%s passed.\n", __PRETTY_FUNCTION__);
+}
+
+static gboolean
+rect_overlaps_region (const GList *spanning_rects,
+ const MetaRectangle *rect)
+{
+ /* FIXME: Should I move this to boxes.[ch]? */
+ const GList *temp;
+ gboolean overlaps;
+
+ temp = spanning_rects;
+ overlaps = FALSE;
+ while (!overlaps && temp != NULL)
+ {
+ overlaps = overlaps || meta_rectangle_overlap (temp->data, rect);
+ temp = temp->next;
+ }
+
+ return overlaps;
+}
+
+gboolean time_to_print = FALSE;
+
+static void
+test_clipping_to_region ()
+{
+ GList* region;
+ MetaRectangle rect, temp;
+ FixedDirections fixed_directions;
+
+ fixed_directions = 0;
+
+ int i;
+ region = get_screen_region (3);
+ for (i = 0; i < NUM_RANDOM_RUNS; i++)
+ {
+ get_random_rect (&rect);
+ if (rect_overlaps_region (region, &rect))
+ {
+ meta_rectangle_clip_to_region (region, 0, &rect);
+ g_assert (meta_rectangle_contained_in_region (region, &rect) == TRUE);
+ }
+ }
+ meta_rectangle_free_list_and_elements (region);
+
+ /* Do some manual tests too */
+ region = get_screen_region (2);
+
+ rect = meta_rect (-50, -10, 10000, 10000);
+ meta_rectangle_clip_to_region (region,
+ fixed_directions,
+ &rect);
+ g_assert (meta_rectangle_equal (region->data, &rect));
+
+ rect = meta_rect (300, 1000, 400, 200);
+ temp = meta_rect (300, 1000, 400, 150);
+ meta_rectangle_clip_to_region (region,
+ fixed_directions,
+ &rect);
+ g_assert (meta_rectangle_equal (&rect, &temp));
+
+ rect = meta_rect (400, 1000, 300, 200);
+ temp = meta_rect (450, 1000, 250, 200);
+ meta_rectangle_clip_to_region (region,
+ fixed_directions,
+ &rect);
+ g_assert (meta_rectangle_equal (&rect, &temp));
+
+ rect = meta_rect (400, 1000, 300, 200);
+ temp = meta_rect (400, 1000, 300, 150);
+ meta_rectangle_clip_to_region (region,
+ FIXED_DIRECTION_X,
+ &rect);
+ g_assert (meta_rectangle_equal (&rect, &temp));
+
+ rect = meta_rect (400, 1000, 300, 200);
+ temp = meta_rect (400, 1000, 300, 150);
+ meta_rectangle_clip_to_region (region,
+ FIXED_DIRECTION_X,
+ &rect);
+ g_assert (meta_rectangle_equal (&rect, &temp));
+
+ meta_rectangle_free_list_and_elements (region);
+
+ printf ("%s passed.\n", __PRETTY_FUNCTION__);
+}
+
+static void
+test_shoving_into_region ()
+{
+ GList* region;
+ MetaRectangle rect, temp;
+ FixedDirections fixed_directions;
+
+ fixed_directions = 0;
+
+ int i;
+ region = get_screen_region (3);
+ for (i = 0; i < NUM_RANDOM_RUNS; i++)
+ {
+ get_random_rect (&rect);
+ if (meta_rectangle_could_fit_in_region (region, &rect))
+ {
+ meta_rectangle_shove_into_region (region, 0, &rect);
+ g_assert (meta_rectangle_contained_in_region (region, &rect));
+ }
+ }
+ meta_rectangle_free_list_and_elements (region);
+
+ /* Do some manual tests too */
+ region = get_screen_region (2);
+
+ rect = meta_rect (300, 1000, 400, 200);
+ temp = meta_rect (300, 950, 400, 200);
+ meta_rectangle_shove_into_region (region,
+ fixed_directions,
+ &rect);
+ g_assert (meta_rectangle_equal (&rect, &temp));
+
+ rect = meta_rect (425, 1000, 300, 200);
+ temp = meta_rect (450, 1000, 300, 200);
+ meta_rectangle_shove_into_region (region,
+ fixed_directions,
+ &rect);
+ g_assert (meta_rectangle_equal (&rect, &temp));
+
+ rect = meta_rect (425, 1000, 300, 200);
+ temp = meta_rect (425, 950, 300, 200);
+ meta_rectangle_shove_into_region (region,
+ FIXED_DIRECTION_X,
+ &rect);
+ g_assert (meta_rectangle_equal (&rect, &temp));
+
+ rect = meta_rect ( 300, 1000, 400, 200);
+ temp = meta_rect (1200, 1000, 400, 200);
+ meta_rectangle_shove_into_region (region,
+ FIXED_DIRECTION_Y,
+ &rect);
+ g_assert (meta_rectangle_equal (&rect, &temp));
+
+ rect = meta_rect ( 800, 1150, 400, 50); /* Completely "offscreen" :) */
+ temp = meta_rect ( 800, 1050, 400, 50);
+ meta_rectangle_shove_into_region (region,
+ 0,
+ &rect);
+ g_assert (meta_rectangle_equal (&rect, &temp));
+
+ rect = meta_rect (-1000, 0, 400, 150); /* Offscreen in 2 directions */
+ temp = meta_rect ( 0, 20, 400, 150);
+ meta_rectangle_shove_into_region (region,
+ 0,
+ &rect);
+ g_assert (meta_rectangle_equal (&rect, &temp));
+
+ meta_rectangle_free_list_and_elements (region);
+
+ printf ("%s passed.\n", __PRETTY_FUNCTION__);
+}
+
+static void
+verify_edge_lists_are_equal (GList *code, GList *answer)
+{
+ int which = 0;
+
+ while (code && answer)
+ {
+ MetaEdge *a = code->data;
+ MetaEdge *b = answer->data;
+
+ if (!meta_rectangle_equal (&a->rect, &b->rect) ||
+ a->side_type != b->side_type ||
+ a->edge_type != b->edge_type)
+ {
+ g_error ("%dth item in code answer answer lists do not match; "
+ "code rect: %d,%d + %d,%d; answer rect: %d,%d + %d,%d\n",
+ which,
+ a->rect.x, a->rect.y, a->rect.width, a->rect.height,
+ b->rect.x, b->rect.y, b->rect.width, b->rect.height);
+ }
+
+ code = code->next;
+ answer = answer->next;
+
+ which++;
+ }
+
+ /* Ought to be at the end of both lists; check if we aren't */
+ if (code)
+ {
+ MetaEdge *tmp = code->data;
+ g_error ("code list longer than answer list by %d items; "
+ "first extra item rect: %d,%d +%d,%d\n",
+ g_list_length (code),
+ tmp->rect.x, tmp->rect.y, tmp->rect.width, tmp->rect.height);
+ }
+
+ if (answer)
+ {
+ MetaEdge *tmp = answer->data;
+ g_error ("answer list longer than code list by %d items; "
+ "first extra item rect: %d,%d +%d,%d\n",
+ g_list_length (answer),
+ tmp->rect.x, tmp->rect.y, tmp->rect.width, tmp->rect.height);
+ }
+}
+
+static void
+test_find_onscreen_edges ()
+{
+ GList* edges;
+ GList* tmp;
+
+ int left = META_DIRECTION_LEFT;
+ int right = META_DIRECTION_RIGHT;
+ int top = META_DIRECTION_TOP;
+ int bottom = META_DIRECTION_BOTTOM;
+
+ /*************************************************/
+ /* Make sure test region 0 has the correct edges */
+ /*************************************************/
+ edges = get_screen_edges (0);
+ tmp = NULL;
+ tmp = g_list_prepend (tmp, new_screen_edge ( 0, 1200, 1600, 0, bottom));
+ tmp = g_list_prepend (tmp, new_screen_edge ( 0, 0, 1600, 0, top));
+ tmp = g_list_prepend (tmp, new_screen_edge (1600, 0, 0, 1200, right));
+ tmp = g_list_prepend (tmp, new_screen_edge ( 0, 0, 0, 1200, left));
+ verify_edge_lists_are_equal (edges, tmp);
+ meta_rectangle_free_list_and_elements (tmp);
+ meta_rectangle_free_list_and_elements (edges);
+
+ /*************************************************/
+ /* Make sure test region 1 has the correct edges */
+ /*************************************************/
+ edges = get_screen_edges (1);
+ tmp = NULL;
+ tmp = g_list_prepend (tmp, new_screen_edge ( 0, 1200, 400, 0, bottom));
+ tmp = g_list_prepend (tmp, new_screen_edge ( 400, 1160, 1200, 0, bottom));
+ tmp = g_list_prepend (tmp, new_screen_edge ( 0, 20, 1600, 0, top));
+ tmp = g_list_prepend (tmp, new_screen_edge (1600, 20, 0, 1140, right));
+ tmp = g_list_prepend (tmp, new_screen_edge ( 400, 1160, 0, 40, right));
+ tmp = g_list_prepend (tmp, new_screen_edge ( 0, 20, 0, 1180, left));
+ verify_edge_lists_are_equal (edges, tmp);
+ meta_rectangle_free_list_and_elements (tmp);
+ meta_rectangle_free_list_and_elements (edges);
+
+ /*************************************************/
+ /* Make sure test region 2 has the correct edges */
+ /*************************************************/
+ edges = get_screen_edges (2);
+ tmp = NULL;
+ tmp = g_list_prepend (tmp, new_screen_edge (1200, 1200, 400, 0, bottom));
+ tmp = g_list_prepend (tmp, new_screen_edge ( 450, 1200, 350, 0, bottom));
+ tmp = g_list_prepend (tmp, new_screen_edge ( 0, 1200, 300, 0, bottom));
+ tmp = g_list_prepend (tmp, new_screen_edge ( 300, 1150, 150, 0, bottom));
+ tmp = g_list_prepend (tmp, new_screen_edge ( 800, 1100, 400, 0, bottom));
+ tmp = g_list_prepend (tmp, new_screen_edge ( 0, 20, 1600, 0, top));
+ tmp = g_list_prepend (tmp, new_screen_edge (1600, 20, 0, 1180, right));
+ tmp = g_list_prepend (tmp, new_screen_edge ( 800, 1100, 0, 100, right));
+ tmp = g_list_prepend (tmp, new_screen_edge ( 300, 1150, 0, 50, right));
+ tmp = g_list_prepend (tmp, new_screen_edge (1200, 1100, 0, 100, left));
+ tmp = g_list_prepend (tmp, new_screen_edge ( 450, 1150, 0, 50, left));
+ tmp = g_list_prepend (tmp, new_screen_edge ( 0, 20, 0, 1180, left));
+ verify_edge_lists_are_equal (edges, tmp);
+ meta_rectangle_free_list_and_elements (tmp);
+ meta_rectangle_free_list_and_elements (edges);
+
+ /*************************************************/
+ /* Make sure test region 3 has the correct edges */
+ /*************************************************/
+ edges = get_screen_edges (3);
+ tmp = NULL;
+ tmp = g_list_prepend (tmp, new_screen_edge (1200, 1200, 400, 0, bottom));
+ tmp = g_list_prepend (tmp, new_screen_edge ( 380, 1200, 420, 0, bottom));
+ tmp = g_list_prepend (tmp, new_screen_edge ( 0, 1200, 300, 0, bottom));
+ tmp = g_list_prepend (tmp, new_screen_edge ( 300, 1150, 80, 0, bottom));
+ tmp = g_list_prepend (tmp, new_screen_edge ( 800, 1100, 400, 0, bottom));
+ tmp = g_list_prepend (tmp, new_screen_edge ( 700, 525, 200, 0, bottom));
+ tmp = g_list_prepend (tmp, new_screen_edge ( 700, 675, 200, 0, top));
+ tmp = g_list_prepend (tmp, new_screen_edge ( 0, 20, 1600, 0, top));
+ tmp = g_list_prepend (tmp, new_screen_edge (1600, 20, 0, 1180, right));
+ tmp = g_list_prepend (tmp, new_screen_edge ( 800, 1100, 0, 100, right));
+ tmp = g_list_prepend (tmp, new_screen_edge ( 700, 525, 0, 150, right));
+ tmp = g_list_prepend (tmp, new_screen_edge ( 300, 1150, 0, 50, right));
+ tmp = g_list_prepend (tmp, new_screen_edge (1200, 1100, 0, 100, left));
+ tmp = g_list_prepend (tmp, new_screen_edge ( 900, 525, 0, 150, left));
+ tmp = g_list_prepend (tmp, new_screen_edge ( 380, 1150, 0, 50, left));
+ tmp = g_list_prepend (tmp, new_screen_edge ( 0, 20, 0, 1180, left));
+
+#if 0
+ #define FUDGE 50 /* number of edges */
+ char big_buffer1[(EDGE_LENGTH+2)*FUDGE], big_buffer2[(EDGE_LENGTH+2)*FUDGE];
+ meta_rectangle_edge_list_to_string (edges, "\n ", big_buffer1);
+ meta_rectangle_edge_list_to_string (tmp, "\n ", big_buffer2);
+ printf("Generated edge list:\n %s\nComparison edges list:\n %s\n",
+ big_buffer1, big_buffer2);
+#endif
+
+ verify_edge_lists_are_equal (edges, tmp);
+ meta_rectangle_free_list_and_elements (tmp);
+ meta_rectangle_free_list_and_elements (edges);
+
+ /*************************************************/
+ /* Make sure test region 4 has the correct edges */
+ /*************************************************/
+ edges = get_screen_edges (4);
+ tmp = NULL;
+ tmp = g_list_prepend (tmp, new_screen_edge ( 800, 1200, 800, 0, bottom));
+ tmp = g_list_prepend (tmp, new_screen_edge ( 800, 20, 800, 0, top));
+ tmp = g_list_prepend (tmp, new_screen_edge (1600, 20, 0, 1180, right));
+ tmp = g_list_prepend (tmp, new_screen_edge ( 800, 20, 0, 1180, left));
+ verify_edge_lists_are_equal (edges, tmp);
+ meta_rectangle_free_list_and_elements (tmp);
+ meta_rectangle_free_list_and_elements (edges);
+
+ /*************************************************/
+ /* Make sure test region 5 has the correct edges */
+ /*************************************************/
+ edges = get_screen_edges (5);
+ tmp = NULL;
+ verify_edge_lists_are_equal (edges, tmp);
+ meta_rectangle_free_list_and_elements (tmp);
+ meta_rectangle_free_list_and_elements (edges);
+
+ /*************************************************/
+ /* Make sure test region 6 has the correct edges */
+ /*************************************************/
+ edges = get_screen_edges (6);
+ tmp = NULL;
+ tmp = g_list_prepend (tmp, new_screen_edge ( 0, 1200, 1600, 0, bottom));
+ tmp = g_list_prepend (tmp, new_screen_edge ( 0, 40, 1600, 0, top));
+ tmp = g_list_prepend (tmp, new_screen_edge (1600, 40, 0, 1160, right));
+ tmp = g_list_prepend (tmp, new_screen_edge ( 0, 40, 0, 1160, left));
+ verify_edge_lists_are_equal (edges, tmp);
+ meta_rectangle_free_list_and_elements (tmp);
+ meta_rectangle_free_list_and_elements (edges);
+
+ printf ("%s passed.\n", __PRETTY_FUNCTION__);
+}
+
+static void
+test_find_nonintersected_xinerama_edges ()
+{
+ GList* edges;
+ GList* tmp;
+
+ int left = META_DIRECTION_LEFT;
+ int right = META_DIRECTION_RIGHT;
+ int top = META_DIRECTION_TOP;
+ int bottom = META_DIRECTION_BOTTOM;
+
+ /*************************************************************************/
+ /* Make sure test xinerama set 0 for with region 0 has the correct edges */
+ /*************************************************************************/
+ edges = get_xinerama_edges (0, 0);
+ tmp = NULL;
+ verify_edge_lists_are_equal (edges, tmp);
+ meta_rectangle_free_list_and_elements (tmp);
+ meta_rectangle_free_list_and_elements (edges);
+
+ /*************************************************************************/
+ /* Make sure test xinerama set 2 for with region 1 has the correct edges */
+ /*************************************************************************/
+ edges = get_xinerama_edges (2, 1);
+ tmp = NULL;
+ tmp = g_list_prepend (tmp, new_xinerama_edge ( 0, 600, 1600, 0, bottom));
+ tmp = g_list_prepend (tmp, new_xinerama_edge ( 0, 600, 1600, 0, top));
+ verify_edge_lists_are_equal (edges, tmp);
+ meta_rectangle_free_list_and_elements (tmp);
+ meta_rectangle_free_list_and_elements (edges);
+
+ /*************************************************************************/
+ /* Make sure test xinerama set 1 for with region 2 has the correct edges */
+ /*************************************************************************/
+ edges = get_xinerama_edges (1, 2);
+ tmp = NULL;
+ tmp = g_list_prepend (tmp, new_xinerama_edge ( 800, 20, 0, 1080, right));
+ tmp = g_list_prepend (tmp, new_xinerama_edge ( 800, 20, 0, 1180, left));
+#if 0
+ #define FUDGE 50
+ char big_buffer1[(EDGE_LENGTH+2)*FUDGE], big_buffer2[(EDGE_LENGTH+2)*FUDGE];
+ meta_rectangle_edge_list_to_string (edges, "\n ", big_buffer1);
+ meta_rectangle_edge_list_to_string (tmp, "\n ", big_buffer2);
+ printf("Generated edge list:\n %s\nComparison edges list:\n %s\n",
+ big_buffer1, big_buffer2);
+#endif
+ verify_edge_lists_are_equal (edges, tmp);
+ meta_rectangle_free_list_and_elements (tmp);
+ meta_rectangle_free_list_and_elements (edges);
+
+ /*************************************************************************/
+ /* Make sure test xinerama set 3 for with region 3 has the correct edges */
+ /*************************************************************************/
+ edges = get_xinerama_edges (3, 3);
+ tmp = NULL;
+ tmp = g_list_prepend (tmp, new_xinerama_edge ( 900, 600, 700, 0, bottom));
+ tmp = g_list_prepend (tmp, new_xinerama_edge ( 0, 600, 700, 0, bottom));
+ tmp = g_list_prepend (tmp, new_xinerama_edge ( 900, 600, 700, 0, top));
+ tmp = g_list_prepend (tmp, new_xinerama_edge ( 0, 600, 700, 0, top));
+ tmp = g_list_prepend (tmp, new_xinerama_edge ( 800, 675, 0, 425, right));
+ tmp = g_list_prepend (tmp, new_xinerama_edge ( 800, 675, 0, 525, left));
+ verify_edge_lists_are_equal (edges, tmp);
+ meta_rectangle_free_list_and_elements (tmp);
+ meta_rectangle_free_list_and_elements (edges);
+
+ /*************************************************************************/
+ /* Make sure test xinerama set 3 for with region 4 has the correct edges */
+ /*************************************************************************/
+ edges = get_xinerama_edges (3, 4);
+ tmp = NULL;
+ tmp = g_list_prepend (tmp, new_xinerama_edge ( 800, 600, 800, 0, bottom));
+ tmp = g_list_prepend (tmp, new_xinerama_edge ( 800, 600, 800, 0, top));
+ tmp = g_list_prepend (tmp, new_xinerama_edge ( 800, 600, 0, 600, right));
+ verify_edge_lists_are_equal (edges, tmp);
+ meta_rectangle_free_list_and_elements (tmp);
+ meta_rectangle_free_list_and_elements (edges);
+
+ /*************************************************************************/
+ /* Make sure test xinerama set 3 for with region 5has the correct edges */
+ /*************************************************************************/
+ edges = get_xinerama_edges (3, 5);
+ tmp = NULL;
+ verify_edge_lists_are_equal (edges, tmp);
+ meta_rectangle_free_list_and_elements (tmp);
+ meta_rectangle_free_list_and_elements (edges);
+
+ printf ("%s passed.\n", __PRETTY_FUNCTION__);
+}
+
+static void
+test_gravity_resize ()
+{
+ MetaRectangle oldrect, rect, temp;
+
+ rect.x = -500; /* Some random amount not equal to oldrect.x to ensure that
+ * the resize is done with respect to oldrect instead of rect
+ */
+ oldrect = meta_rect ( 50, 300, 250, 400);
+ temp = meta_rect ( 50, 300, 20, 5);
+ meta_rectangle_resize_with_gravity (&oldrect,
+ &rect,
+ NorthWestGravity,
+ 20,
+ 5);
+ g_assert (meta_rectangle_equal (&rect, &temp));
+
+ rect = meta_rect ( 50, 300, 250, 400);
+ temp = meta_rect (165, 300, 20, 5);
+ meta_rectangle_resize_with_gravity (&rect,
+ &rect,
+ NorthGravity,
+ 20,
+ 5);
+ g_assert (meta_rectangle_equal (&rect, &temp));
+
+ rect = meta_rect ( 50, 300, 250, 400);
+ temp = meta_rect (280, 300, 20, 5);
+ meta_rectangle_resize_with_gravity (&rect,
+ &rect,
+ NorthEastGravity,
+ 20,
+ 5);
+ g_assert (meta_rectangle_equal (&rect, &temp));
+
+ rect = meta_rect ( 50, 300, 250, 400);
+ temp = meta_rect ( 50, 695, 50, 5);
+ meta_rectangle_resize_with_gravity (&rect,
+ &rect,
+ SouthWestGravity,
+ 50,
+ 5);
+ g_assert (meta_rectangle_equal (&rect, &temp));
+
+ rect = meta_rect ( 50, 300, 250, 400);
+ temp = meta_rect (150, 695, 50, 5);
+ meta_rectangle_resize_with_gravity (&rect,
+ &rect,
+ SouthGravity,
+ 50,
+ 5);
+ g_assert (meta_rectangle_equal (&rect, &temp));
+
+ rect = meta_rect ( 50, 300, 250, 400);
+ temp = meta_rect (250, 695, 50, 5);
+ meta_rectangle_resize_with_gravity (&rect,
+ &rect,
+ SouthEastGravity,
+ 50,
+ 5);
+ g_assert (meta_rectangle_equal (&rect, &temp));
+
+ rect = meta_rect (167, 738, 237, 843);
+ temp = meta_rect (167, 1113, 832, 93);
+ meta_rectangle_resize_with_gravity (&rect,
+ &rect,
+ WestGravity,
+ 832,
+ 93);
+ g_assert (meta_rectangle_equal (&rect, &temp));
+
+ rect = meta_rect ( 167, 738, 237, 843);
+ temp = meta_rect (-131, 1113, 833, 93);
+ meta_rectangle_resize_with_gravity (&rect,
+ &rect,
+ CenterGravity,
+ 832,
+ 93);
+ g_assert (meta_rectangle_equal (&rect, &temp));
+
+ rect = meta_rect (300, 1000, 400, 200);
+ temp = meta_rect (270, 994, 430, 212);
+ meta_rectangle_resize_with_gravity (&rect,
+ &rect,
+ EastGravity,
+ 430,
+ 211);
+ g_assert (meta_rectangle_equal (&rect, &temp));
+
+ rect = meta_rect (300, 1000, 400, 200);
+ temp = meta_rect (300, 1000, 430, 211);
+ meta_rectangle_resize_with_gravity (&rect,
+ &rect,
+ StaticGravity,
+ 430,
+ 211);
+ g_assert (meta_rectangle_equal (&rect, &temp));
+
+ printf ("%s passed.\n", __PRETTY_FUNCTION__);
+}
+
+static void
+test_find_closest_point_to_line ()
+{
+ double x1, y1, x2, y2, px, py, rx, ry;
+ double answer_x, answer_y;
+
+ x1 = 3.0; y1 = 49.0;
+ x2 = 2.0; y2 = - 1.0;
+ px = -2.6; py = 19.1;
+ answer_x = 2.4; answer_y = 19;
+ meta_rectangle_find_linepoint_closest_to_point (x1, y1,
+ x2, y2,
+ px, py,
+ &rx, &ry);
+ g_assert (rx == answer_x && ry == answer_y);
+
+ /* Special test for x1 == x2, so that slop of line is infinite */
+ x1 = 3.0; y1 = 49.0;
+ x2 = 3.0; y2 = - 1.0;
+ px = -2.6; py = 19.1;
+ answer_x = 3.0; answer_y = 19.1;
+ meta_rectangle_find_linepoint_closest_to_point (x1, y1,
+ x2, y2,
+ px, py,
+ &rx, &ry);
+ g_assert (rx == answer_x && ry == answer_y);
+
+ /* Special test for y1 == y2, so perp line has slope of infinity */
+ x1 = 3.14; y1 = 7.0;
+ x2 = 2.718; y2 = 7.0;
+ px = -2.6; py = 19.1;
+ answer_x = -2.6; answer_y = 7;
+ meta_rectangle_find_linepoint_closest_to_point (x1, y1,
+ x2, y2,
+ px, py,
+ &rx, &ry);
+ g_assert (rx == answer_x && ry == answer_y);
+
+ /* Test when we the point we want to be closest to is actually on the line */
+ x1 = 3.0; y1 = 49.0;
+ x2 = 2.0; y2 = - 1.0;
+ px = 2.4; py = 19.0;
+ answer_x = 2.4; answer_y = 19;
+ meta_rectangle_find_linepoint_closest_to_point (x1, y1,
+ x2, y2,
+ px, py,
+ &rx, &ry);
+ g_assert (rx == answer_x && ry == answer_y);
+
+ printf ("%s passed.\n", __PRETTY_FUNCTION__);
+}
+
+int
+main()
+{
+ init_random_ness ();
+ test_area ();
+ test_intersect ();
+ test_equal ();
+ test_overlap_funcs ();
+ test_basic_fitting ();
+
+ test_regions_okay ();
+ test_region_fitting ();
+
+ test_clamping_to_region ();
+ test_clipping_to_region ();
+ test_shoving_into_region ();
+
+ /* And now the functions dealing with edges more than boxes */
+ test_find_onscreen_edges ();
+ test_find_nonintersected_xinerama_edges ();
+
+ /* And now the misfit functions that don't quite fit in anywhere else... */
+ test_gravity_resize ();
+ test_find_closest_point_to_line ();
+
+ printf ("All tests passed.\n");
+ return 0;
+}
diff --git a/src/util.c b/src/util.c
index fbdfa24..a7d03ea 100644
--- a/src/util.c
+++ b/src/util.c
@@ -29,6 +29,7 @@
#include <unistd.h>
#include <errno.h>
#include <string.h>
+#include <X11/Xutil.h> /* Just for the definition of the various gravities */
#ifdef HAVE_BACKTRACE
#include <execinfo.h>
@@ -464,3 +465,44 @@ meta_unsigned_long_hash (gconstpointer v)
return val;
#endif
}
+
+const char*
+meta_gravity_to_string (int gravity)
+{
+ switch (gravity)
+ {
+ case NorthWestGravity:
+ return "NorthWestGravity";
+ break;
+ case NorthGravity:
+ return "NorthGravity";
+ break;
+ case NorthEastGravity:
+ return "NorthEastGravity";
+ break;
+ case WestGravity:
+ return "WestGravity";
+ break;
+ case CenterGravity:
+ return "CenterGravity";
+ break;
+ case EastGravity:
+ return "EastGravity";
+ break;
+ case SouthWestGravity:
+ return "SouthWestGravity";
+ break;
+ case SouthGravity:
+ return "SouthGravity";
+ break;
+ case SouthEastGravity:
+ return "SouthEastGravity";
+ break;
+ case StaticGravity:
+ return "StaticGravity";
+ break;
+ default:
+ return "NorthWestGravity";
+ break;
+ }
+}
diff --git a/src/util.h b/src/util.h
index 86e8dfd..67c7b01 100644
--- a/src/util.h
+++ b/src/util.h
@@ -85,6 +85,8 @@ guint meta_unsigned_long_hash (gconstpointer v);
void meta_print_backtrace (void);
+const char* meta_gravity_to_string (int gravity);
+
#include <libintl.h>
#define _(x) dgettext (GETTEXT_PACKAGE, x)
#define N_(x) x
diff --git a/src/window.c b/src/window.c
index 4624157..6e0ab93 100644
--- a/src/window.c
+++ b/src/window.c
@@ -24,6 +24,7 @@
#include <config.h>
#include "window.h"
+#include "edge-resistance.h"
#include "util.h"
#include "frame.h"
#include "errors.h"
@@ -48,13 +49,6 @@
#include <X11/extensions/shape.h>
#endif
-typedef enum
-{
- META_IS_CONFIGURE_REQUEST = 1 << 0,
- META_DO_GRAVITY_ADJUST = 1 << 1,
- META_USER_MOVE_RESIZE = 1 << 2
-} MetaMoveResizeFlags;
-
static int destroying_windows_disallowed = 0;
@@ -95,6 +89,19 @@ static void ensure_mru_position_after (MetaWindow *window,
void meta_window_move_resize_now (MetaWindow *window);
+static void update_move (MetaWindow *window,
+ gboolean snap,
+ int x,
+ int y);
+static gboolean update_move_timeout (gpointer data);
+static void update_resize (MetaWindow *window,
+ gboolean snap,
+ int x,
+ int y,
+ gboolean force);
+static gboolean update_resize_timeout (gpointer data);
+
+
/* FIXME we need an abstraction that covers all these queues. */
void meta_window_unqueue_calc_showing (MetaWindow *window);
@@ -395,7 +402,6 @@ meta_window_new_with_attrs (MetaDisplay *display,
window->has_shape = has_shape;
- /* Remember this rect is the actual window size */
window->rect.x = attrs->x;
window->rect.y = attrs->y;
window->rect.width = attrs->width;
@@ -431,9 +437,13 @@ meta_window_new_with_attrs (MetaDisplay *display,
window->user_has_move_resized = FALSE;
- window->maximized = FALSE;
- window->maximize_after_placement = FALSE;
+ window->maximized_horizontally = FALSE;
+ window->maximized_vertically = FALSE;
+ window->maximize_horizontally_after_placement = FALSE;
+ window->maximize_vertically_after_placement = FALSE;
window->fullscreen = FALSE;
+ window->require_fully_onscreen = TRUE;
+ window->require_on_single_xinerama = TRUE;
window->on_all_workspaces = FALSE;
window->shaded = FALSE;
window->initially_iconic = FALSE;
@@ -688,9 +698,11 @@ meta_window_new_with_attrs (MetaDisplay *display,
* passing TRUE for is_configure_request, ICCCM says
* initial map is handled same as configure request
*/
+ MetaMoveResizeFlags flags =
+ META_IS_CONFIGURE_REQUEST | META_IS_MOVE_ACTION | META_IS_RESIZE_ACTION;
meta_window_move_resize_internal (window,
- META_IS_CONFIGURE_REQUEST,
- NorthWestGravity,
+ flags,
+ window->size_hints.win_gravity,
window->size_hints.x,
window->size_hints.y,
window->size_hints.width,
@@ -769,8 +781,10 @@ meta_window_apply_session_info (MetaWindow *window,
info->maximized, window->desc);
if (window->has_maximize_func && info->maximized)
- {
- meta_window_maximize (window);
+ {
+ meta_window_maximize (window,
+ META_MAXIMIZE_HORIZONTAL |
+ META_MAXIMIZE_VERTICAL);
if (info->saved_rect_set)
{
@@ -866,9 +880,11 @@ meta_window_apply_session_info (MetaWindow *window,
"Restoring pos %d,%d size %d x %d for %s\n",
x, y, w, h, window->desc);
+ MetaMoveResizeFlags flags =
+ META_DO_GRAVITY_ADJUST | META_IS_MOVE_ACTION | META_IS_RESIZE_ACTION;
meta_window_move_resize_internal (window,
- META_DO_GRAVITY_ADJUST,
- NorthWestGravity,
+ flags,
+ window->size_hints.win_gravity,
x, y, w, h);
}
}
@@ -961,8 +977,9 @@ meta_window_free (MetaWindow *window)
if (window->display->focus_window == window)
window->display->focus_window = NULL;
- if (window->maximized)
- meta_window_unmaximize (window);
+ if (window->maximized_horizontally || window->maximized_vertically)
+ meta_window_unmaximize (window,
+ META_MAXIMIZE_HORIZONTAL | META_MAXIMIZE_VERTICAL);
meta_window_unqueue_calc_showing (window);
meta_window_unqueue_move_resize (window);
@@ -1130,10 +1147,13 @@ set_net_wm_state (MetaWindow *window)
data[i] = window->display->atom_net_wm_state_skip_taskbar;
++i;
}
- if (window->maximized)
+ if (window->maximized_horizontally)
{
data[i] = window->display->atom_net_wm_state_maximized_horz;
++i;
+ }
+ if (window->maximized_vertically)
+ {
data[i] = window->display->atom_net_wm_state_maximized_vert;
++i;
}
@@ -1326,8 +1346,8 @@ implement_showing (MetaWindow *window,
/* just animate into the corner somehow - maybe
* not a good idea...
*/
- icon_rect.x = window->screen->width;
- icon_rect.y = window->screen->height;
+ icon_rect.x = window->screen->rect.width;
+ icon_rect.y = window->screen->rect.height;
icon_rect.width = 1;
icon_rect.height = 1;
}
@@ -1984,53 +2004,93 @@ meta_window_unminimize (MetaWindow *window)
static void
meta_window_save_rect (MetaWindow *window)
{
- if (!(window->maximized || window->fullscreen))
+ if (!(META_WINDOW_MAXIMIZED (window) || window->fullscreen))
{
/* save size/pos as appropriate args for move_resize */
- window->saved_rect = window->rect;
- if (window->frame)
+ if (!window->maximized_horizontally)
{
- window->saved_rect.x += window->frame->rect.x;
- window->saved_rect.y += window->frame->rect.y;
+ 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)
+ {
+ window->saved_rect.y = window->rect.y;
+ window->saved_rect.height = window->rect.height;
+ if (window->frame)
+ window->saved_rect.y += window->frame->rect.y;
}
}
}
void
-meta_window_maximize_internal (MetaWindow *window,
- MetaRectangle *saved_rect)
+meta_window_maximize_internal (MetaWindow *window,
+ MetaMaximizeFlags directions,
+ MetaRectangle *saved_rect)
{
+ /* At least one of the two directions ought to be set */
+ gboolean maximize_horizontally, maximize_vertically;
+ maximize_horizontally = directions & META_MAXIMIZE_HORIZONTAL;
+ maximize_vertically = directions & META_MAXIMIZE_VERTICAL;
+ g_assert (maximize_horizontally || maximize_vertically);
+
meta_topic (META_DEBUG_WINDOW_OPS,
- "Maximizing %s\n", window->desc);
+ "Maximizing %s%s\n",
+ window->desc,
+ maximize_horizontally && maximize_vertically ? "" :
+ maximize_horizontally ? " horizontally" :
+ maximize_vertically ? " vertically" : "BUGGGGG");
if (saved_rect != NULL)
window->saved_rect = *saved_rect;
else
meta_window_save_rect (window);
- window->maximized = TRUE;
+ window->maximized_horizontally =
+ window->maximized_horizontally || maximize_horizontally;
+ window->maximized_vertically =
+ window->maximized_vertically || maximize_vertically;
recalc_window_features (window);
set_net_wm_state (window);
}
void
-meta_window_maximize (MetaWindow *window)
+meta_window_maximize (MetaWindow *window,
+ MetaMaximizeFlags directions)
{
- if (!window->maximized)
+ /* At least one of the two directions ought to be set */
+ gboolean maximize_horizontally, maximize_vertically;
+ maximize_horizontally = directions & META_MAXIMIZE_HORIZONTAL;
+ maximize_vertically = directions & META_MAXIMIZE_VERTICAL;
+ g_assert (maximize_horizontally || maximize_vertically);
+
+ /* Only do something if the window isn't already maximized in the
+ * given direction(s).
+ */
+ if ((maximize_horizontally && !window->maximized_horizontally) ||
+ (maximize_vertically && !window->maximized_vertically))
{
- if (window->shaded)
+ if (window->shaded && maximize_vertically)
meta_window_unshade (window);
/* if the window hasn't been placed yet, we'll maximize it then
*/
if (!window->placed)
{
- window->maximize_after_placement = TRUE;
+ window->maximize_horizontally_after_placement =
+ window->maximize_horizontally_after_placement ||
+ maximize_horizontally;
+ window->maximize_vertically_after_placement =
+ window->maximize_vertically_after_placement ||
+ maximize_vertically;
return;
}
- meta_window_maximize_internal (window, NULL);
+ meta_window_maximize_internal (window,
+ directions,
+ NULL);
/* move_resize with new maximization constraints
*/
@@ -2039,14 +2099,32 @@ meta_window_maximize (MetaWindow *window)
}
void
-meta_window_unmaximize (MetaWindow *window)
+meta_window_unmaximize (MetaWindow *window,
+ MetaMaximizeFlags directions)
{
- if (window->maximized)
+ /* At least one of the two directions ought to be set */
+ gboolean unmaximize_horizontally, unmaximize_vertically;
+ unmaximize_horizontally = directions & META_MAXIMIZE_HORIZONTAL;
+ unmaximize_vertically = directions & META_MAXIMIZE_VERTICAL;
+ g_assert (unmaximize_horizontally || unmaximize_vertically);
+
+ /* Only do something if the window isn't already maximized in the
+ * given direction(s).
+ */
+ if ((unmaximize_horizontally && window->maximized_horizontally) ||
+ (unmaximize_vertically && window->maximized_vertically))
{
meta_topic (META_DEBUG_WINDOW_OPS,
- "Unmaximizing %s\n", window->desc);
+ "Unmaximizing %s%s\n",
+ window->desc,
+ unmaximize_horizontally && unmaximize_vertically ? "" :
+ unmaximize_horizontally ? " horizontally" :
+ unmaximize_vertically ? " vertically" : "BUGGGGG");
- window->maximized = FALSE;
+ window->maximized_horizontally =
+ window->maximized_horizontally && !unmaximize_horizontally;
+ window->maximized_vertically =
+ window->maximized_vertically && !unmaximize_vertically;
/* When we unmaximize, if we're doing a mouse move also we could
* get the window suddenly jumping to the upper left corner of
@@ -2283,19 +2361,17 @@ meta_window_activate (MetaWindow *window,
meta_window_focus (window, timestamp);
}
-/* returns values suitable for meta_window_move
- * i.e. static gravity
+/* Manually fix all the weirdness explained in the big comment at the
+ * beginning of meta_window_move_resize_internal() giving positions
+ * expected by meta_window_constrain (i.e. positions & sizes of the
+ * internal or client window).
*/
static void
adjust_for_gravity (MetaWindow *window,
MetaFrameGeometry *fgeom,
gboolean coords_assume_border,
- int x,
- int y,
- int width,
- int height,
- int *xp,
- int *yp)
+ int gravity,
+ MetaRectangle *rect)
{
int ref_x, ref_y;
int bw;
@@ -2311,15 +2387,15 @@ adjust_for_gravity (MetaWindow *window,
{
child_x = fgeom->left_width;
child_y = fgeom->top_height;
- frame_width = child_x + width + fgeom->right_width;
- frame_height = child_y + height + fgeom->bottom_height;
+ frame_width = child_x + rect->width + fgeom->right_width;
+ frame_height = child_y + rect->height + fgeom->bottom_height;
}
else
{
child_x = 0;
child_y = 0;
- frame_width = width;
- frame_height = height;
+ frame_width = rect->width;
+ frame_height = rect->height;
}
/* We're computing position to pass to window_move, which is
@@ -2329,93 +2405,93 @@ adjust_for_gravity (MetaWindow *window,
* their formulas assume we're honoring the border width, rather
* than compensating for having turned it off)
*/
- switch (window->size_hints.win_gravity)
+ switch (gravity)
{
case NorthWestGravity:
- ref_x = x;
- ref_y = y;
+ ref_x = rect->x;
+ ref_y = rect->y;
break;
case NorthGravity:
- ref_x = x + width / 2 + bw;
- ref_y = y;
+ ref_x = rect->x + rect->width / 2 + bw;
+ ref_y = rect->y;
break;
case NorthEastGravity:
- ref_x = x + width + bw * 2;
- ref_y = y;
+ ref_x = rect->x + rect->width + bw * 2;
+ ref_y = rect->y;
break;
case WestGravity:
- ref_x = x;
- ref_y = y + height / 2 + bw;
+ ref_x = rect->x;
+ ref_y = rect->y + rect->height / 2 + bw;
break;
case CenterGravity:
- ref_x = x + width / 2 + bw;
- ref_y = y + height / 2 + bw;
+ ref_x = rect->x + rect->width / 2 + bw;
+ ref_y = rect->y + rect->height / 2 + bw;
break;
case EastGravity:
- ref_x = x + width + bw * 2;
- ref_y = y + height / 2 + bw;
+ ref_x = rect->x + rect->width + bw * 2;
+ ref_y = rect->y + rect->height / 2 + bw;
break;
case SouthWestGravity:
- ref_x = x;
- ref_y = y + height + bw * 2;
+ ref_x = rect->x;
+ ref_y = rect->y + rect->height + bw * 2;
break;
case SouthGravity:
- ref_x = x + width / 2 + bw;
- ref_y = y + height + bw * 2;
+ ref_x = rect->x + rect->width / 2 + bw;
+ ref_y = rect->y + rect->height + bw * 2;
break;
case SouthEastGravity:
- ref_x = x + width + bw * 2;
- ref_y = y + height + bw * 2;
+ ref_x = rect->x + rect->width + bw * 2;
+ ref_y = rect->y + rect->height + bw * 2;
break;
case StaticGravity:
default:
- ref_x = x;
- ref_y = y;
+ ref_x = rect->x;
+ ref_y = rect->y;
break;
}
- switch (window->size_hints.win_gravity)
+ switch (gravity)
{
case NorthWestGravity:
- *xp = ref_x + child_x;
- *yp = ref_y + child_y;
+ rect->x = ref_x + child_x;
+ rect->y = ref_y + child_y;
break;
case NorthGravity:
- *xp = ref_x - frame_width / 2 + child_x;
- *yp = ref_y + child_y;
+ rect->x = ref_x - frame_width / 2 + child_x;
+ rect->y = ref_y + child_y;
break;
case NorthEastGravity:
- *xp = ref_x - frame_width + child_x;
- *yp = ref_y + child_y;
+ rect->x = ref_x - frame_width + child_x;
+ rect->y = ref_y + child_y;
break;
case WestGravity:
- *xp = ref_x + child_x;
- *yp = ref_y - frame_height / 2 + child_y;
+ rect->x = ref_x + child_x;
+ rect->y = ref_y - frame_height / 2 + child_y;
break;
case CenterGravity:
- *xp = ref_x - frame_width / 2 + child_x;
- *yp = ref_y - frame_height / 2 + child_y;
+ rect->x = ref_x - frame_width / 2 + child_x;
+ rect->y = ref_y - frame_height / 2 + child_y;
break;
case EastGravity:
- *xp = ref_x - frame_width + child_x;
- *yp = ref_y - frame_height / 2 + child_y;
+ rect->x = ref_x - frame_width + child_x;
+ rect->y = ref_y - frame_height / 2 + child_y;
break;
case SouthWestGravity:
- *xp = ref_x + child_x;
- *yp = ref_y - frame_height + child_y;
+ rect->x = ref_x + child_x;
+ rect->y = ref_y - frame_height + child_y;
break;
case SouthGravity:
- *xp = ref_x - frame_width / 2 + child_x;
- *yp = ref_y - frame_height + child_y;
+ rect->x = ref_x - frame_width / 2 + child_x;
+ rect->y = ref_y - frame_height + child_y;
break;
case SouthEastGravity:
- *xp = ref_x - frame_width + child_x;
- *yp = ref_y - frame_height + child_y;
+ rect->x = ref_x - frame_width + child_x;
+ rect->y = ref_y - frame_height + child_y;
break;
case StaticGravity:
default:
- *xp = ref_x;
- *yp = ref_y;
+ rect->x = ref_x;
+ rect->y = ref_y;
break;
}
}
@@ -2426,47 +2502,6 @@ static_gravity_works (MetaDisplay *display)
return display->static_gravity_works;
}
-static void
-get_mouse_deltas_for_resize (MetaWindow *window,
- int resize_gravity,
- int w,
- int h,
- int *x_delta,
- int *y_delta)
-{
- switch (meta_x_direction_from_gravity (resize_gravity))
- {
- case META_RESIZE_LEFT_OR_TOP:
- *x_delta = window->rect.width - w;
- break;
- case META_RESIZE_RIGHT_OR_BOTTOM:
- *x_delta = w - window->rect.width;
- break;
- case META_RESIZE_CENTER:
- /* FIXME this implies that with center gravity you have to grow
- * in increments of two
- */
- *x_delta = (w - window->rect.width) / 2;
- break;
- }
-
- switch (meta_y_direction_from_gravity (resize_gravity))
- {
- case META_RESIZE_LEFT_OR_TOP:
- *y_delta = window->rect.height - h;
- break;
- case META_RESIZE_RIGHT_OR_BOTTOM:
- *y_delta = h - window->rect.height;
- break;
- case META_RESIZE_CENTER:
- /* FIXME this implies that with center gravity you have to grow
- * in increments of two
- */
- *y_delta = (h - window->rect.height) / 2;
- break;
- }
-}
-
#ifdef HAVE_XSYNC
static void
send_sync_request (MetaWindow *window)
@@ -2506,6 +2541,41 @@ meta_window_move_resize_internal (MetaWindow *window,
int w,
int h)
{
+ /* meta_window_move_resize_internal gets called with very different
+ * meanings for root_x_nw and root_y_nw. w & h are always the area of
+ * the inner or client window (i.e. excluding the frame) and the
+ * resize_gravity is always the gravity associated with the resize or
+ * move_resize request (the gravity is ignored for move-only operations).
+ * But the location is different because of how this function gets
+ * called; note that in all cases what we want to find out is the upper
+ * left corner of the position of the inner window:
+ *
+ * Case | Called from (flags; resize_gravity)
+ * -----+-----------------------------------------------
+ * 1 | A resize only ConfigureRequest
+ * 1 | meta_window_resize
+ * 1 | meta_window_resize_with_gravity
+ * 2 | New window
+ * 2 | Session restore
+ * 2 | A not-resize-only ConfigureRequest
+ * 3 | meta_window_move
+ * 3 | meta_window_move_resize
+ * 4 | various functions via handle_net_moveresize_window() in display.c
+ *
+ * For each of the cases, root_x_nw and root_y_nw must be treated as follows:
+ *
+ * (1) They should be entirely ignored; instead the previous position
+ * and size of the window should be resized according to the given
+ * gravity in order to determine the new position of the window.
+ * (2) Needs to be fixed up by adjust_for_gravity() as these
+ * coordinates are relative to some corner or side of the outer
+ * window (except for the case of StaticGravity) and we want to
+ * know the location of the upper left corner of the inner window.
+ * (3) These values are already the desired positon of the NW corner
+ * of the inner window
+ * (4) The place that calls this function this way must be fixed; it is
+ * wrong.
+ */
XWindowChanges values;
unsigned int mask;
gboolean need_configure_notify;
@@ -2528,14 +2598,15 @@ meta_window_move_resize_internal (MetaWindow *window,
*/
int client_move_x;
int client_move_y;
- int x_delta;
- int y_delta;
MetaRectangle new_rect;
MetaRectangle old_rect;
is_configure_request = (flags & META_IS_CONFIGURE_REQUEST) != 0;
do_gravity_adjust = (flags & META_DO_GRAVITY_ADJUST) != 0;
- is_user_action = (flags & META_USER_MOVE_RESIZE) != 0;
+ is_user_action = (flags & META_IS_USER_ACTION) != 0;
+
+ /* The action has to be a move or a resize or both... */
+ g_assert (flags & (META_IS_MOVE_ACTION | META_IS_RESIZE_ACTION));
/* We don't need it in the idle queue anymore. */
meta_window_unqueue_move_resize (window);
@@ -2553,8 +2624,30 @@ meta_window_move_resize_internal (MetaWindow *window,
if (window->frame)
meta_frame_calc_geometry (window->frame,
&fgeom);
-
- if (is_configure_request || do_gravity_adjust)
+
+ new_rect.x = root_x_nw;
+ new_rect.y = root_y_nw;
+ new_rect.width = w;
+ new_rect.height = h;
+
+ /* If this is a resize only, the position should be ignored and
+ * instead obtained by resizing the old rectangle according to the
+ * relevant gravity.
+ */
+ if ((flags & (META_IS_MOVE_ACTION | META_IS_RESIZE_ACTION)) ==
+ META_IS_RESIZE_ACTION)
+ {
+ meta_rectangle_resize_with_gravity (&old_rect,
+ &new_rect,
+ resize_gravity,
+ new_rect.width,
+ new_rect.height);
+
+ meta_topic (META_DEBUG_GEOMETRY,
+ "Compensated for gravity in resize action; new pos %d,%d\n",
+ new_rect.x, new_rect.y);
+ }
+ else if (is_configure_request || do_gravity_adjust)
{
adjust_for_gravity (window,
window->frame ? &fgeom : NULL,
@@ -2562,29 +2655,20 @@ meta_window_move_resize_internal (MetaWindow *window,
* the border width existed
*/
is_configure_request,
- root_x_nw,
- root_y_nw,
- w, h,
- &root_x_nw,
- &root_y_nw);
-
+ window->size_hints.win_gravity,
+ &new_rect);
+
meta_topic (META_DEBUG_GEOMETRY,
- "Compensated position for gravity, new pos %d,%d\n",
- root_x_nw, root_y_nw);
+ "Compensated for configure_request/do_gravity_adjust needing "
+ "weird positioning; new pos %d,%d\n",
+ new_rect.x, new_rect.y);
}
- get_mouse_deltas_for_resize (window, resize_gravity, w, h,
- &x_delta, &y_delta);
-
meta_window_constrain (window,
window->frame ? &fgeom : NULL,
+ flags,
+ resize_gravity,
&old_rect,
- root_x_nw - old_rect.x,
- root_y_nw - old_rect.y,
- meta_x_direction_from_gravity (resize_gravity),
- x_delta,
- meta_y_direction_from_gravity (resize_gravity),
- y_delta,
&new_rect);
w = new_rect.width;
@@ -2908,8 +2992,10 @@ meta_window_resize (MetaWindow *window,
meta_window_get_position (window, &x, &y);
+ MetaMoveResizeFlags flags =
+ (user_op ? META_IS_USER_ACTION : 0) | META_IS_RESIZE_ACTION;
meta_window_move_resize_internal (window,
- user_op ? META_USER_MOVE_RESIZE : 0,
+ flags,
NorthWestGravity,
x, y, w, h);
}
@@ -2920,8 +3006,10 @@ meta_window_move (MetaWindow *window,
int root_x_nw,
int root_y_nw)
{
+ MetaMoveResizeFlags flags =
+ (user_op ? META_IS_USER_ACTION : 0) | META_IS_MOVE_ACTION;
meta_window_move_resize_internal (window,
- user_op ? META_USER_MOVE_RESIZE : 0,
+ flags,
NorthWestGravity,
root_x_nw, root_y_nw,
window->rect.width,
@@ -2936,8 +3024,11 @@ meta_window_move_resize (MetaWindow *window,
int w,
int h)
{
+ MetaMoveResizeFlags flags =
+ (user_op ? META_IS_USER_ACTION : 0) |
+ META_IS_MOVE_ACTION | META_IS_RESIZE_ACTION;
meta_window_move_resize_internal (window,
- user_op ? META_USER_MOVE_RESIZE : 0,
+ flags,
NorthWestGravity,
root_x_nw, root_y_nw,
w, h);
@@ -2954,8 +3045,10 @@ meta_window_resize_with_gravity (MetaWindow *window,
meta_window_get_position (window, &x, &y);
+ MetaMoveResizeFlags flags =
+ (user_op ? META_IS_USER_ACTION : 0) | META_IS_RESIZE_ACTION;
meta_window_move_resize_internal (window,
- user_op ? META_USER_MOVE_RESIZE : 0,
+ flags,
gravity,
x, y, w, h);
}
@@ -2981,94 +3074,6 @@ meta_window_move_resize_now (MetaWindow *window)
window->rect.height);
}
-static void
-check_maximize_to_work_area (MetaWindow *window,
- const MetaRectangle *work_area)
-{
- /* If we now fill the screen, maximize.
- * the point here is that fill horz + fill vert = maximized
- */
- MetaRectangle rect;
-
- if (!window->has_maximize_func)
- return;
-
- meta_window_get_outer_rect (window, &rect);
-
- /* The logic in this if is basically:
- * if window's left side is at far left or offscreen AND
- * window's bottom side is far top or offscreen AND
- * window's right side is at far right or offscreen AND
- * window's bottom side is at far bottom or offscreen
- * except that we maximize windows with a size increment hint (e.g.
- * terminals) should be maximized if they are "sufficiently close"
- * to the above criteria...
- */
- if ( rect.x <= work_area->x &&
- rect.y <= work_area->y &&
- (((work_area->width + work_area->x) - (rect.width + rect.x)) <
- window->size_hints.width_inc) &&
- (((work_area->height + work_area->y) - (rect.height + rect.y)) <
- window->size_hints.height_inc) )
- meta_window_maximize (window);
-}
-
-void
-meta_window_fill_horizontal (MetaWindow *window)
-{
- MetaRectangle work_area;
- int x, y, w, h;
-
- meta_window_get_user_position (window, &x, &y);
-
- w = window->rect.width;
- h = window->rect.height;
-
- meta_window_get_work_area_current_xinerama (window, &work_area);
-
- x = work_area.x;
- w = work_area.width;
-
- if (window->frame != NULL)
- {
- x += window->frame->child_x;
- w -= (window->frame->child_x + window->frame->right_width);
- }
-
- meta_window_move_resize (window, TRUE,
- x, y, w, h);
-
- check_maximize_to_work_area (window, &work_area);
-}
-
-void
-meta_window_fill_vertical (MetaWindow *window)
-{
- MetaRectangle work_area;
- int x, y, w, h;
-
- meta_window_get_user_position (window, &x, &y);
-
- w = window->rect.width;
- h = window->rect.height;
-
- meta_window_get_work_area_current_xinerama (window, &work_area);
-
- y = work_area.y;
- h = work_area.height;
-
- if (window->frame != NULL)
- {
- y += window->frame->child_y;
- h -= (window->frame->child_y + window->frame->bottom_height);
- }
-
- meta_window_move_resize (window, TRUE,
- x, y, w, h);
-
- check_maximize_to_work_area (window, &work_area);
-}
-
static guint move_resize_idle = 0;
static GSList *move_resize_pending = NULL;
@@ -3380,12 +3385,9 @@ meta_window_begin_wireframe (MetaWindow *window)
int display_width, display_height;
window->display->grab_wireframe_rect = window->rect;
-
- if (window->frame)
- {
- window->display->grab_wireframe_rect.x += window->frame->rect.x;
- window->display->grab_wireframe_rect.y += window->frame->rect.y;
- }
+ meta_window_get_position (window,
+ &window->display->grab_wireframe_rect.x,
+ &window->display->grab_wireframe_rect.y);
meta_window_get_xor_rect (window, &window->display->grab_wireframe_rect,
&new_xor);
@@ -4046,15 +4048,28 @@ meta_window_configure_request (MetaWindow *window,
* don't make too much sense. I think I am doing the math in a couple
* places and could do it in only one function, and remove some of the
* move_resize_internal arguments.
+ *
+ * UPDATE (2005-09-17): See the huge comment at the beginning of
+ * meta_window_move_resize_internal() which explains why the current
+ * setup requires the only_resize thing. Yeah, it'd be much better to
+ * have a different setup for meta_window_move_resize_internal()...
*/
- meta_window_move_resize_internal (window, META_IS_CONFIGURE_REQUEST,
- only_resize ?
- window->size_hints.win_gravity : NorthWestGravity,
- window->size_hints.x,
- window->size_hints.y,
- window->size_hints.width,
- window->size_hints.height);
+ MetaMoveResizeFlags flags =
+ META_IS_CONFIGURE_REQUEST;
+ if (event->xconfigurerequest.value_mask & (CWX | CWY))
+ flags |= META_IS_MOVE_ACTION;
+ if (event->xconfigurerequest.value_mask & (CWWidth | CWHeight))
+ flags |= META_IS_RESIZE_ACTION;
+
+ if (flags & (META_IS_MOVE_ACTION | META_IS_RESIZE_ACTION))
+ meta_window_move_resize_internal (window,
+ flags,
+ window->size_hints.win_gravity,
+ window->size_hints.x,
+ window->size_hints.y,
+ window->size_hints.width,
+ window->size_hints.height);
/* Handle stacking. We only handle raises/lowers, mostly because
* stack.c really can't deal with anything else. I guess we'll fix
@@ -4259,18 +4274,31 @@ meta_window_client_message (MetaWindow *window,
}
if (first == display->atom_net_wm_state_maximized_horz ||
- second == display->atom_net_wm_state_maximized_horz ||
- first == display->atom_net_wm_state_maximized_vert ||
+ second == display->atom_net_wm_state_maximized_horz)
+ {
+ gboolean max;
+
+ max = (action == _NET_WM_STATE_ADD ||
+ (action == _NET_WM_STATE_TOGGLE &&
+ !window->maximized_horizontally));
+ if (max && window->has_maximize_func)
+ meta_window_maximize (window, META_MAXIMIZE_HORIZONTAL);
+ else
+ meta_window_unmaximize (window, META_MAXIMIZE_HORIZONTAL);
+ }
+
+ if (first == display->atom_net_wm_state_maximized_vert ||
second == display->atom_net_wm_state_maximized_vert)
{
gboolean max;
max = (action == _NET_WM_STATE_ADD ||
- (action == _NET_WM_STATE_TOGGLE && !window->maximized));
+ (action == _NET_WM_STATE_TOGGLE &&
+ !window->maximized_vertically));
if (max && window->has_maximize_func)
- meta_window_maximize (window);
+ meta_window_maximize (window, META_MAXIMIZE_VERTICAL);
else
- meta_window_unmaximize (window);
+ meta_window_unmaximize (window, META_MAXIMIZE_VERTICAL);
}
if (first == display->atom_net_wm_state_modal ||
@@ -4886,7 +4914,8 @@ update_net_wm_state (MetaWindow *window)
Atom *atoms;
window->shaded = FALSE;
- window->maximized = FALSE;
+ window->maximized_horizontally = FALSE;
+ window->maximized_vertically = FALSE;
window->wm_state_modal = FALSE;
window->wm_state_skip_taskbar = FALSE;
window->wm_state_skip_pager = FALSE;
@@ -4906,9 +4935,9 @@ update_net_wm_state (MetaWindow *window)
if (atoms[i] == window->display->atom_net_wm_state_shaded)
window->shaded = TRUE;
else if (atoms[i] == window->display->atom_net_wm_state_maximized_horz)
- window->maximize_after_placement = TRUE;
+ window->maximize_horizontally_after_placement = TRUE;
else if (atoms[i] == window->display->atom_net_wm_state_maximized_vert)
- window->maximize_after_placement = TRUE;
+ window->maximize_vertically_after_placement = TRUE;
else if (atoms[i] == window->display->atom_net_wm_state_modal)
window->wm_state_modal = TRUE;
else if (atoms[i] == window->display->atom_net_wm_state_skip_taskbar)
@@ -5526,15 +5555,6 @@ meta_window_update_struts (MetaWindow *window)
MetaRectangle new_top;
MetaRectangle new_bottom;
- /**
- * This gap must be kept to at least 75 pixels, since otherwise
- * struts on opposite sides of the screen left/right could interfere
- * in each other in a way that makes it so there is no feasible
- * solution to the constraint satisfaction problem. See
- * constraints.c.
- */
-#define MIN_EMPTY (76)
-
meta_verbose ("Updating struts for %s\n", window->desc);
if (window->struts)
@@ -5551,25 +5571,19 @@ meta_window_update_struts (MetaWindow *window)
}
new_has_struts = FALSE;
+ new_left = window->screen->rect;
new_left.width = 0;
- new_left.x = 0;
- new_left.y = 0;
- new_left.height = window->screen->height;
+ new_right = window->screen->rect;
new_right.width = 0;
- new_right.x = window->screen->width;
- new_right.y = 0;
- new_right.height = window->screen->height;
+ new_right.x = window->screen->rect.width;
+ new_top = window->screen->rect;
new_top.height = 0;
- new_top.y = 0;
- new_top.x = 0;
- new_top.width = window->screen->width;
+ new_bottom = window->screen->rect;
new_bottom.height = 0;
- new_bottom.y = window->screen->height;
- new_bottom.x = 0;
- new_bottom.width = window->screen->width;
+ new_bottom.y = window->screen->rect.height;
if (meta_prop_get_cardinal_list (window->display,
window->xwindow,
@@ -5583,20 +5597,13 @@ meta_window_update_struts (MetaWindow *window)
}
else
{
- int gap;
- gap = window->screen->width - struts[0] - struts[1];
- gap -= MIN_EMPTY;
new_has_struts = TRUE;
- new_left.width = (int) struts[0] + MIN (0, gap/2);
- new_right.width = (int) struts[1] + MIN (0, gap/2);
- gap = window->screen->height - struts[2] - struts[3];
- gap -= MIN_EMPTY;
- new_top.height = (int)struts[2] + MIN (0, gap/2);
- new_bottom.height = (int)struts[3] + MIN (0, gap/2);
- new_right.x = window->screen->width -
- new_right.width;
- new_bottom.y = window->screen->height -
- new_bottom.height;
+ new_left.width = (int) struts[0];
+ new_right.width = (int) struts[1];
+ new_top.height = (int)struts[2];
+ new_bottom.height = (int)struts[3];
+ new_right.x = window->screen->rect.width - new_right.width;
+ new_bottom.y = window->screen->rect.height - new_bottom.height;
new_left.y = struts[4];
new_left.height = struts[5] - new_left.y + 1;
new_right.y = struts[6];
@@ -5636,22 +5643,13 @@ meta_window_update_struts (MetaWindow *window)
}
else
{
- int gap;
- gap = window->screen->width - struts[0] - struts[1];
- gap -= MIN_EMPTY;
new_has_struts = TRUE;
- new_left.width = (int) struts[0] + MIN (0, gap/2);
- new_right.width = (int) struts[1] + MIN (0, gap/2);
- gap = window->screen->height - struts[2] - struts[3];
- gap -= MIN_EMPTY;
- new_top.height = (int)struts[2] + MIN (0, gap/2);
- new_bottom.height = (int)struts[3] + MIN (0, gap/2);
- new_left.x = 0;
- new_right.x = window->screen->width -
- new_right.width;
- new_top.y = 0;
- new_bottom.y = window->screen->height -
- new_bottom.height;
+ new_left.width = (int) struts[0];
+ new_right.width = (int) struts[1];
+ new_top.height = (int)struts[2];
+ new_bottom.height = (int)struts[3];
+ new_right.x = window->screen->rect.width - new_right.width;
+ new_bottom.y = window->screen->rect.height - new_bottom.height;
meta_verbose ("_NET_WM_STRUT struts %d %d %d %d for window %s\n",
new_left.width,
@@ -5933,8 +5931,8 @@ recalc_window_features (MetaWindow *window)
* is entire screen size (kind of broken, because we
* actually fullscreen to xinerama head size not screen size)
*/
- if (window->size_hints.min_width == window->screen->width &&
- window->size_hints.min_height == window->screen->height &&
+ if (window->size_hints.min_width == window->screen->rect.width &&
+ window->size_hints.min_height == window->screen->rect.height &&
!window->decorated)
; /* leave fullscreen available */
else
@@ -6070,11 +6068,15 @@ menu_callback (MetaWindowMenu *menu,
break;
case META_MENU_OP_UNMAXIMIZE:
- meta_window_unmaximize (window);
+ meta_window_unmaximize (window,
+ META_MAXIMIZE_HORIZONTAL |
+ META_MAXIMIZE_VERTICAL);
break;
case META_MENU_OP_MAXIMIZE:
- meta_window_maximize (window);
+ meta_window_maximize (window,
+ META_MAXIMIZE_HORIZONTAL |
+ META_MAXIMIZE_VERTICAL);
break;
case META_MENU_OP_UNSHADE:
@@ -6137,6 +6139,10 @@ menu_callback (MetaWindowMenu *menu,
META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN,
meta_display_get_current_time (window->display));
break;
+
+ case META_MENU_OP_RECOVER:
+ meta_window_shove_titlebar_onscreen (window);
+ break;
case 0:
/* nothing */
@@ -6196,6 +6202,11 @@ meta_window_show_menu (MetaWindow *window,
ops |= (META_MENU_OP_DELETE | META_MENU_OP_MINIMIZE | META_MENU_OP_MOVE | META_MENU_OP_RESIZE);
+ if (!meta_window_titlebar_is_onscreen (window) &&
+ window->type != META_WINDOW_DOCK &&
+ window->type != META_WINDOW_DESKTOP)
+ ops |= META_MENU_OP_RECOVER;
+
n_workspaces = meta_screen_get_n_workspaces (window->screen);
if (n_workspaces > 1)
@@ -6223,7 +6234,7 @@ meta_window_show_menu (MetaWindow *window,
meta_screen_free_workspace_layout (&layout);
- if (window->maximized)
+ if (META_WINDOW_MAXIMIZED (window))
ops |= META_MENU_OP_UNMAXIMIZE;
else
ops |= META_MENU_OP_MAXIMIZE;
@@ -6295,6 +6306,98 @@ meta_window_show_menu (MetaWindow *window,
meta_ui_window_menu_popup (menu, root_x, root_y, button, timestamp);
}
+void
+meta_window_shove_titlebar_onscreen (MetaWindow *window)
+{
+ MetaRectangle outer_rect;
+ GList *onscreen_region;
+ int horiz_amount, vert_amount;
+ int newx, newy;
+
+ /* If there's no titlebar, don't bother */
+ if (!window->frame)
+ return;
+
+ /* Get the basic info we need */
+ meta_window_get_outer_rect (window, &outer_rect);
+ onscreen_region = window->screen->active_workspace->screen_region;
+
+ /* Extend the region (just in case the window is too big to fit on the
+ * screen), then shove the window on screen, then return the region to
+ * normal.
+ */
+ horiz_amount = outer_rect.width;
+ vert_amount = outer_rect.height;
+ meta_rectangle_expand_region (onscreen_region,
+ horiz_amount,
+ horiz_amount,
+ 0,
+ vert_amount);
+ meta_rectangle_shove_into_region(onscreen_region,
+ FIXED_DIRECTION_X,
+ &outer_rect);
+ meta_rectangle_expand_region (onscreen_region,
+ -horiz_amount,
+ -horiz_amount,
+ 0,
+ -vert_amount);
+
+ newx = outer_rect.x + window->frame->child_x;
+ newy = outer_rect.y + window->frame->child_y;
+ meta_window_move_resize (window,
+ TRUE,
+ newx,
+ newy,
+ window->rect.width,
+ window->rect.height);
+}
+
+gboolean
+meta_window_titlebar_is_onscreen (MetaWindow *window)
+{
+ MetaRectangle titlebar_rect;
+ GList *onscreen_region;
+ int titlebar_size;
+ gboolean is_onscreen;
+
+ const int min_height_needed = 8;
+ const int min_width_percent = 0.5;
+ const int min_width_absolute = 50;
+
+ /* Titlebar can't be offscreen if there is no titlebar... */
+ if (!window->frame)
+ return FALSE;
+
+ /* Get the rectangle corresponding to the titlebar */
+ meta_window_get_outer_rect (window, &titlebar_rect);
+ titlebar_rect.height = window->frame->child_y;
+ titlebar_size = meta_rectangle_area (&titlebar_rect);
+
+ /* Run through the spanning rectangles for the screen and see if one of
+ * them overlaps with the titlebar sufficiently to consider it onscreen.
+ */
+ is_onscreen = FALSE;
+ onscreen_region = window->screen->active_workspace->screen_region;
+ while (onscreen_region)
+ {
+ MetaRectangle *spanning_rect = onscreen_region->data;
+ MetaRectangle overlap;
+
+ meta_rectangle_intersect (&titlebar_rect, spanning_rect, &overlap);
+ if (overlap.height > MIN (titlebar_rect.height, min_height_needed) &&
+ overlap.width > MIN (titlebar_rect.width * min_width_percent,
+ min_width_absolute))
+ {
+ is_onscreen = TRUE;
+ break;
+ }
+
+ onscreen_region = onscreen_region->next;
+ }
+
+ return is_onscreen;
+}
+
static double
timeval_to_ms (const GTimeVal *timeval)
{
@@ -6385,14 +6488,28 @@ check_moveresize_frequency (MetaWindow *window,
}
}
+static gboolean
+update_move_timeout (gpointer data)
+{
+ MetaWindow *window = data;
+
+ update_move (window,
+ window->display->grab_last_user_action_was_snap,
+ window->display->grab_latest_motion_x,
+ window->display->grab_latest_motion_y);
+
+ return FALSE;
+}
+
static void
update_move (MetaWindow *window,
- unsigned int mask,
+ gboolean snap,
int x,
int y)
{
int dx, dy;
int new_x, new_y;
+ MetaRectangle old;
int shake_threshold;
window->display->grab_latest_motion_x = x;
@@ -6411,7 +6528,14 @@ update_move (MetaWindow *window,
window->display->grab_anchor_window_pos.x,
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.
+ */
+ 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.
*/
@@ -6420,7 +6544,7 @@ update_move (MetaWindow *window,
shake_threshold = meta_ui_get_drag_threshold (window->screen->ui) *
DRAG_THRESHOLD_TO_SHAKE_THRESHOLD_FACTOR;
- if (window->maximized && ABS (dy) >= shake_threshold)
+ if (META_WINDOW_MAXIMIZED (window) && ABS (dy) >= shake_threshold)
{
double prop;
@@ -6446,14 +6570,16 @@ update_move (MetaWindow *window,
window->display->grab_anchor_root_x = x;
window->display->grab_anchor_root_y = y;
- meta_window_unmaximize (window);
+ 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)
*/
- else if (window->shaken_loose || window->maximized)
+ else if (window->shaken_loose || META_WINDOW_MAXIMIZED (window))
{
const MetaXineramaScreenInfo *wxinerama;
MetaRectangle work_area;
@@ -6485,7 +6611,9 @@ update_move (MetaWindow *window,
window->saved_rect.y += window->frame->child_y;
}
- meta_window_unmaximize (window);
+ meta_window_unmaximize (window,
+ META_MAXIMIZE_HORIZONTAL |
+ META_MAXIMIZE_VERTICAL);
}
window->display->grab_initial_window_pos = work_area;
@@ -6493,27 +6621,39 @@ update_move (MetaWindow *window,
window->display->grab_anchor_root_y = y;
window->shaken_loose = FALSE;
- meta_window_maximize (window);
+ meta_window_maximize (window,
+ META_MAXIMIZE_HORIZONTAL |
+ META_MAXIMIZE_VERTICAL);
return;
}
}
}
- /* don't allow a maximized window to move */
- if (window->maximized)
- return;
-
- if (mask & ShiftMask)
+ if (window->display->grab_wireframe_active)
+ old = window->display->grab_wireframe_rect;
+ else
{
- /* snap to edges */
- if (dy != 0)
- new_x = meta_window_find_nearest_vertical_edge (window, new_x);
-
- if (dx != 0)
- new_y = meta_window_find_nearest_horizontal_edge (window, new_y);
+ old = window->rect;
+ meta_window_get_position (window, &old.x, &old.y);
}
+ /* Don't allow movement in the maximized directions */
+ if (window->maximized_horizontally)
+ new_x = old.x;
+ if (window->maximized_vertically)
+ new_y = old.y;
+
+ /* Do any edge resistance/snapping */
+ meta_window_edge_resistance_for_move (window,
+ old.x,
+ old.y,
+ &new_x,
+ &new_y,
+ update_move_timeout,
+ snap,
+ FALSE);
+
if (window->display->grab_wireframe_active)
meta_window_update_wireframe (window, new_x, new_y,
window->display->grab_wireframe_rect.width,
@@ -6522,25 +6662,22 @@ update_move (MetaWindow *window,
meta_window_move (window, TRUE, new_x, new_y);
}
-static void update_resize (MetaWindow *window,
- int x,
- int y,
- gboolean force);
-
static gboolean
update_resize_timeout (gpointer data)
{
MetaWindow *window = data;
update_resize (window,
- window->display->grab_latest_motion_x,
- window->display->grab_latest_motion_y,
- TRUE);
+ window->display->grab_last_user_action_was_snap,
+ window->display->grab_latest_motion_x,
+ window->display->grab_latest_motion_y,
+ TRUE);
return FALSE;
}
static void
update_resize (MetaWindow *window,
+ gboolean snap,
int x, int y,
gboolean force)
{
@@ -6560,6 +6697,13 @@ update_resize (MetaWindow *window,
new_w = window->display->grab_anchor_window_pos.width;
new_h = window->display->grab_anchor_window_pos.height;
+ /* Don't bother doing anything if no move has been specified. (This
+ * happens often, even in keyboard resizing, due to the warping of the
+ * pointer.
+ */
+ if (dx == 0 && dy == 0)
+ return;
+
/* FIXME this is only used in wireframe mode */
new_x = window->display->grab_anchor_window_pos.x;
new_y = window->display->grab_anchor_window_pos.y;
@@ -6608,6 +6752,11 @@ update_resize (MetaWindow *window,
}
}
+ /* FIXME: This stupidity only needed because of wireframe mode and
+ * the fact that wireframe isn't making use of
+ * meta_rectangle_resize_with_gravity(). If we were to use that, we
+ * could just increment new_w and new_h by dx and dy in all cases.
+ */
switch (window->display->grab_op)
{
case META_GRAB_OP_RESIZING_SE:
@@ -6680,12 +6829,46 @@ update_resize (MetaWindow *window,
window->display->grab_resize_timeout_id = 0;
}
- old = window->rect;
+ if (window->display->grab_wireframe_active)
+ old = window->display->grab_wireframe_rect;
+ else
+ old = window->rect; /* Don't actually care about x,y */
+
+ /* One sided resizing ought to actually be one-sided, despite the fact that
+ * aspect ratio windows don't interact nicely with the above stuff. So,
+ * to avoid some nasty flicker, we enforce that.
+ */
+ switch (window->display->grab_op)
+ {
+ case META_GRAB_OP_RESIZING_S:
+ case META_GRAB_OP_RESIZING_N:
+ new_w = old.width;
+ break;
+
+ case META_GRAB_OP_RESIZING_E:
+ case META_GRAB_OP_RESIZING_W:
+ new_h = old.height;
+ break;
+
+ default:
+ break;
+ }
/* compute gravity of client during operation */
gravity = meta_resize_gravity_from_grab_op (window->display->grab_op);
g_assert (gravity >= 0);
+ /* Do any edge resistance/snapping */
+ meta_window_edge_resistance_for_resize (window,
+ old.width,
+ old.height,
+ &new_w,
+ &new_h,
+ gravity,
+ update_resize_timeout,
+ snap,
+ FALSE);
+
if (window->display->grab_wireframe_active)
{
if ((new_x + new_w <= new_x) || (new_y + new_h <= new_y))
@@ -6702,7 +6885,11 @@ update_resize (MetaWindow *window,
}
else
{
- meta_window_resize_with_gravity (window, TRUE, new_w, new_h, gravity);
+ /* We don't need to update unless the specified width and height
+ * are actually different from what we had before.
+ */
+ if (old.width != new_w || old.height != new_h)
+ meta_window_resize_with_gravity (window, TRUE, new_w, new_h, gravity);
}
/* Store the latest resize time, if we actually resized. */
@@ -6831,6 +7018,7 @@ meta_window_handle_mouse_grab_op_event (MetaWindow *window,
case META_GRAB_OP_KEYBOARD_RESIZING_NW:
/* no pointer round trip here, to keep in sync */
update_resize (window,
+ window->display->grab_last_user_action_was_snap,
window->display->grab_latest_motion_x,
window->display->grab_latest_motion_y,
TRUE);
@@ -6844,20 +7032,29 @@ meta_window_handle_mouse_grab_op_event (MetaWindow *window,
switch (event->type)
{
- case ButtonRelease:
- if (meta_grab_op_is_moving (window->display->grab_op))
- {
- if (event->xbutton.root == window->screen->xroot)
- update_move (window, event->xbutton.state,
- event->xbutton.x_root, event->xbutton.y_root);
- }
- else if (meta_grab_op_is_resizing (window->display->grab_op))
+ case ButtonRelease:
+ /* If the user was snap moving then ignore the button release
+ * because they may have let go of shift before releasing the
+ * mouse button and they almost certainly do not want a
+ * non-snapped movement to occur from the button release.
+ */
+ if (!window->display->grab_last_user_action_was_snap)
{
- if (event->xbutton.root == window->screen->xroot)
- update_resize (window,
- event->xbutton.x_root,
- event->xbutton.y_root,
- TRUE);
+ if (meta_grab_op_is_moving (window->display->grab_op))
+ {
+ if (event->xbutton.root == window->screen->xroot)
+ update_move (window, event->xbutton.state & ShiftMask,
+ event->xbutton.x_root, event->xbutton.y_root);
+ }
+ else if (meta_grab_op_is_resizing (window->display->grab_op))
+ {
+ if (event->xbutton.root == window->screen->xroot)
+ update_resize (window,
+ event->xbutton.state & ShiftMask,
+ event->xbutton.x_root,
+ event->xbutton.y_root,
+ TRUE);
+ }
}
meta_display_end_grab_op (window->display, event->xbutton.time);
@@ -6871,7 +7068,7 @@ meta_window_handle_mouse_grab_op_event (MetaWindow *window,
if (check_use_this_motion_notify (window,
event))
update_move (window,
- event->xmotion.state,
+ event->xmotion.state & ShiftMask,
event->xmotion.x_root,
event->xmotion.y_root);
}
@@ -6883,32 +7080,14 @@ meta_window_handle_mouse_grab_op_event (MetaWindow *window,
if (check_use_this_motion_notify (window,
event))
update_resize (window,
+ event->xmotion.state & ShiftMask,
event->xmotion.x_root,
event->xmotion.y_root,
- FALSE);
+ FALSE);
}
}
break;
- case EnterNotify:
- case LeaveNotify:
- if (meta_grab_op_is_moving (window->display->grab_op))
- {
- if (event->xcrossing.root == window->screen->xroot)
- update_move (window,
- event->xcrossing.state,
- event->xcrossing.x_root,
- event->xcrossing.y_root);
- }
- else if (meta_grab_op_is_resizing (window->display->grab_op))
- {
- if (event->xcrossing.root == window->screen->xroot)
- update_resize (window,
- event->xcrossing.x_root,
- event->xcrossing.y_root,
- FALSE);
- }
- break;
default:
break;
}
@@ -6939,54 +7118,26 @@ get_work_area_xinerama (MetaWindow *window,
MetaRectangle *area,
int which_xinerama)
{
- MetaRectangle space_area;
GList *tmp;
- int left_strut;
- int right_strut;
- int top_strut;
- int bottom_strut;
- int xinerama_origin_x;
- int xinerama_origin_y;
- int xinerama_width;
- int xinerama_height;
g_assert (which_xinerama >= 0);
- xinerama_origin_x = window->screen->xinerama_infos[which_xinerama].x_origin;
- xinerama_origin_y = window->screen->xinerama_infos[which_xinerama].y_origin;
- xinerama_width = window->screen->xinerama_infos[which_xinerama].width;
- xinerama_height = window->screen->xinerama_infos[which_xinerama].height;
-
- left_strut = 0;
- right_strut = 0;
- top_strut = 0;
- bottom_strut = 0;
+ /* Initialize to the whole xinerama */
+ *area = window->screen->xinerama_infos[which_xinerama].rect;
tmp = meta_window_get_workspaces (window);
while (tmp != NULL)
{
+ MetaRectangle workspace_work_area;
meta_workspace_get_work_area_for_xinerama (tmp->data,
which_xinerama,
- &space_area);
-
- left_strut = MAX (left_strut, space_area.x - xinerama_origin_x);
- right_strut = MAX (right_strut,
- (xinerama_width -
- (space_area.x - xinerama_origin_x) -
- space_area.width));
- top_strut = MAX (top_strut, space_area.y - xinerama_origin_y);
- bottom_strut = MAX (bottom_strut,
- (xinerama_height -
- (space_area.y - xinerama_origin_y) -
- space_area.height));
+ &workspace_work_area);
+ meta_rectangle_intersect (area,
+ &workspace_work_area,
+ area);
tmp = tmp->next;
}
- area->x = xinerama_origin_x + left_strut;
- area->y = xinerama_origin_y + top_strut;
- area->width = xinerama_width - left_strut - right_strut;
- area->height = xinerama_height - top_strut - bottom_strut;
-
meta_topic (META_DEBUG_WORKAREA,
"Window %s xinerama %d has work area %d,%d %d x %d\n",
window->desc, which_xinerama,
@@ -7022,50 +7173,22 @@ void
meta_window_get_work_area_all_xineramas (MetaWindow *window,
MetaRectangle *area)
{
- MetaRectangle space_area;
GList *tmp;
- int left_strut;
- int right_strut;
- int top_strut;
- int bottom_strut;
- int screen_origin_x;
- int screen_origin_y;
- int screen_width;
- int screen_height;
-
- screen_origin_x = 0;
- screen_origin_y = 0;
- screen_width = window->screen->width;
- screen_height = window->screen->height;
-
- left_strut = 0;
- right_strut = 0;
- top_strut = 0;
- bottom_strut = 0;
+
+ /* Initialize to the whole screen */
+ *area = window->screen->rect;
tmp = meta_window_get_workspaces (window);
while (tmp != NULL)
{
+ MetaRectangle workspace_work_area;
meta_workspace_get_work_area_all_xineramas (tmp->data,
- &space_area);
-
- left_strut = MAX (left_strut, space_area.x - screen_origin_x);
- right_strut = MAX (right_strut,
- (screen_width -
- (space_area.x - screen_origin_x) -
- space_area.width));
- top_strut = MAX (top_strut, space_area.y - screen_origin_y);
- bottom_strut = MAX (bottom_strut,
- (screen_height -
- (space_area.y - screen_origin_y) -
- space_area.height));
+ &workspace_work_area);
+ meta_rectangle_intersect (area,
+ &workspace_work_area,
+ area);
tmp = tmp->next;
}
-
- area->x = screen_origin_x + left_strut;
- area->y = screen_origin_y + top_strut;
- area->width = screen_width - left_strut - right_strut;
- area->height = screen_height - top_strut - bottom_strut;
meta_topic (META_DEBUG_WORKAREA,
"Window %s has whole-screen work area %d,%d %d x %d\n",
@@ -7378,12 +7501,35 @@ warp_grab_pointer (MetaWindow *window,
*x += rect.x;
*y += rect.y;
+
+ /* Avoid weird bouncing at the screen edge; see bug 154706 */
+ *x = CLAMP (*x, 0, window->screen->rect.width-1);
+ *y = CLAMP (*y, 0, window->screen->rect.height-1);
meta_error_trap_push_with_return (window->display);
meta_topic (META_DEBUG_WINDOW_OPS,
"Warping pointer to %d,%d with window at %d,%d\n",
*x, *y, rect.x, rect.y);
+
+ /* Need to update the grab positions so that the MotionNotify and other
+ * events generated by the XWarpPointer() call below don't cause complete
+ * funkiness. See bug 124582 and bug 122670.
+ */
+ window->display->grab_anchor_root_x = *x;
+ window->display->grab_anchor_root_y = *y;
+ window->display->grab_latest_motion_x = *x;
+ window->display->grab_latest_motion_y = *y;
+ if (window->display->grab_wireframe_active)
+ window->display->grab_anchor_window_pos =
+ window->display->grab_wireframe_rect;
+ else
+ {
+ window->display->grab_anchor_window_pos = window->rect;
+ meta_window_get_position (window,
+ &window->display->grab_anchor_window_pos.x,
+ &window->display->grab_anchor_window_pos.y);
+ }
XWarpPointer (window->display->xdisplay,
None,
@@ -7443,33 +7589,6 @@ meta_window_update_keyboard_resize (MetaWindow *window,
window->display->grab_op,
&x, &y);
- {
- /* As we warped the pointer, we have to reset the anchor state,
- * since if the mouse moves we want to use those events to do the
- * right thing. Also, this means that the motion notify
- * from the pointer warp comes back as a no-op.
- */
- int dx, dy;
-
- dx = x - window->display->grab_anchor_root_x;
- dy = y - window->display->grab_anchor_root_y;
-
- window->display->grab_anchor_root_x += dx;
- window->display->grab_anchor_root_y += dy;
- if (window->display->grab_wireframe_active)
- {
- window->display->grab_anchor_window_pos =
- window->display->grab_wireframe_rect;
- }
- else
- {
- window->display->grab_anchor_window_pos = window->rect;
- meta_window_get_position (window,
- &window->display->grab_anchor_window_pos.x,
- &window->display->grab_anchor_window_pos.y);
- }
- }
-
if (update_cursor)
{
meta_display_set_grab_op_cursor (window->display,
diff --git a/src/window.h b/src/window.h
index 4253c96..54a5308 100644
--- a/src/window.h
+++ b/src/window.h
@@ -52,6 +52,12 @@ typedef enum
META_WINDOW_SPLASHSCREEN
} MetaWindowType;
+typedef enum
+{
+ META_MAXIMIZE_HORIZONTAL = 1 << 0,
+ META_MAXIMIZE_VERTICAL = 1 << 1
+} MetaMaximizeFlags;
+
struct _MetaStruts
{
/* struts */
@@ -108,8 +114,10 @@ struct _MetaWindow
Time initial_timestamp;
/* Whether we're maximized */
- guint maximized : 1;
- guint maximize_after_placement : 1;
+ guint maximized_horizontally : 1;
+ guint maximized_vertically : 1;
+ guint maximize_horizontally_after_placement : 1;
+ guint maximize_vertically_after_placement : 1;
/* Whether we're shaded */
guint shaded : 1;
@@ -117,6 +125,12 @@ struct _MetaWindow
/* Whether we're fullscreen */
guint fullscreen : 1;
+ /* Whether we're trying to constrain the window to be fully onscreen */
+ guint require_fully_onscreen : 1;
+
+ /* Whether we're trying to constrain the window to be on a single xinerama */
+ guint require_on_single_xinerama : 1;
+
/* Whether we're sticky in the multi-workspace sense
* (vs. the not-scroll-with-viewport sense, we don't
* have no stupid viewports)
@@ -287,25 +301,29 @@ struct _MetaWindow
/* The size we set the window to last (i.e. what we believe
* to be its actual size on the server). The x, y are
* the actual server-side x,y so are relative to the frame
- * or the root window as appropriate.
+ * (meaning that they just hold the frame width and height)
+ * or the root window (meaning they specify the location
+ * of the top left of the inner window) as appropriate.
*/
MetaRectangle rect;
- /* The geometry to restore when we unmaximize.
- * The position is in root window coords, even if
- * there's a frame, which contrasts with window->rect
- * above.
+ /* The geometry to restore when we unmaximize. The position is in
+ * root window coords, even if there's a frame, which contrasts with
+ * window->rect above. Note that this gives the position and size
+ * of the client window (i.e. ignoring the frame).
*/
MetaRectangle saved_rect;
/* This is the geometry the window had after the last user-initiated
* move/resize operations. We use this whenever we are moving the
- * implicitly (for example, if we move to avoid a panel, we
- * can snap back to this position if the panel moves again)
+ * implicitly (for example, if we move to avoid a panel, we can snap
+ * back to this position if the panel moves again). Note that this
+ * gives the position and size of the client window (i.e. ignoring
+ * the frame).
*
* Position valid if user_has_moved, size valid if user_has_resized
*
- * Position always in root coords, unlike window->rect
+ * Position always in root coords, unlike window->rect.
*/
MetaRectangle user_rect;
@@ -330,8 +348,10 @@ struct _MetaWindow
* the dynamic window state such as "maximized", not just the
* window's type
*/
+#define META_WINDOW_MAXIMIZED(w) ((w)->maximized_horizontally && \
+ (w)->maximized_vertically)
#define META_WINDOW_ALLOWS_MOVE(w) ((w)->has_move_func && !(w)->fullscreen)
-#define META_WINDOW_ALLOWS_RESIZE_EXCEPT_HINTS(w) ((w)->has_resize_func && !(w)->maximized && !(w)->fullscreen && !(w)->shaded)
+#define META_WINDOW_ALLOWS_RESIZE_EXCEPT_HINTS(w) ((w)->has_resize_func && !META_WINDOW_MAXIMIZED (w) && !(w)->fullscreen && !(w)->shaded)
#define META_WINDOW_ALLOWS_RESIZE(w) (META_WINDOW_ALLOWS_RESIZE_EXCEPT_HINTS (w) && \
(((w)->size_hints.min_width < (w)->size_hints.max_width) || \
((w)->size_hints.min_height < (w)->size_hints.max_height)))
@@ -350,10 +370,13 @@ void meta_window_calc_showing (MetaWindow *window);
void meta_window_queue_calc_showing (MetaWindow *window);
void meta_window_minimize (MetaWindow *window);
void meta_window_unminimize (MetaWindow *window);
-void meta_window_maximize (MetaWindow *window);
-void meta_window_maximize_internal (MetaWindow *window,
- MetaRectangle *saved_rect);
-void meta_window_unmaximize (MetaWindow *window);
+void meta_window_maximize (MetaWindow *window,
+ MetaMaximizeFlags directions);
+void meta_window_maximize_internal (MetaWindow *window,
+ MetaMaximizeFlags directions,
+ MetaRectangle *saved_rect);
+void meta_window_unmaximize (MetaWindow *window,
+ MetaMaximizeFlags directions);
void meta_window_make_above (MetaWindow *window);
void meta_window_unmake_above (MetaWindow *window);
void meta_window_shade (MetaWindow *window);
@@ -390,9 +413,6 @@ void meta_window_resize_with_gravity (MetaWindow *window,
int gravity);
-void meta_window_fill_horizontal (MetaWindow *window);
-void meta_window_fill_vertical (MetaWindow *window);
-
/* Return whether the window would be showing if we were on its workspace */
gboolean meta_window_showing_on_its_workspace (MetaWindow *window);
@@ -477,6 +497,9 @@ void meta_window_show_menu (MetaWindow *window,
int button,
Time timestamp);
+gboolean meta_window_titlebar_is_onscreen (MetaWindow *window);
+void meta_window_shove_titlebar_onscreen (MetaWindow *window);
+
void meta_window_set_gravity (MetaWindow *window,
int gravity);
diff --git a/src/workspace.c b/src/workspace.c
index 70f6ebd..d19e6a8 100644
--- a/src/workspace.c
+++ b/src/workspace.c
@@ -57,17 +57,19 @@ meta_workspace_new (MetaScreen *screen)
workspace->mru_list = NULL;
meta_screen_foreach_window (screen, maybe_add_to_list, &workspace->mru_list);
- workspace->work_areas = NULL;
workspace->work_areas_invalid = TRUE;
- workspace->all_work_areas.x = 0;
- workspace->all_work_areas.y = 0;
- workspace->all_work_areas.width = 0;
- workspace->all_work_areas.height = 0;
+ workspace->work_area_xinerama = NULL;
+ workspace->work_area_screen.x = 0;
+ workspace->work_area_screen.y = 0;
+ workspace->work_area_screen.width = 0;
+ workspace->work_area_screen.height = 0;
- workspace->left_struts = NULL;
- workspace->right_struts = NULL;
- workspace->top_struts = NULL;
- workspace->bottom_struts = NULL;
+ workspace->screen_region = NULL;
+ workspace->xinerama_region = NULL;
+ workspace->screen_edges = NULL;
+ workspace->xinerama_edges = NULL;
+
+ workspace->all_struts = NULL;
workspace->showing_desktop = FALSE;
@@ -79,6 +81,7 @@ meta_workspace_free (MetaWorkspace *workspace)
{
GList *tmp;
MetaScreen *screen;
+ int i;
g_return_if_fail (workspace != workspace->screen->active_workspace);
@@ -107,13 +110,17 @@ meta_workspace_free (MetaWorkspace *workspace)
workspace->screen->workspaces =
g_list_remove (workspace->screen->workspaces, workspace);
- g_free (workspace->work_areas);
+ g_free (workspace->work_area_xinerama);
g_list_free (workspace->mru_list);
- g_slist_free (workspace->left_struts);
- g_slist_free (workspace->right_struts);
- g_slist_free (workspace->top_struts);
- g_slist_free (workspace->bottom_struts);
+ g_slist_free (workspace->all_struts);
+
+ for (i = 0; i < screen->n_xinerama_infos; i++)
+ meta_rectangle_free_list_and_elements (workspace->xinerama_region[i]);
+ g_free (workspace->xinerama_region);
+ meta_rectangle_free_list_and_elements (workspace->screen_region);
+ meta_rectangle_free_list_and_elements (workspace->screen_edges);
+ meta_rectangle_free_list_and_elements (workspace->xinerama_edges);
g_free (workspace);
@@ -429,6 +436,7 @@ meta_workspace_invalidate_work_area (MetaWorkspace *workspace)
{
GList *tmp;
GList *windows;
+ int i;
if (workspace->work_areas_invalid)
{
@@ -442,17 +450,22 @@ meta_workspace_invalidate_work_area (MetaWorkspace *workspace)
"Invalidating work area for workspace %d\n",
meta_workspace_index (workspace));
- g_free (workspace->work_areas);
- workspace->work_areas = NULL;
+ g_free (workspace->work_area_xinerama);
+ workspace->work_area_xinerama = NULL;
- g_slist_free (workspace->left_struts);
- workspace->left_struts = NULL;
- g_slist_free (workspace->right_struts);
- workspace->right_struts = NULL;
- g_slist_free (workspace->top_struts);
- workspace->top_struts = NULL;
- g_slist_free (workspace->bottom_struts);
- workspace->bottom_struts = NULL;
+ g_slist_free (workspace->all_struts);
+ workspace->all_struts = NULL;
+
+ for (i = 0; i < workspace->screen->n_xinerama_infos; i++)
+ meta_rectangle_free_list_and_elements (workspace->xinerama_region[i]);
+ g_free (workspace->xinerama_region);
+ meta_rectangle_free_list_and_elements (workspace->screen_region);
+ meta_rectangle_free_list_and_elements (workspace->screen_edges);
+ meta_rectangle_free_list_and_elements (workspace->xinerama_edges);
+ workspace->xinerama_region = NULL;
+ workspace->screen_region = NULL;
+ workspace->screen_edges = NULL;
+ workspace->xinerama_edges = NULL;
workspace->work_areas_invalid = TRUE;
@@ -491,16 +504,17 @@ ensure_work_areas_validated (MetaWorkspace *workspace)
if (!workspace->work_areas_invalid)
return;
- g_assert (workspace->top_struts == NULL);
- g_assert (workspace->bottom_struts == NULL);
- g_assert (workspace->left_struts == NULL);
- g_assert (workspace->right_struts == NULL);
+ g_assert (workspace->all_struts == NULL);
+ g_assert (workspace->xinerama_region == NULL);
+ g_assert (workspace->screen_region == NULL);
+ g_assert (workspace->screen_edges == NULL);
+ g_assert (workspace->xinerama_edges == NULL);
windows = meta_workspace_list_windows (workspace);
- g_free (workspace->work_areas);
- workspace->work_areas = g_new (MetaRectangle,
- workspace->screen->n_xinerama_infos);
+ g_free (workspace->work_area_xinerama);
+ workspace->work_area_xinerama = g_new (MetaRectangle,
+ workspace->screen->n_xinerama_infos);
i = 0;
while (i < workspace->screen->n_xinerama_infos)
@@ -528,67 +542,63 @@ ensure_work_areas_validated (MetaWorkspace *workspace)
if ((i == 0) && (w->struts->left.width > 0))
{
- workspace->left_struts = g_slist_prepend (workspace->left_struts,
- &w->struts->left);
+ workspace->all_struts = g_slist_prepend (workspace->all_struts,
+ &w->struts->left);
}
- if (meta_screen_rect_intersects_xinerama (w->screen,
- &w->struts->left,
- i))
+ if (meta_rectangle_overlap (&w->screen->xinerama_infos[i].rect,
+ &w->struts->left))
{
left_strut = MAX (left_strut,
w->struts->left.width -
- workspace->screen->xinerama_infos[i].x_origin);
+ workspace->screen->xinerama_infos[i].rect.x);
all_left_strut = MAX (all_left_strut, w->struts->left.width);
}
if ((i == 0) && (w->struts->right.width > 0))
{
- workspace->right_struts = g_slist_prepend (workspace->right_struts,
- &w->struts->right);
+ workspace->all_struts = g_slist_prepend (workspace->all_struts,
+ &w->struts->right);
}
- if (meta_screen_rect_intersects_xinerama (w->screen,
- &w->struts->right,
- i))
+ if (meta_rectangle_overlap (&w->screen->xinerama_infos[i].rect,
+ &w->struts->right))
{
right_strut = MAX (right_strut, w->struts->right.width -
- workspace->screen->width +
- workspace->screen->xinerama_infos[i].width +
- workspace->screen->xinerama_infos[i].x_origin);
+ workspace->screen->rect.width +
+ workspace->screen->xinerama_infos[i].rect.width +
+ workspace->screen->xinerama_infos[i].rect.x);
all_right_strut = MAX (all_right_strut, w->struts->right.width);
}
if ((i == 0) && (w->struts->top.height > 0))
{
- workspace->top_struts = g_slist_prepend (workspace->top_struts,
+ workspace->all_struts = g_slist_prepend (workspace->all_struts,
&w->struts->top);
}
- if (meta_screen_rect_intersects_xinerama (w->screen,
- &w->struts->top,
- i))
+ if (meta_rectangle_overlap (&w->screen->xinerama_infos[i].rect,
+ &w->struts->top))
{
top_strut = MAX (top_strut,
w->struts->top.height -
- workspace->screen->xinerama_infos[i].y_origin);
+ workspace->screen->xinerama_infos[i].rect.y);
all_top_strut = MAX (all_top_strut, w->struts->top.height);
}
if ((i == 0) && (w->struts->bottom.height > 0))
{
- workspace->bottom_struts = g_slist_prepend (workspace->bottom_struts,
- &w->struts->bottom);
+ workspace->all_struts = g_slist_prepend (workspace->all_struts,
+ &w->struts->bottom);
}
- if (meta_screen_rect_intersects_xinerama (w->screen,
- &w->struts->bottom,
- i))
+ if (meta_rectangle_overlap (&w->screen->xinerama_infos[i].rect,
+ &w->struts->bottom))
{
bottom_strut = MAX (bottom_strut, w->struts->bottom.height -
- workspace->screen->height +
- workspace->screen->xinerama_infos[i].height +
- workspace->screen->xinerama_infos[i].y_origin);
+ workspace->screen->rect.height +
+ workspace->screen->xinerama_infos[i].rect.height +
+ workspace->screen->xinerama_infos[i].rect.y);
all_bottom_strut = MAX (all_bottom_strut, w->struts->bottom.height);
}
}
@@ -600,36 +610,36 @@ ensure_work_areas_validated (MetaWorkspace *workspace)
#define MIN_SANE_AREA 100
if ((left_strut + right_strut) >
- (workspace->screen->xinerama_infos[i].width - MIN_SANE_AREA))
+ (workspace->screen->xinerama_infos[i].rect.width - MIN_SANE_AREA))
{
meta_topic (META_DEBUG_WORKAREA,
"Making left/right struts %d %d sane xinerama %d\n",
left_strut, right_strut, i);
- left_strut = (workspace->screen->xinerama_infos[i].width -
+ left_strut = (workspace->screen->xinerama_infos[i].rect.width -
MIN_SANE_AREA) / 2;
right_strut = left_strut;
}
if ((top_strut + bottom_strut) >
- (workspace->screen->xinerama_infos[i].height - MIN_SANE_AREA))
+ (workspace->screen->xinerama_infos[i].rect.height - MIN_SANE_AREA))
{
meta_topic (META_DEBUG_WORKAREA,
"Making top/bottom struts %d %d sane xinerama %d\n",
top_strut, bottom_strut, i);
- top_strut = (workspace->screen->xinerama_infos[i].height -
+ top_strut = (workspace->screen->xinerama_infos[i].rect.height -
MIN_SANE_AREA) / 2;
bottom_strut = top_strut;
}
- workspace->work_areas[i].x =
- left_strut + workspace->screen->xinerama_infos[i].x_origin;
- workspace->work_areas[i].y = top_strut +
- workspace->screen->xinerama_infos[i].y_origin;
- workspace->work_areas[i].width =
- workspace->screen->xinerama_infos[i].width -
+ workspace->work_area_xinerama[i].x =
+ left_strut + workspace->screen->xinerama_infos[i].rect.x;
+ workspace->work_area_xinerama[i].y = top_strut +
+ workspace->screen->xinerama_infos[i].rect.y;
+ workspace->work_area_xinerama[i].width =
+ workspace->screen->xinerama_infos[i].rect.width -
left_strut - right_strut;
- workspace->work_areas[i].height =
- workspace->screen->xinerama_infos[i].height -
+ workspace->work_area_xinerama[i].height =
+ workspace->screen->xinerama_infos[i].rect.height -
top_strut - bottom_strut;
meta_topic (META_DEBUG_WORKAREA,
@@ -637,10 +647,10 @@ ensure_work_areas_validated (MetaWorkspace *workspace)
"xinerama %d: %d,%d %d x %d\n",
meta_workspace_index (workspace),
i,
- workspace->work_areas[i].x,
- workspace->work_areas[i].y,
- workspace->work_areas[i].width,
- workspace->work_areas[i].height);
+ workspace->work_area_xinerama[i].x,
+ workspace->work_area_xinerama[i].y,
+ workspace->work_area_xinerama[i].width,
+ workspace->work_area_xinerama[i].height);
++i;
}
@@ -648,41 +658,91 @@ ensure_work_areas_validated (MetaWorkspace *workspace)
g_list_free (windows);
if ((all_left_strut + all_right_strut) >
- (workspace->screen->width - MIN_SANE_AREA))
+ (workspace->screen->rect.width - MIN_SANE_AREA))
{
meta_topic (META_DEBUG_WORKAREA,
"Making screen-wide left/right struts %d %d sane\n",
all_left_strut, all_right_strut);
- all_left_strut = (workspace->screen->width - MIN_SANE_AREA) / 2;
+ all_left_strut = (workspace->screen->rect.width - MIN_SANE_AREA) / 2;
all_right_strut = all_left_strut;
}
if ((all_top_strut + all_bottom_strut) >
- (workspace->screen->height - MIN_SANE_AREA))
+ (workspace->screen->rect.height - MIN_SANE_AREA))
{
meta_topic (META_DEBUG_WORKAREA,
"Making top/bottom struts %d %d sane\n",
all_top_strut, all_bottom_strut);
- all_top_strut = (workspace->screen->height - MIN_SANE_AREA) / 2;
+ all_top_strut = (workspace->screen->rect.height - MIN_SANE_AREA) / 2;
all_bottom_strut = all_top_strut;
}
- workspace->all_work_areas.x = all_left_strut;
- workspace->all_work_areas.y = all_top_strut;
- workspace->all_work_areas.width =
- workspace->screen->width - all_left_strut - all_right_strut;
- workspace->all_work_areas.height =
- workspace->screen->height - all_top_strut - all_bottom_strut;
-
+ workspace->work_area_screen.x = all_left_strut;
+ workspace->work_area_screen.y = all_top_strut;
+ workspace->work_area_screen.width =
+ workspace->screen->rect.width - all_left_strut - all_right_strut;
+ workspace->work_area_screen.height =
+ workspace->screen->rect.height - all_top_strut - all_bottom_strut;
+
+ /* Now cache the spanning rects for the onscreen and
+ * on-single-xinerama regions
+ */
+ g_assert (workspace->xinerama_region == NULL);
+ g_assert (workspace->screen_region == NULL);
+
+ workspace->xinerama_region = g_new (GList*,
+ workspace->screen->n_xinerama_infos);
+ for (i = 0; i < workspace->screen->n_xinerama_infos; i++)
+ {
+ workspace->xinerama_region[i] =
+ meta_rectangle_get_minimal_spanning_set_for_region (
+ &workspace->screen->xinerama_infos[i].rect,
+ workspace->all_struts);
+ }
+ workspace->screen_region =
+ meta_rectangle_get_minimal_spanning_set_for_region (
+ &workspace->screen->rect,
+ workspace->all_struts);
+ /* Since get_minimal_spanning_set_for_region() doesn't do the
+ * MIN_SANE_AREA thing, manually account for it. Note that it's okay if
+ * get_minimal_spanning_set_for_region() returns a very small region--all
+ * we really need here is a 1x1 region that corresponds to somewhere on
+ * the monitor for the partially onscreen constraint. If we don't get
+ * anything, though, we use work_area_screen just for convenience.
+ */
+ if (workspace->screen_region == NULL)
+ {
+ MetaRectangle *nonempty_region;
+ nonempty_region = g_new (MetaRectangle, 1);
+ *nonempty_region = workspace->work_area_screen;
+ workspace->screen_region = g_list_prepend (NULL, nonempty_region);
+ }
+
+ /* Now cache the screen and xinerama edges for edge resistance and snapping */
+ g_assert (workspace->screen_edges == NULL);
+ g_assert (workspace->xinerama_edges == NULL);
+ workspace->screen_edges =
+ meta_rectangle_find_onscreen_edges (&workspace->screen->rect,
+ workspace->all_struts);
+ tmp = NULL;
+ for (i = 0; i < workspace->screen->n_xinerama_infos; i++)
+ tmp = g_list_prepend (tmp, &workspace->screen->xinerama_infos[i].rect);
+ workspace->xinerama_edges =
+ meta_rectangle_find_nonintersected_xinerama_edges (tmp,
+ workspace->all_struts);
+ g_list_free (tmp);
+
+
+ /* We're all done, YAAY! Record that everything has been validated. */
workspace->work_areas_invalid = FALSE;
meta_topic (META_DEBUG_WORKAREA,
"Computed work area for workspace %d: %d,%d %d x %d\n",
meta_workspace_index (workspace),
- workspace->all_work_areas.x,
- workspace->all_work_areas.y,
- workspace->all_work_areas.width,
- workspace->all_work_areas.height);
+ workspace->work_area_screen.x,
+ workspace->work_area_screen.y,
+ workspace->work_area_screen.width,
+ workspace->work_area_screen.height);
}
void
@@ -695,7 +755,7 @@ meta_workspace_get_work_area_for_xinerama (MetaWorkspace *workspace,
ensure_work_areas_validated (workspace);
g_assert (which_xinerama < workspace->screen->n_xinerama_infos);
- *area = workspace->work_areas[which_xinerama];
+ *area = workspace->work_area_xinerama[which_xinerama];
}
void
@@ -704,7 +764,24 @@ meta_workspace_get_work_area_all_xineramas (MetaWorkspace *workspace,
{
ensure_work_areas_validated (workspace);
- *area = workspace->all_work_areas;
+ *area = workspace->work_area_screen;
+}
+
+GList*
+meta_workspace_get_onscreen_region (MetaWorkspace *workspace)
+{
+ ensure_work_areas_validated (workspace);
+
+ return workspace->screen_region;
+}
+
+GList*
+meta_workspace_get_onxinerama_region (MetaWorkspace *workspace,
+ int which_xinerama)
+{
+ ensure_work_areas_validated (workspace);
+
+ return workspace->xinerama_region[which_xinerama];
}
#ifdef WITH_VERBOSE_MODE
diff --git a/src/workspace.h b/src/workspace.h
index 311c94d..3c3e8d8 100644
--- a/src/workspace.h
+++ b/src/workspace.h
@@ -2,7 +2,7 @@
/*
* Copyright (C) 2001 Havoc Pennington
- * Copyright (C) 2004 Elijah Newren
+ * Copyright (C) 2004, 2005 Elijah Newren
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -43,12 +43,13 @@ struct _MetaWorkspace
GList *windows;
GList *mru_list;
- MetaRectangle all_work_areas;
- MetaRectangle *work_areas;
- GSList *left_struts;
- GSList *right_struts;
- GSList *top_struts;
- GSList *bottom_struts;
+ MetaRectangle work_area_screen;
+ MetaRectangle *work_area_xinerama;
+ GList *screen_region;
+ GList **xinerama_region;
+ GList *screen_edges;
+ GList *xinerama_edges;
+ GSList *all_struts;
guint work_areas_invalid : 1;
guint showing_desktop : 1;
@@ -78,6 +79,11 @@ void meta_workspace_get_work_area_for_xinerama (MetaWorkspace *workspace,
MetaRectangle *area);
void meta_workspace_get_work_area_all_xineramas (MetaWorkspace *workspace,
MetaRectangle *area);
+GList* meta_workspace_get_onscreen_region (MetaWorkspace *workspace);
+GList* meta_workspace_get_onxinerama_region (MetaWorkspace *workspace,
+ int which_xinerama);
+void meta_workspace_get_work_area_all_xineramas (MetaWorkspace *workspace,
+ MetaRectangle *area);
void meta_workspace_focus_default_window (MetaWorkspace *workspace,
MetaWindow *not_this_one,