Compare commits

...

14 Commits

Author SHA1 Message Date
Georges Basile Stavracas Neto
446c9b7460 x11: Add support for _GTK_EDGE_CONSTRAINTS atom
To keep feature parity with the Wayland backend, and
to improve the overall tiling experience with GTK apps,
add the _GTK_EDGE_CONSTRAINTS X11 atom and update it
when necessary.

https://bugzilla.gnome.org/show_bug.cgi?id=751857
2017-10-03 18:16:44 -03:00
Georges Basile Stavracas Neto
437ab625ed wayland: Send edge constraints
Following up the previous patch, this patch makes the
Wayland backend send the edge constraints through a
custom protocol extension internal to GTK.

As it mature, we can think of upstreaming the protocol
to Wayland itself.

https://bugzilla.gnome.org/show_bug.cgi?id=751857
2017-10-03 18:16:41 -03:00
Georges Basile Stavracas Neto
d89d5af040 window: Track edge constraints
GTK has the ability to handle client-decorated windows
in such a way that the behavior of these windows must
match the behavior of the current window manager.

In Mutter, windows can be tiled horizontally (and, in
the future, vertically as well), which comes with a few
requirements that the toolkit must supply. Tiled windows
have their borders' behavior changed depending on the
tiled position, and the toolkit must be aware of this
information in order to properly match the window manager
behavior.

In order to provide toolkits with more precise and general
data regarding resizable and constrained edges, this patch
makes MetaWindow track its own edge constraints.

This will later be used by the backends to send information
to the toolkit.

https://bugzilla.gnome.org/show_bug.cgi?id=751857
2017-10-03 18:16:28 -03:00
Georges Basile Stavracas Neto
5a95e5c80a window: Also consider touching edges for matching tiled windows
When computing a potential match for a tiled window, there is a
chance we face the case where 2 windows really complement each
other's tile mode (i.e. left and right) but they have different
sizes, and their borders don't really touch each other.

In that case, the current code would mistakenly assume they're
tile matches, and would resize them with either a hole or an
overlapping area between windows. This is clearly a misbehavior
that is a consequence of the previous assumptions pre-resizable
tiles.

This patch adapts the tile match algorithm to also consider the
touching edges when computing the matching tile, unless:

 * the window is not currently tiled (for example when computing
   the tile preview)
 * the window is currently resized in tandem with an existing
   tile match

https://bugzilla.gnome.org/show_bug.cgi?id=645153

bar
2017-10-03 18:16:28 -03:00
Georges Basile Stavracas Neto
211669ba47 window: Raise and lower tile match in tandem
When a pair of tiled windows are grouped together, they
are treated as parts of a whole and interacting with one
affects the other.

Following the idea that sibling tiled windows are treated
as part of the same group, they should also be raised and
lowered together.

It is still possible to break tiled windows grouping by
simply untiling the window with the keyboard or by grabbing
and resizing or moving the window with the cursor.

This patch makes sibling tiled windows be lowered and raised
in tandem. For future reference, this behavior is documented
in [1].

[1] https://wiki.gnome.org/GeorgesNeto/MinutesOfFeaneron/Tiling

https://bugzilla.gnome.org/show_bug.cgi?id=645153
2017-10-03 18:16:28 -03:00
Georges Basile Stavracas Neto
db5795a7cd edge-resistance: Remove useless variable
There is a variable in meta_window_edge_resistance_for_resize
that isn't really helpful: it just assumes TRUE, and is passed
to apply_edge_resistance_to_each_side.

This patch removes that useless variable and simply pass TRUE
instead.

https://bugzilla.gnome.org/show_bug.cgi?id=645153
2017-10-03 18:16:27 -03:00
Georges Basile Stavracas Neto
d3527bf99e edge-resistance: Add snapping for tiled windows
When windows are tiled, it improves the interaction with
them when they have a set of snapping edges relative to
the monitor. For example, when there's a document editor
and a PDF file opened, I might want to rescale the former
to 2/3 of the screen and the latter to 1/3.

These snapping sections are not really tied to any other
window, and only depend on the current work area of the
window. Thus, it is not necessary to adapt the current
snapping edge detection algorithm.

This patch adds the necessary code in edge-resistance.c
to special-case tiled windows and allow them to cover
1/4, 1/3 and 1/2 (horizontally) of the screen. These
values are hardcoded.

https://bugzilla.gnome.org/show_bug.cgi?id=645153
2017-10-03 18:16:27 -03:00
Georges Basile Stavracas Neto
94f5a161f9 window: Tile and resize considering the tile match
After the introduction of the possibility to resize tiled windows,
it is a sensible decision to make windows aware of their tiling
match. A tiling match is another window that is tiled in such a
way that is the complement of the current window.

The newly introduced behavior attepts to make tiling as smooth as
possible, with the following rules:

 * Windows now compute their tile match when tiling and, if there's
   a match, they automatically complement the sibling's width.
 * Resizing a window with a sibling automatically resizes the sibling
   too to be the complement of the window's width.
 * It is not possible to resize below windows' minimum widths.

https://bugzilla.gnome.org/show_bug.cgi?id=645153
2017-10-03 18:16:27 -03:00
Georges Basile Stavracas Neto
209407657d window: Maximize tiled windows when resizing to work area
Now that tiled windows are resizable, the user may grow a tiled
windows until it covers the entire work area. As this makes the
window state mostly indistinguishable from maximization, avoid
subtle differences by properly maximizing the window in that case.

https://bugzilla.gnome.org/show_bug.cgi?id=645153
2017-10-03 18:16:27 -03:00
Florian Müllner
ff34e51826 window: Allow resizing of tiled windows
Currently tiled windows are not resizable and their size is fixed
to half the screen width. Adjust the code to work with fractions
other than half, and allow users to adjust the split by dragging
the window edge that is not constrained by a monitor edge.

Follow-up patches will improve on that by resizing neighboring
tiled windows by a shared edge, and making the functionality
available to client-side decorated windows implementing the
new edge constraints protocol.
2017-10-03 18:16:27 -03:00
Florian Müllner
5b5d8676d4 window: Pass mode as parameter to tile() operation
Now that the preview tile mode has been split from the window's
tile_mode property, it is much more natural to pass the requested
tile_mode to the tile() function instead of setting it externally
and calling the function to apply the state.
2017-10-03 18:16:27 -03:00
Florian Müllner
a2dcf44c60 window: Split out preview_tile_mode
The existing semantics of the tile_mode property are terribly confusing,
as it depends on some other property whether it represents the requested
or current mode. Clear this up by just using separate variables for the
two. As it is unlikely that we will ever support more than one tile
preview, we can track the requested mode globally instead of adding
another per-window variable.

https://bugzilla.gnome.org/show_bug.cgi?id=645153
2017-10-03 16:00:31 -03:00
Georges Basile Stavracas Neto
ff0eee4331 window: Update tile monitor before move
The actual move may involve the tile monitor, so make sure to not use
an outdated value by setting it before calling move_between_rects().

https://bugzilla.gnome.org/show_bug.cgi?id=645153
2017-10-03 16:00:31 -03:00
Florian Müllner
0726d93e79 window: Remove obsolete code
Commit 91b7dedf36 removed the ability to temporarily break out
of maximization/tiling during grab operations, so this code is no
longer necessary.

https://bugzilla.gnome.org/show_bug.cgi?id=645153
2017-10-03 16:00:31 -03:00
13 changed files with 513 additions and 95 deletions

View File

@@ -943,7 +943,7 @@ constrain_maximization (MetaWindow *window,
/* Calculate target_size = maximized size of (window + frame) */
if (META_WINDOW_TILED_MAXIMIZED (window))
{
meta_window_get_current_tile_area (window, &target_size);
meta_window_get_tile_area (window, window->tile_mode, &target_size);
}
else if (META_WINDOW_MAXIMIZED (window))
{
@@ -1030,7 +1030,7 @@ constrain_tiling (MetaWindow *window,
/* Calculate target_size - as the tile previews need this as well, we
* use an external function for the actual calculation
*/
meta_window_get_current_tile_area (window, &target_size);
meta_window_get_tile_area (window, window->tile_mode, &target_size);
/* Check min size constraints; max size constraints are ignored as for
* maximized windows.

View File

@@ -556,7 +556,7 @@ apply_edge_resistance_to_each_side (MetaDisplay *display,
edge_data = display->grab_edge_resistance_data;
if (auto_snap)
if (auto_snap && !META_WINDOW_TILED_SIDE_BY_SIDE (window))
{
/* Do the auto snapping instead of normal edge resistance; in all
* cases, we allow snapping to opposite kinds of edges (e.g. left
@@ -591,6 +591,50 @@ apply_edge_resistance_to_each_side (MetaDisplay *display,
FALSE,
keyboard_op);
}
else if (auto_snap && META_WINDOW_TILED_SIDE_BY_SIDE (window))
{
MetaRectangle workarea;
guint i;
const gfloat tile_edges[] =
{
1./4.,
1./3.,
1./2.,
2./3.,
3./4.,
};
meta_window_get_work_area_current_monitor (window, &workarea);
new_left = new_outer->x;
new_top = new_outer->y;
new_right = new_outer->x + new_outer->width;
new_bottom = new_outer->y + new_outer->height;
/* When snapping tiled windows, we don't really care about the
* x and y position, only about the width and height. Also, it
* is special-cased (instead of relying on edge_data) because
* we don't really care for other windows when calculating the
* snapping points of tiled windows - we only care about the
* work area and the target position.
*/
for (i = 0; i < G_N_ELEMENTS (tile_edges); i++)
{
guint horizontal_point = workarea.x + floor (workarea.width * tile_edges[i]);
if (ABS (horizontal_point - new_left) < 16)
{
new_left = horizontal_point;
new_right = workarea.x + workarea.width;
}
else if (ABS (horizontal_point - new_right) < 16)
{
new_left = workarea.x;
new_right = horizontal_point;
}
}
}
else
{
/* Disable edge resistance for resizes when windows have size
@@ -1223,7 +1267,6 @@ meta_window_edge_resistance_for_resize (MetaWindow *window,
{
MetaRectangle old_outer, new_outer;
int proposed_outer_width, proposed_outer_height;
gboolean is_resize;
meta_window_get_frame_rect (window, &old_outer);
proposed_outer_width = *new_width;
@@ -1235,7 +1278,6 @@ meta_window_edge_resistance_for_resize (MetaWindow *window,
proposed_outer_height);
window->display->grab_last_user_action_was_snap = snap;
is_resize = TRUE;
if (apply_edge_resistance_to_each_side (window->display,
window,
&old_outer,
@@ -1243,7 +1285,7 @@ meta_window_edge_resistance_for_resize (MetaWindow *window,
timeout_func,
snap,
is_keyboard_op,
is_resize))
TRUE))
{
*new_width = new_outer.width;
*new_height = new_outer.height;

View File

@@ -2221,12 +2221,14 @@ process_mouse_move_resize_grab (MetaDisplay *display,
if (event->keyval == CLUTTER_KEY_Escape)
{
MetaTileMode tile_mode;
/* Hide the tiling preview if necessary */
if (window->tile_mode != META_TILE_NONE)
if (screen->preview_tile_mode != META_TILE_NONE)
meta_screen_hide_tile_preview (screen);
/* Restore the original tile mode */
window->tile_mode = display->grab_tile_mode;
tile_mode = display->grab_tile_mode;
window->tile_monitor_number = display->grab_tile_monitor_number;
/* End move or resize and restore to original state. If the
@@ -2234,10 +2236,13 @@ process_mouse_move_resize_grab (MetaDisplay *display,
* need to remaximize it. In normal cases, we need to do a
* moveresize now to get the position back to the original.
*/
if (window->shaken_loose || window->tile_mode == META_TILE_MAXIMIZED)
if (window->shaken_loose || tile_mode == META_TILE_MAXIMIZED)
meta_window_maximize (window, META_MAXIMIZE_BOTH);
else if (window->tile_mode != META_TILE_NONE)
meta_window_tile (window);
else if (tile_mode != META_TILE_NONE)
meta_window_restore_tile (window,
tile_mode,
display->grab_initial_window_pos.width,
display->grab_initial_window_pos.height);
else
meta_window_move_resize_frame (display->grab_window,
TRUE,
@@ -3184,7 +3189,6 @@ handle_toggle_tiled (MetaDisplay *display,
else if (meta_window_can_tile_side_by_side (window))
{
window->tile_monitor_number = window->monitor->number;
window->tile_mode = mode;
/* Maximization constraints beat tiling constraints, so if the window
* is maximized, tiling won't have any effect unless we unmaximize it
* horizontally first; rather than calling meta_window_unmaximize(),
@@ -3192,7 +3196,7 @@ handle_toggle_tiled (MetaDisplay *display,
* save an additional roundtrip.
*/
window->maximized_horizontally = FALSE;
meta_window_tile (window);
meta_window_tile (window, mode);
}
}

View File

@@ -56,6 +56,7 @@ struct _MetaScreen
MetaUI *ui;
guint tile_preview_timeout_id;
guint preview_tile_mode : 2;
MetaWorkspace *active_workspace;

View File

@@ -308,6 +308,7 @@ set_supported_hint (MetaScreen *screen)
screen->display->atom__GTK_FRAME_EXTENTS,
screen->display->atom__GTK_SHOW_WINDOW_MENU,
screen->display->atom__GTK_EDGE_CONSTRAINTS,
};
XChangeProperty (screen->display->xdisplay, screen->xroot,
@@ -1410,7 +1411,7 @@ meta_screen_update_tile_preview_timeout (gpointer data)
if (window)
{
switch (window->tile_mode)
switch (screen->preview_tile_mode)
{
case META_TILE_LEFT:
case META_TILE_RIGHT:
@@ -1435,7 +1436,7 @@ meta_screen_update_tile_preview_timeout (gpointer data)
int monitor;
monitor = meta_window_get_current_tile_monitor_number (window);
meta_window_get_current_tile_area (window, &tile_rect);
meta_window_get_tile_area (window, screen->preview_tile_mode, &tile_rect);
meta_compositor_show_tile_preview (screen->display->compositor,
window, &tile_rect, monitor);
}
@@ -1479,6 +1480,7 @@ meta_screen_hide_tile_preview (MetaScreen *screen)
g_source_remove (screen->tile_preview_timeout_id);
screen->tile_preview_timeout_id = 0;
screen->preview_tile_mode = META_TILE_NONE;
meta_compositor_hide_tile_preview (screen->display->compositor);
}

View File

@@ -129,6 +129,13 @@ typedef struct _MetaPlacementRule
int height;
} MetaPlacementRule;
typedef enum
{
META_EDGE_CONSTRAINT_NONE = 0,
META_EDGE_CONSTRAINT_WINDOW = 1,
META_EDGE_CONSTRAINT_MONITOR = 2,
} MetaEdgeConstraint;
struct _MetaWindow
{
GObject parent_instance;
@@ -199,14 +206,21 @@ struct _MetaWindow
guint maximize_vertically_after_placement : 1;
guint minimize_after_placement : 1;
/* The current or requested tile mode. If maximized_vertically is true,
* this is the current mode. If not, it is the mode which will be
* requested after the window grab is released */
/* The current tile mode */
guint tile_mode : 2;
/* The last "full" maximized/unmaximized state. We need to keep track of
* that to toggle between normal/tiled or maximized/tiled states. */
guint saved_maximize : 1;
int tile_monitor_number;
/* 0 - top
* 1 - right
* 2 - bottom
* 3 - left */
MetaEdgeConstraint edge_constraints[4];
double tile_hfraction;
int preferred_output_winsys_id;
/* Whether we're shaded */
@@ -559,7 +573,7 @@ struct _MetaWindowClass
#define META_WINDOW_TILED_MAXIMIZED(w)(META_WINDOW_MAXIMIZED(w) && \
(w)->tile_mode == META_TILE_MAXIMIZED)
#define META_WINDOW_ALLOWS_MOVE(w) ((w)->has_move_func && !(w)->fullscreen)
#define META_WINDOW_ALLOWS_RESIZE_EXCEPT_HINTS(w) ((w)->has_resize_func && !META_WINDOW_MAXIMIZED (w) && !META_WINDOW_TILED_SIDE_BY_SIDE(w) && !(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)))
@@ -579,7 +593,12 @@ void meta_window_unmanage (MetaWindow *window,
guint32 timestamp);
void meta_window_queue (MetaWindow *window,
guint queuebits);
void meta_window_tile (MetaWindow *window);
void meta_window_tile (MetaWindow *window,
MetaTileMode mode);
void meta_window_restore_tile (MetaWindow *window,
MetaTileMode mode,
int width,
int height);
void meta_window_maximize_internal (MetaWindow *window,
MetaMaximizeFlags directions,
MetaRectangle *saved_rect);
@@ -648,8 +667,9 @@ void meta_window_get_work_area_for_logical_monitor (MetaWindow *window,
MetaRectangle *area);
int meta_window_get_current_tile_monitor_number (MetaWindow *window);
void meta_window_get_current_tile_area (MetaWindow *window,
MetaRectangle *tile_area);
void meta_window_get_tile_area (MetaWindow *window,
MetaTileMode mode,
MetaRectangle *tile_area);
gboolean meta_window_same_application (MetaWindow *window,

View File

@@ -133,6 +133,10 @@ static void set_workspace_state (MetaWindow *window,
gboolean on_all_workspaces,
MetaWorkspace *workspace);
static MetaWindow * meta_window_find_tile_match (MetaWindow *window,
MetaTileMode mode);
static void update_edge_constraints (MetaWindow *window);
/* Idle handlers for the three queues (run with meta_later_add()). The
* "data" parameter in each case will be a GINT_TO_POINTER of the
* index into the queue arrays to use.
@@ -1017,6 +1021,7 @@ _meta_window_shared_new (MetaDisplay *display,
window->on_all_workspaces_requested = FALSE;
window->tile_mode = META_TILE_NONE;
window->tile_monitor_number = -1;
window->tile_hfraction = -1.;
window->shaded = FALSE;
window->initially_iconic = FALSE;
window->minimized = FALSE;
@@ -2761,6 +2766,9 @@ meta_window_maximize_internal (MetaWindow *window,
window->maximized_vertically =
window->maximized_vertically || maximize_vertically;
/* Update the edge constraints */
update_edge_constraints (window);;
meta_window_recalc_features (window);
set_net_wm_state (window);
@@ -2968,12 +2976,122 @@ meta_window_requested_dont_bypass_compositor (MetaWindow *window)
return window->bypass_compositor == _NET_WM_BYPASS_COMPOSITOR_HINT_OFF;
}
static void
meta_window_get_tile_fraction (MetaWindow *window,
MetaTileMode tile_mode,
double *fraction)
{
MetaWindow *tile_match;
/* Make sure the tile match is up-to-date and matches the
* passed in mode rather than the current state
*/
tile_match = meta_window_find_tile_match (window, tile_mode);
if (tile_mode == META_TILE_NONE)
*fraction = -1.;
else if (tile_mode == META_TILE_MAXIMIZED)
*fraction = 1.;
else if (tile_match)
*fraction = 1. - tile_match->tile_hfraction;
else if (META_WINDOW_TILED_SIDE_BY_SIDE (window))
{
if (window->tile_mode != tile_mode)
*fraction = 1. - window->tile_hfraction;
else
*fraction = window->tile_hfraction;
}
else
*fraction = .5;
}
static void
meta_window_update_tile_fraction (MetaWindow *window,
int new_w,
int new_h)
{
MetaWindow *tile_match = window->tile_match;
MetaRectangle work_area;
if (!META_WINDOW_TILED_SIDE_BY_SIDE (window))
return;
meta_window_get_work_area_for_monitor (window,
window->tile_monitor_number,
&work_area);
window->tile_hfraction = (double)new_w / work_area.width;
if (tile_match && window->display->grab_window == window)
meta_window_tile (tile_match, tile_match->tile_mode);
}
static void
update_edge_constraints (MetaWindow *window)
{
switch (window->tile_mode)
{
case META_TILE_NONE:
window->edge_constraints[0] = META_EDGE_CONSTRAINT_NONE;
window->edge_constraints[1] = META_EDGE_CONSTRAINT_NONE;
window->edge_constraints[2] = META_EDGE_CONSTRAINT_NONE;
window->edge_constraints[3] = META_EDGE_CONSTRAINT_NONE;
break;
case META_TILE_MAXIMIZED:
window->edge_constraints[0] = META_EDGE_CONSTRAINT_MONITOR;
window->edge_constraints[1] = META_EDGE_CONSTRAINT_MONITOR;
window->edge_constraints[2] = META_EDGE_CONSTRAINT_MONITOR;
window->edge_constraints[3] = META_EDGE_CONSTRAINT_MONITOR;
break;
case META_TILE_LEFT:
window->edge_constraints[0] = META_EDGE_CONSTRAINT_MONITOR;
if (window->tile_match)
window->edge_constraints[1] = META_EDGE_CONSTRAINT_WINDOW;
else
window->edge_constraints[1] = META_EDGE_CONSTRAINT_NONE;
window->edge_constraints[2] = META_EDGE_CONSTRAINT_MONITOR;
window->edge_constraints[3] = META_EDGE_CONSTRAINT_MONITOR;
break;
case META_TILE_RIGHT:
window->edge_constraints[0] = META_EDGE_CONSTRAINT_MONITOR;
window->edge_constraints[1] = META_EDGE_CONSTRAINT_MONITOR;
window->edge_constraints[2] = META_EDGE_CONSTRAINT_MONITOR;
if (window->tile_match)
window->edge_constraints[3] = META_EDGE_CONSTRAINT_WINDOW;
else
window->edge_constraints[3] = META_EDGE_CONSTRAINT_NONE;
break;
}
/* h/vmaximize also modify the edge constraints */
if (window->maximized_vertically)
{
window->edge_constraints[0] = META_EDGE_CONSTRAINT_MONITOR;
window->edge_constraints[2] = META_EDGE_CONSTRAINT_MONITOR;
}
if (window->maximized_horizontally)
{
window->edge_constraints[1] = META_EDGE_CONSTRAINT_MONITOR;
window->edge_constraints[3] = META_EDGE_CONSTRAINT_MONITOR;
}
}
void
meta_window_tile (MetaWindow *window)
meta_window_tile (MetaWindow *window,
MetaTileMode tile_mode)
{
MetaMaximizeFlags directions;
MetaRectangle old_frame_rect, old_buffer_rect;
meta_window_get_tile_fraction (window, tile_mode, &window->tile_hfraction);
window->tile_mode = tile_mode;
/* Don't do anything if no tiling is requested */
if (window->tile_mode == META_TILE_NONE)
return;
@@ -2986,6 +3104,9 @@ meta_window_tile (MetaWindow *window)
meta_window_maximize_internal (window, directions, NULL);
meta_screen_update_tile_preview (window->screen, FALSE);
/* Setup the edge constraints */
update_edge_constraints (window);
meta_window_get_frame_rect (window, &old_frame_rect);
meta_window_get_buffer_rect (window, &old_buffer_rect);
@@ -3004,6 +3125,16 @@ meta_window_tile (MetaWindow *window)
meta_frame_queue_draw (window->frame);
}
void
meta_window_restore_tile (MetaWindow *window,
MetaTileMode mode,
int width,
int height)
{
meta_window_update_tile_fraction (window, width, height);
meta_window_tile (window, mode);
}
static gboolean
meta_window_can_tile_maximized (MetaWindow *window)
{
@@ -3102,6 +3233,9 @@ meta_window_unmaximize (MetaWindow *window,
meta_window_get_frame_rect (window, &old_frame_rect);
meta_window_get_buffer_rect (window, &old_buffer_rect);
if (unmaximize_vertically)
window->tile_mode = META_TILE_NONE;
meta_topic (META_DEBUG_WINDOW_OPS,
"Unmaximizing %s%s\n",
window->desc,
@@ -3114,6 +3248,9 @@ meta_window_unmaximize (MetaWindow *window,
window->maximized_vertically =
window->maximized_vertically && !unmaximize_vertically;
/* Update the edge constraints */
update_edge_constraints (window);
/* recalc_features() will eventually clear the cached frame
* extents, but we need the correct frame extents in the code below,
* so invalidate the old frame extents manually up front.
@@ -3977,6 +4114,9 @@ meta_window_move_to_monitor (MetaWindow *window,
{
MetaRectangle old_area, new_area;
if (window->tile_mode != META_TILE_NONE)
window->tile_monitor_number = monitor;
meta_window_get_work_area_for_monitor (window,
window->monitor->number,
&old_area);
@@ -3998,15 +4138,40 @@ meta_window_move_to_monitor (MetaWindow *window,
meta_window_move_between_rects (window, &old_area, &new_area);
}
if (window->tile_mode != META_TILE_NONE)
window->tile_monitor_number = monitor;
window->preferred_output_winsys_id = window->monitor->winsys_id;
if (window->fullscreen || window->override_redirect)
meta_screen_queue_check_fullscreen (window->screen);
}
static void
adjust_size_for_tile_match (MetaWindow *window,
int *new_w,
int *new_h)
{
MetaRectangle work_area, rect;
MetaWindow *tile_match = window->tile_match;
if (!META_WINDOW_TILED_SIDE_BY_SIDE (window) || !tile_match)
return;
meta_window_get_work_area_for_monitor (window, window->tile_monitor_number, &work_area);
/* Make sure the resize does not break minimum sizes */
rect = work_area;
rect.width = *new_w;
meta_window_frame_rect_to_client_rect (window, &rect, &rect);
*new_w += MAX(0, window->size_hints.min_width - rect.width);
/* Make sure we're not resizing the tile match below its min width */
rect = work_area;
rect.width = work_area.width - *new_w;
meta_window_frame_rect_to_client_rect (tile_match, &rect, &rect);
*new_w -= MAX(0, tile_match->size_hints.min_width - rect.width);
}
void
meta_window_resize_frame_with_gravity (MetaWindow *window,
gboolean user_op,
@@ -4020,6 +4185,16 @@ meta_window_resize_frame_with_gravity (MetaWindow *window,
rect.width = w;
rect.height = h;
if (user_op)
{
/* When resizing in-tandem with a tile match, we need to respect
* its minimum width
*/
if (window->display->grab_window == window)
adjust_size_for_tile_match (window, &w, &h);
meta_window_update_tile_fraction (window, w, h);
}
flags = (user_op ? META_MOVE_RESIZE_USER_ACTION : 0) | META_MOVE_RESIZE_RESIZE_ACTION;
meta_window_move_resize_internal (window, flags, gravity, rect);
}
@@ -4764,7 +4939,13 @@ meta_window_raise (MetaWindow *window)
* the child windows appropriately.
*/
if (window->screen->stack == ancestor->screen->stack)
meta_stack_raise (window->screen->stack, ancestor);
{
/* If the window has a tile sibling, raise it before raising the window itself */
if (window->tile_match)
meta_stack_raise (window->tile_match->screen->stack, window->tile_match);
meta_stack_raise (window->screen->stack, ancestor);
}
else
{
meta_warning (
@@ -4796,6 +4977,10 @@ meta_window_lower (MetaWindow *window)
meta_topic (META_DEBUG_WINDOW_OPS,
"Lowering window %s\n", window->desc);
/* If the window has a tile sibling, lower it before loweting the window itself */
if (window->tile_match)
meta_stack_lower (window->tile_match->screen->stack, window->tile_match);
meta_stack_lower (window->screen->stack, window);
}
@@ -5713,6 +5898,7 @@ update_move_maybe_tile (MetaWindow *window,
MetaMonitorManager *monitor_manager =
meta_backend_get_monitor_manager (backend);
MetaLogicalMonitor *logical_monitor;
MetaScreen *screen = window->screen;
MetaRectangle work_area;
/* For side-by-side tiling we are interested in the inside vertical
@@ -5742,18 +5928,18 @@ update_move_maybe_tile (MetaWindow *window,
*/
if (meta_window_can_tile_side_by_side (window) &&
x >= logical_monitor->rect.x && x < (work_area.x + shake_threshold))
window->tile_mode = META_TILE_LEFT;
screen->preview_tile_mode = META_TILE_LEFT;
else if (meta_window_can_tile_side_by_side (window) &&
x >= work_area.x + work_area.width - shake_threshold &&
x < (logical_monitor->rect.x + logical_monitor->rect.width))
window->tile_mode = META_TILE_RIGHT;
screen->preview_tile_mode = META_TILE_RIGHT;
else if (meta_window_can_tile_maximized (window) &&
y >= logical_monitor->rect.y && y <= work_area.y)
window->tile_mode = META_TILE_MAXIMIZED;
screen->preview_tile_mode = META_TILE_MAXIMIZED;
else
window->tile_mode = META_TILE_NONE;
screen->preview_tile_mode = META_TILE_NONE;
if (window->tile_mode != META_TILE_NONE)
if (screen->preview_tile_mode != META_TILE_NONE)
window->tile_monitor_number = logical_monitor->number;
}
@@ -5768,6 +5954,7 @@ update_move (MetaWindow *window,
MetaRectangle old;
int shake_threshold;
MetaDisplay *display = window->display;
MetaScreen *screen = window->screen;
display->grab_latest_motion_x = x;
display->grab_latest_motion_y = y;
@@ -5805,7 +5992,7 @@ update_move (MetaWindow *window,
{
/* We don't want to tile while snapping. Also, clear any previous tile
request. */
window->tile_mode = META_TILE_NONE;
screen->preview_tile_mode = META_TILE_NONE;
window->tile_monitor_number = -1;
}
else if (meta_prefs_get_edge_tiling () &&
@@ -5921,8 +6108,8 @@ update_move (MetaWindow *window,
* trigger it unwittingly, e.g. when shaking loose the window or moving
* it to another monitor.
*/
meta_screen_update_tile_preview (window->screen,
window->tile_mode != META_TILE_NONE);
meta_screen_update_tile_preview (screen,
screen->preview_tile_mode != META_TILE_NONE);
meta_window_get_frame_rect (window, &old);
@@ -6089,20 +6276,21 @@ update_resize (MetaWindow *window,
}
static void
update_tile_mode (MetaWindow *window)
maybe_maximize_tiled_window (MetaWindow *window)
{
switch (window->tile_mode)
{
case META_TILE_LEFT:
case META_TILE_RIGHT:
if (!META_WINDOW_TILED_SIDE_BY_SIDE (window))
window->tile_mode = META_TILE_NONE;
break;
case META_TILE_MAXIMIZED:
if (!META_WINDOW_MAXIMIZED (window))
window->tile_mode = META_TILE_NONE;
break;
}
MetaRectangle work_area;
gint shake_threshold;
if (!META_WINDOW_TILED_SIDE_BY_SIDE (window))
return;
shake_threshold = meta_prefs_get_drag_threshold ();
meta_window_get_work_area_for_monitor (window,
window->tile_monitor_number,
&work_area);
if (window->rect.width >= work_area.width - shake_threshold)
meta_window_maximize (window, META_MAXIMIZE_BOTH);
}
void
@@ -6135,8 +6323,8 @@ end_grab_op (MetaWindow *window,
{
if (meta_grab_op_is_moving (window->display->grab_op))
{
if (window->tile_mode != META_TILE_NONE)
meta_window_tile (window);
if (window->screen->preview_tile_mode != META_TILE_NONE)
meta_window_tile (window, window->screen->preview_tile_mode);
else
update_move (window,
modifiers & CLUTTER_SHIFT_MASK,
@@ -6145,20 +6333,13 @@ end_grab_op (MetaWindow *window,
else if (meta_grab_op_is_resizing (window->display->grab_op))
{
update_resize (window,
modifiers & CLUTTER_SHIFT_MASK,
modifiers & CLUTTER_SHIFT_MASK || window->tile_match != NULL,
x, y,
TRUE);
/* If a tiled window has been dragged free with a
* mouse resize without snapping back to the tiled
* state, it will end up with an inconsistent tile
* mode on mouse release; cleaning the mode earlier
* would break the ability to snap back to the tiled
* state, so we wait until mouse release.
*/
update_tile_mode (window);
maybe_maximize_tiled_window (window);
}
}
window->screen->preview_tile_mode = META_TILE_NONE;
meta_display_end_grab_op (window->display, clutter_event_get_time (event));
}
@@ -6229,7 +6410,7 @@ meta_window_handle_mouse_grab_op_event (MetaWindow *window,
else if (meta_grab_op_is_resizing (window->display->grab_op))
{
update_resize (window,
modifier_state & CLUTTER_SHIFT_MASK,
modifier_state & CLUTTER_SHIFT_MASK || window->tile_match != NULL,
x, y,
FALSE);
}
@@ -6362,23 +6543,26 @@ meta_window_get_current_tile_monitor_number (MetaWindow *window)
}
void
meta_window_get_current_tile_area (MetaWindow *window,
MetaRectangle *tile_area)
meta_window_get_tile_area (MetaWindow *window,
MetaTileMode tile_mode,
MetaRectangle *tile_area)
{
MetaRectangle work_area;
int tile_monitor_number;
double fraction;
g_return_if_fail (window->tile_mode != META_TILE_NONE);
g_return_if_fail (tile_mode != META_TILE_NONE);
tile_monitor_number = meta_window_get_current_tile_monitor_number (window);
meta_window_get_work_area_for_monitor (window, tile_monitor_number, tile_area);
meta_window_get_work_area_for_monitor (window, tile_monitor_number, &work_area);
meta_window_get_tile_fraction (window, tile_mode, &fraction);
if (window->tile_mode == META_TILE_LEFT ||
window->tile_mode == META_TILE_RIGHT)
tile_area->width /= 2;
*tile_area = work_area;
tile_area->width = round (tile_area->width * fraction);
if (window->tile_mode == META_TILE_RIGHT)
tile_area->x += tile_area->width;
if (tile_mode == META_TILE_RIGHT)
tile_area->x += work_area.width - tile_area->width;
}
gboolean
@@ -7511,22 +7695,27 @@ meta_window_get_tile_match (MetaWindow *window)
void
meta_window_compute_tile_match (MetaWindow *window)
{
window->tile_match = meta_window_find_tile_match (window, window->tile_mode);
}
static MetaWindow *
meta_window_find_tile_match (MetaWindow *window,
MetaTileMode current_mode)
{
MetaWindow *match;
MetaStack *stack;
MetaTileMode match_tile_mode = META_TILE_NONE;
window->tile_match = NULL;
if (window->shaded || window->minimized)
return;
return NULL;
if (META_WINDOW_TILED_LEFT (window))
if (current_mode == META_TILE_LEFT)
match_tile_mode = META_TILE_RIGHT;
else if (META_WINDOW_TILED_RIGHT (window))
else if (current_mode == META_TILE_RIGHT)
match_tile_mode = META_TILE_LEFT;
else
return;
return NULL;
stack = window->screen->stack;
@@ -7537,7 +7726,7 @@ meta_window_compute_tile_match (MetaWindow *window)
if (!match->shaded &&
!match->minimized &&
match->tile_mode == match_tile_mode &&
match->monitor == window->monitor &&
match->tile_monitor_number == window->tile_monitor_number &&
meta_window_get_workspace (match) == meta_window_get_workspace (window))
break;
}
@@ -7560,6 +7749,23 @@ meta_window_compute_tile_match (MetaWindow *window)
meta_window_get_frame_rect (bottommost, &bottommost_rect);
meta_window_get_frame_rect (topmost, &topmost_rect);
/*
* If we are looking for a tile match while actually being tiled,
* rather than a match for a potential tile mode, then discard
* windows with too much gap or overlap
*/
if (window->tile_mode == current_mode &&
!(meta_grab_op_is_resizing (window->display->grab_op) &&
window->display->grab_window == window &&
window->tile_match != NULL))
{
int threshold = meta_prefs_get_drag_threshold ();
if (ABS (topmost_rect.x - bottommost_rect.x - bottommost_rect.width) > threshold &&
ABS (bottommost_rect.x - topmost_rect.x - topmost_rect.width) > threshold)
return NULL;
}
/*
* If there's a window stacked in between which is partially visible
* behind the topmost tile we don't consider the tiles to match.
@@ -7577,11 +7783,11 @@ meta_window_compute_tile_match (MetaWindow *window)
if (meta_rectangle_overlap (&above_rect, &bottommost_rect) &&
meta_rectangle_overlap (&above_rect, &topmost_rect))
return;
return NULL;
}
window->tile_match = match;
}
return match;
}
void

View File

@@ -1621,6 +1621,9 @@ get_control (MetaUIFrame *frame, int root_x, int root_y)
has_vert = (flags & META_FRAME_ALLOWS_VERTICAL_RESIZE) != 0;
has_horiz = (flags & META_FRAME_ALLOWS_HORIZONTAL_RESIZE) != 0;
if (flags & META_FRAME_TILED_LEFT || flags & META_FRAME_TILED_RIGHT)
has_vert = has_horiz = FALSE;
if (POINT_IN_RECT (x, y, fgeom.title_rect))
{
if (has_vert && y <= TOP_RESIZE_HEIGHT && has_north_resize)
@@ -1693,12 +1696,12 @@ get_control (MetaUIFrame *frame, int root_x, int root_y)
}
else if (x <= fgeom.borders.total.left)
{
if (has_horiz)
if (has_horiz || flags & META_FRAME_TILED_RIGHT)
return META_FRAME_CONTROL_RESIZE_W;
}
else if (x >= (fgeom.width - fgeom.borders.total.right))
{
if (has_horiz)
if (has_horiz || flags & META_FRAME_TILED_LEFT)
return META_FRAME_CONTROL_RESIZE_E;
}

View File

@@ -148,31 +148,124 @@ gtk_surface_surface_destroyed (MetaWaylandGtkSurface *gtk_surface)
}
static void
fill_states (struct wl_array *states,
MetaWindow *window)
fill_edge_states (struct wl_array *states,
MetaWindow *window)
{
uint32_t *s;
if (window->tile_mode == META_TILE_LEFT ||
window->tile_mode == META_TILE_RIGHT)
/* Top */
if (window->edge_constraints[0] != META_EDGE_CONSTRAINT_MONITOR)
{
s = wl_array_add (states, sizeof *s);
*s = GTK_SURFACE1_EDGE_CONSTRAINT_RESIZABLE_TOP;
}
/* Right */
if (window->edge_constraints[1] != META_EDGE_CONSTRAINT_MONITOR)
{
s = wl_array_add (states, sizeof *s);
*s = GTK_SURFACE1_EDGE_CONSTRAINT_RESIZABLE_RIGHT;
}
/* Bottom */
if (window->edge_constraints[2] != META_EDGE_CONSTRAINT_MONITOR)
{
s = wl_array_add (states, sizeof *s);
*s = GTK_SURFACE1_EDGE_CONSTRAINT_RESIZABLE_BOTTOM;
}
/* Left */
if (window->edge_constraints[3] != META_EDGE_CONSTRAINT_MONITOR)
{
s = wl_array_add (states, sizeof *s);
*s = GTK_SURFACE1_EDGE_CONSTRAINT_RESIZABLE_LEFT;
}
}
static void
send_configure_edges (MetaWaylandGtkSurface *gtk_surface,
MetaWindow *window)
{
struct wl_array edge_states;
wl_array_init (&edge_states);
fill_edge_states (&edge_states, window);
gtk_surface1_send_configure_edges (gtk_surface->resource, &edge_states);
wl_array_release (&edge_states);
}
static void
fill_states (struct wl_array *states,
MetaWindow *window,
struct wl_resource *resource)
{
uint32_t *s;
guint version;
version = wl_resource_get_version (resource);
if (version < GTK_SURFACE1_CONFIGURE_EDGES_SINCE_VERSION &&
(window->tile_mode == META_TILE_LEFT ||
window->tile_mode == META_TILE_RIGHT))
{
s = wl_array_add (states, sizeof *s);
*s = GTK_SURFACE1_STATE_TILED;
}
if (version >= GTK_SURFACE1_STATE_TILED_TOP_SINCE_VERSION &&
window->edge_constraints[0] != META_EDGE_CONSTRAINT_NONE)
{
s = wl_array_add (states, sizeof *s);
*s = GTK_SURFACE1_STATE_TILED_TOP;
}
if (version >= GTK_SURFACE1_STATE_TILED_RIGHT_SINCE_VERSION &&
window->edge_constraints[1] != META_EDGE_CONSTRAINT_NONE)
{
s = wl_array_add (states, sizeof *s);
*s = GTK_SURFACE1_STATE_TILED_RIGHT;
}
if (version >= GTK_SURFACE1_STATE_TILED_BOTTOM_SINCE_VERSION &&
window->edge_constraints[2] != META_EDGE_CONSTRAINT_NONE)
{
s = wl_array_add (states, sizeof *s);
*s = GTK_SURFACE1_STATE_TILED_BOTTOM;
}
if (version >= GTK_SURFACE1_STATE_TILED_LEFT_SINCE_VERSION &&
window->edge_constraints[3] != META_EDGE_CONSTRAINT_NONE)
{
s = wl_array_add (states, sizeof *s);
*s = GTK_SURFACE1_STATE_TILED_LEFT;
}
}
static void
send_configure (MetaWaylandGtkSurface *gtk_surface,
MetaWindow *window)
{
struct wl_array states;
wl_array_init (&states);
fill_states (&states, window, gtk_surface->resource);
gtk_surface1_send_configure (gtk_surface->resource, &states);
wl_array_release (&states);
}
static void
on_configure (MetaWaylandSurface *surface,
MetaWaylandGtkSurface *gtk_surface)
{
struct wl_array states;
send_configure (gtk_surface, surface->window);
wl_array_init (&states);
fill_states (&states, surface->window);
gtk_surface1_send_configure (gtk_surface->resource, &states);
wl_array_release (&states);
if (wl_resource_get_version (gtk_surface->resource) >= GTK_SURFACE1_CONFIGURE_EDGES_SINCE_VERSION)
send_configure_edges (gtk_surface, surface->window);
}
static void

View File

@@ -42,7 +42,7 @@
#define META_WL_SEAT_VERSION 5
#define META_WL_OUTPUT_VERSION 2
#define META_XSERVER_VERSION 1
#define META_GTK_SHELL1_VERSION 1
#define META_GTK_SHELL1_VERSION 2
#define META_WL_SUBCOMPOSITOR_VERSION 1
#define META_ZWP_POINTER_GESTURES_V1_VERSION 1
#define META_ZXDG_EXPORTER_V1_VERSION 1

View File

@@ -1,6 +1,6 @@
<protocol name="gtk">
<interface name="gtk_shell1" version="1">
<interface name="gtk_shell1" version="2">
<description summary="gtk specific extensions">
gtk_shell is a protocol extension providing additional features for
clients implementing it.
@@ -30,7 +30,7 @@
</request>
</interface>
<interface name="gtk_surface1" version="1">
<interface name="gtk_surface1" version="2">
<request name="set_dbus_properties">
<arg name="application_id" type="string" allow-null="true"/>
<arg name="app_menu_path" type="string" allow-null="true"/>
@@ -51,11 +51,27 @@
<enum name="state">
<entry name="tiled" value="1"/>
<entry name="tiled_top" value="2" since="2" />
<entry name="tiled_right" value="3" since="2" />
<entry name="tiled_bottom" value="4" since="2" />
<entry name="tiled_left" value="5" since="2" />
</enum>
<enum name="edge_constraint" since="2">
<entry name="resizable_top" value="1"/>
<entry name="resizable_right" value="2"/>
<entry name="resizable_bottom" value="3"/>
<entry name="resizable_left" value="4"/>
</enum>
<event name="configure">
<arg name="states" type="array"/>
</event>
<event name="configure_edges" since="2">
<arg name="constraints" type="array"/>
</event>
</interface>
</protocol>

View File

@@ -63,6 +63,7 @@ item(_GTK_APP_MENU_OBJECT_PATH)
item(_GTK_MENUBAR_OBJECT_PATH)
item(_GTK_FRAME_EXTENTS)
item(_GTK_SHOW_WINDOW_MENU)
item(_GTK_EDGE_CONSTRAINTS)
item(_GNOME_WM_KEYBINDINGS)
item(_GNOME_PANEL_ACTION)
item(_GNOME_PANEL_ACTION_MAIN_MENU)

View File

@@ -902,6 +902,31 @@ update_net_frame_extents (MetaWindow *window)
meta_error_trap_pop (window->display);
}
static void
update_gtk_edge_constraints (MetaWindow *window)
{
MetaEdgeConstraint *constraints = window->edge_constraints;
unsigned long data[1];
/* Edge constraints */
data[0] = (constraints[0] != META_EDGE_CONSTRAINT_NONE ? 1 : 0) << 0 |
(constraints[0] != META_EDGE_CONSTRAINT_MONITOR ? 1 : 0) << 1 |
(constraints[1] != META_EDGE_CONSTRAINT_NONE ? 1 : 0) << 2 |
(constraints[1] != META_EDGE_CONSTRAINT_MONITOR ? 1 : 0) << 3 |
(constraints[2] != META_EDGE_CONSTRAINT_NONE ? 1 : 0) << 4 |
(constraints[2] != META_EDGE_CONSTRAINT_MONITOR ? 1 : 0) << 5 |
(constraints[3] != META_EDGE_CONSTRAINT_NONE ? 1 : 0) << 6 |
(constraints[3] != META_EDGE_CONSTRAINT_MONITOR ? 1 : 0) << 7;
meta_verbose ("Setting _GTK_EDGE_CONSTRAINTS to %lu\n", data[0]);
XChangeProperty (window->display->xdisplay,
window->xwindow,
window->display->atom__GTK_EDGE_CONSTRAINTS,
XA_CARDINAL, 32, PropModeReplace,
(guchar*) data, 1);
}
static gboolean
sync_request_timeout (gpointer data)
{
@@ -1259,6 +1284,8 @@ meta_window_x11_move_resize_internal (MetaWindow *window,
*result |= META_MOVE_RESIZE_RESULT_MOVED;
if (need_resize_client || need_resize_frame)
*result |= META_MOVE_RESIZE_RESULT_RESIZED;
update_gtk_edge_constraints (window);
}
static gboolean
@@ -1670,6 +1697,9 @@ meta_window_x11_set_net_wm_state (MetaWindow *window)
meta_error_trap_pop (window->display);
}
}
/* Edge constraints */
update_gtk_edge_constraints (window);
}
static cairo_region_t *