Commit 0eb770fe authored by Alberts Muktupāvels's avatar Alberts Muktupāvels

Add side-by-side tiling

1. Manually applied this patch:
https://github.com/SolusOS-discontinued/consortium/commit/b463e03f5bdeab307ceee6b969c681f29537c76d
2. Ported tile-preview.c to gtk3.
parent 5be337ce
......@@ -91,6 +91,8 @@ metacity_SOURCES= \
include/resizepopup.h \
ui/tabpopup.c \
include/tabpopup.h \
ui/tile-preview.c \
include/tile-preview.h \
ui/theme-parser.c \
ui/theme-parser.h \
ui/theme.c \
......
......@@ -1793,6 +1793,7 @@ meta_rectangle_find_onscreen_edges (const MetaRectangle *basic_rect,
GList*
meta_rectangle_find_nonintersected_xinerama_edges (
const MetaRectangle *screen_rect,
const GList *xinerama_rects,
const GSList *all_struts)
{
......@@ -1815,99 +1816,41 @@ meta_rectangle_find_nonintersected_xinerama_edges (
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))
{
MetaSide 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_SIDE_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_SIDE_RIGHT;
}
else
/* These rectangles aren't adjacent after all */
x = INT_MIN;
MetaEdge *new_edge;
/* 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))
{
MetaSide 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_SIDE_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_SIDE_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;
if (BOX_LEFT(*cur_rect) != BOX_LEFT(*screen_rect))
{
new_edge = g_new (MetaEdge, 1);
new_edge->rect = meta_rect (BOX_LEFT (*cur_rect), BOX_TOP (*cur_rect), 0, cur_rect->height);
new_edge->side_type = META_SIDE_LEFT;
new_edge->edge_type = META_EDGE_XINERAMA;
ret = g_list_prepend (ret, new_edge);
}
if (BOX_RIGHT(*cur_rect) != BOX_RIGHT(*screen_rect))
{
new_edge = g_new (MetaEdge, 1);
new_edge->rect = meta_rect (BOX_RIGHT (*cur_rect), BOX_TOP (*cur_rect), 0, cur_rect->height);
new_edge->side_type = META_SIDE_RIGHT;
new_edge->edge_type = META_EDGE_XINERAMA;
ret = g_list_prepend (ret, new_edge);
}
if (BOX_TOP(*cur_rect) != BOX_TOP(*screen_rect))
{
new_edge = g_new (MetaEdge, 1);
new_edge->rect = meta_rect (BOX_LEFT (*cur_rect), BOX_TOP (*cur_rect), cur_rect->width, 0);
new_edge->side_type = META_SIDE_TOP;
new_edge->edge_type = META_EDGE_XINERAMA;
ret = g_list_prepend (ret, new_edge);
}
if (BOX_BOTTOM(*cur_rect) != BOX_BOTTOM(*screen_rect))
{
new_edge = g_new (MetaEdge, 1);
new_edge->rect = meta_rect (BOX_LEFT (*cur_rect), BOX_BOTTOM (*cur_rect), cur_rect->width, 0);
new_edge->side_type = META_SIDE_BOTTOM;
new_edge->edge_type = META_EDGE_XINERAMA;
ret = g_list_prepend (ret, new_edge);
}
cur = cur->next;
}
......
......@@ -96,6 +96,7 @@ typedef enum
PRIORITY_ENTIRELY_VISIBLE_ON_WORKAREA = 1,
PRIORITY_SIZE_HINTS_INCREMENTS = 1,
PRIORITY_MAXIMIZATION = 2,
PRIORITY_TILING = 2,
PRIORITY_FULLSCREEN = 2,
PRIORITY_SIZE_HINTS_LIMITS = 3,
PRIORITY_TITLEBAR_VISIBLE = 4,
......@@ -143,6 +144,10 @@ static gboolean constrain_maximization (MetaWindow *window,
ConstraintInfo *info,
ConstraintPriority priority,
gboolean check_only);
static gboolean constrain_tiling (MetaWindow *window,
ConstraintInfo *info,
ConstraintPriority priority,
gboolean check_only);
static gboolean constrain_fullscreen (MetaWindow *window,
ConstraintInfo *info,
ConstraintPriority priority,
......@@ -209,6 +214,7 @@ typedef struct {
static const Constraint all_constraints[] = {
{constrain_maximization, "constrain_maximization"},
{constrain_tiling, "constrain_tiling"},
{constrain_fullscreen, "constrain_fullscreen"},
{constrain_size_increments, "constrain_size_increments"},
{constrain_size_limits, "constrain_size_limits"},
......@@ -742,12 +748,15 @@ constrain_maximization (MetaWindow *window,
return TRUE;
/* Determine whether constraint applies; exit if it doesn't */
if (!window->maximized_horizontally && !window->maximized_vertically)
if ((!window->maximized_horizontally && !window->maximized_vertically) ||
META_WINDOW_TILED_SIDE_BY_SIDE (window))
return TRUE;
/* Calculate target_size = maximized size of (window + frame) */
if (window->maximized_horizontally && window->maximized_vertically)
target_size = info->work_area_xinerama;
if (META_WINDOW_MAXIMIZED (window))
{
target_size = info->work_area_xinerama;
}
else
{
/* Amount of maximization possible in a single direction depends
......@@ -810,6 +819,59 @@ constrain_maximization (MetaWindow *window,
return TRUE;
}
static gboolean
constrain_tiling (MetaWindow *window,
ConstraintInfo *info,
ConstraintPriority priority,
gboolean check_only)
{
MetaRectangle target_size;
MetaRectangle min_size, max_size;
gboolean hminbad, vminbad;
gboolean horiz_equal, vert_equal;
gboolean constraint_already_satisfied;
if (priority > PRIORITY_TILING)
return TRUE;
/* Determine whether constraint applies; exit if it doesn't */
if (!META_WINDOW_TILED_SIDE_BY_SIDE (window))
return TRUE;
/* 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);
unextend_by_frame (&target_size, info->fgeom);
/* Check min size constraints; max size constraints are ignored as for
* maximized windows.
*/
get_size_limits (window, info->fgeom, FALSE, &min_size, &max_size);
hminbad = target_size.width < min_size.width;
vminbad = target_size.height < min_size.height;
if (hminbad || vminbad)
return TRUE;
/* Determine whether constraint is already satisfied; exit if it is */
horiz_equal = target_size.x == info->current.x &&
target_size.width == info->current.width;
vert_equal = target_size.y == info->current.y &&
target_size.height == info->current.height;
constraint_already_satisfied = horiz_equal && vert_equal;
if (check_only || constraint_already_satisfied)
return constraint_already_satisfied;
/*** Enforce constraint ***/
info->current.x = target_size.x;
info->current.width = target_size.width;
info->current.y = target_size.y;
info->current.height = target_size.height;
return TRUE;
}
static gboolean
constrain_fullscreen (MetaWindow *window,
ConstraintInfo *info,
......@@ -861,6 +923,7 @@ constrain_size_increments (MetaWindow *window,
/* Determine whether constraint applies; exit if it doesn't */
if (META_WINDOW_MAXIMIZED (window) || window->fullscreen ||
META_WINDOW_TILED_SIDE_BY_SIDE (window) ||
info->action_type == ACTION_MOVE)
return TRUE;
......@@ -992,6 +1055,7 @@ constrain_aspect_ratio (MetaWindow *window,
constraints_are_inconsistent = minr > maxr;
if (constraints_are_inconsistent ||
META_WINDOW_MAXIMIZED (window) || window->fullscreen ||
META_WINDOW_TILED_SIDE_BY_SIDE (window) ||
info->action_type == ACTION_MOVE)
return TRUE;
......
......@@ -26,6 +26,7 @@
#include "frame-private.h"
#include "workspace.h"
#include "prefs.h"
#include "errors.h"
/* Looks up the MetaWindow representing the frame of the given X window.
* Used as a helper function by a bunch of the functions below.
......@@ -294,6 +295,35 @@ meta_core_user_lower_and_unfocus (Display *xdisplay,
timestamp);
}
void
meta_core_lower_beneath_focus_window (Display *xdisplay,
Window xwindow,
guint32 timestamp)
{
XWindowChanges changes;
MetaDisplay *display;
MetaScreen *screen;
MetaWindow *focus_window;
display = meta_display_for_x_display (xdisplay);
screen = meta_display_screen_for_xwindow (display, xwindow);
focus_window = meta_stack_get_top (screen->stack);
if (focus_window == NULL)
return;
changes.stack_mode = Below;
changes.sibling = focus_window->frame ? focus_window->frame->xwindow
: focus_window->xwindow;
meta_error_trap_push (display);
XConfigureWindow (xdisplay,
xwindow,
CWSibling | CWStackMode,
&changes);
meta_error_trap_pop (display, FALSE);
}
void
meta_core_user_focus (Display *xdisplay,
Window frame_xwindow,
......
......@@ -70,6 +70,13 @@ typedef void (* MetaWindowPingFunc) (MetaDisplay *display,
*/
#define N_IGNORED_SERIALS 4
typedef enum {
META_TILE_NONE,
META_TILE_LEFT,
META_TILE_RIGHT,
META_TILE_MAXIMIZED /* only used for previews */
} MetaTileMode;
struct _MetaDisplay
{
char *name;
......@@ -160,6 +167,8 @@ struct _MetaDisplay
int grab_anchor_root_x;
int grab_anchor_root_y;
MetaRectangle grab_anchor_window_pos;
MetaTileMode grab_tile_mode;
int grab_tile_monitor_number;
int grab_latest_motion_x;
int grab_latest_motion_y;
gulong grab_mask;
......@@ -170,6 +179,9 @@ struct _MetaDisplay
guint grab_frame_action : 1;
MetaRectangle grab_wireframe_rect;
MetaRectangle grab_wireframe_last_xor_rect;
/* During a resize operation, the directions in which we've broken
* out of the initial maximization state */
guint grab_resize_unmaximize : 2; /* MetaMaximizeFlags */
MetaRectangle grab_initial_window_pos;
int grab_initial_x, grab_initial_y; /* These are only relevant for */
gboolean grab_threshold_movement_reached; /* raise_on_click == FALSE. */
......
......@@ -457,6 +457,8 @@ meta_display_open (void)
the_display->grab_window = NULL;
the_display->grab_screen = NULL;
the_display->grab_resize_popup = NULL;
the_display->grab_tile_mode = META_TILE_NONE;
the_display->grab_tile_monitor_number = -1;
the_display->grab_edge_resistance_data = NULL;
......@@ -3308,6 +3310,16 @@ meta_display_begin_grab_op (MetaDisplay *display,
display->grab_xwindow = grab_xwindow;
display->grab_button = button;
display->grab_mask = modmask;
if (window)
{
display->grab_tile_mode = window->tile_mode;
display->grab_tile_monitor_number = window->tile_monitor_number;
}
else
{
display->grab_tile_mode = META_TILE_NONE;
display->grab_tile_monitor_number = -1;
}
display->grab_anchor_root_x = root_x;
display->grab_anchor_root_y = root_y;
display->grab_latest_motion_x = root_x;
......@@ -3493,7 +3505,12 @@ meta_display_end_grab_op (MetaDisplay *display,
if (display->grab_window != NULL)
display->grab_window->shaken_loose = FALSE;
/*if(display->grab_window != NULL && display->grab_window->tile_mode == META_TILE_MAXIMIZED)
{
display->grab_window->tile_mode = META_TILE_NONE;
}*/
if (display->grab_window != NULL &&
!meta_prefs_get_raise_on_click () &&
(meta_grab_op_is_moving (display->grab_op) ||
......@@ -3595,10 +3612,16 @@ meta_display_end_grab_op (MetaDisplay *display,
display->grab_sync_request_alarm = None;
}
#endif /* HAVE_XSYNC */
/* Hide the tile preview if it exists */
if (display->grab_screen->tile_preview)
meta_tile_preview_hide (display->grab_screen->tile_preview);
display->grab_window = NULL;
display->grab_screen = NULL;
display->grab_xwindow = None;
display->grab_tile_mode = META_TILE_NONE;
display->grab_tile_monitor_number = -1;
display->grab_op = META_GRAB_OP_NONE;
if (display->grab_resize_popup)
......
......@@ -280,6 +280,12 @@ meta_frame_get_flags (MetaFrame *frame)
if (META_WINDOW_MAXIMIZED (frame->window))
flags |= META_FRAME_MAXIMIZED;
if (META_WINDOW_TILED_LEFT (frame->window))
flags |= META_FRAME_TILED_LEFT;
if (META_WINDOW_TILED_RIGHT (frame->window))
flags |= META_FRAME_TILED_RIGHT;
if (frame->window->fullscreen)
flags |= META_FRAME_FULLSCREEN;
......
......@@ -1444,6 +1444,10 @@ process_mouse_move_resize_grab (MetaDisplay *display,
if (keysym == XK_Escape)
{
/* Restore the original tile mode */
window->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
* window was a maximized window that had been "shaken loose" we
* need to remaximize it. In normal cases, we need to do a
......@@ -1455,6 +1459,8 @@ process_mouse_move_resize_grab (MetaDisplay *display,
meta_window_maximize (window,
META_MAXIMIZE_HORIZONTAL |
META_MAXIMIZE_VERTICAL);
else if (window->tile_mode == META_TILE_LEFT || window->tile_mode == META_TILE_RIGHT)
meta_window_tile (window);
else if (!display->grab_wireframe_active)
meta_window_move_resize (display->grab_window,
TRUE,
......@@ -2918,6 +2924,43 @@ handle_toggle_above (MetaDisplay *display,
meta_window_make_above (window);
}
/* TODO: actually use this keybinding, without messing up the existing keybinding schema */
static void
handle_toggle_tiled (MetaDisplay *display,
MetaScreen *screen,
MetaWindow *window,
XEvent *event,
MetaKeyBinding *binding)
{
MetaTileMode mode = binding->handler->data;
if ((META_WINDOW_TILED_LEFT (window) && mode == META_TILE_LEFT) ||
(META_WINDOW_TILED_RIGHT (window) && mode == META_TILE_RIGHT))
{
window->tile_mode = META_TILE_NONE;
if (window->saved_maximize)
meta_window_maximize (window, META_MAXIMIZE_VERTICAL |
META_MAXIMIZE_HORIZONTAL);
else
meta_window_unmaximize (window, META_MAXIMIZE_VERTICAL |
META_MAXIMIZE_HORIZONTAL);
}
else if (meta_window_can_tile_side_by_side (window))
{
window->tile_mode = mode;
window->tile_monitor_number = meta_screen_get_xinerama_for_window (window->screen, window)->number;
/* 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(),
* we just set the flag and rely on meta_window_tile() syncing it to
* save an additional roundtrip.
*/
window->maximized_horizontally = FALSE;
meta_window_tile (window);
}
}
static void
handle_toggle_maximized (MetaDisplay *display,
MetaScreen *screen,
......
......@@ -88,6 +88,7 @@ static char *cursor_theme = NULL;
static int cursor_size = 24;
static gboolean compositing_manager = FALSE;
static gboolean resize_with_right_button = FALSE;
static gboolean edge_tiling = FALSE;
static gboolean force_fullscreen = TRUE;
static GDesktopVisualBellType visual_bell_type = G_DESKTOP_VISUAL_BELL_FULLSCREEN_FLASH;
......@@ -353,6 +354,14 @@ static MetaBoolPreference preferences_bool[] =
&resize_with_right_button,
FALSE,
},
{
{ "edge-tiling",
SCHEMA_METACITY,
META_PREF_EDGE_TILING,
},
&edge_tiling,
FALSE,
},
{ { NULL, 0, 0 }, NULL, FALSE },
};
......@@ -1416,6 +1425,9 @@ meta_preference_to_string (MetaPreference pref)
case META_PREF_RESIZE_WITH_RIGHT_BUTTON:
return "RESIZE_WITH_RIGHT_BUTTON";
case META_PREF_EDGE_TILING:
return "EDGE_TILING";
case META_PREF_FORCE_FULLSCREEN:
return "FORCE_FULLSCREEN";
......@@ -1746,6 +1758,12 @@ meta_prefs_get_gnome_animations ()
return gnome_animations;
}
gboolean
meta_prefs_get_edge_tiling ()
{
return edge_tiling;
}
MetaKeyBindingAction
meta_prefs_get_keybinding_action (const char *name)
{
......
......@@ -77,7 +77,10 @@ struct _MetaScreen
MetaRectangle rect; /* Size of screen; rect.x & rect.y are always 0 */
MetaUI *ui;
MetaTabPopup *tab_popup;
MetaTilePreview *tile_preview;
guint tile_preview_timeout_id;
MetaWorkspace *active_workspace;
/* This window holds the focus when we don't want to focus
......@@ -159,6 +162,10 @@ void meta_screen_ensure_tab_popup (MetaScreen *scree
MetaTabShowType show_type);
void meta_screen_ensure_workspace_popup (MetaScreen *screen);
void meta_screen_tile_preview_update (MetaScreen *screen,
gboolean delay);
void meta_screen_tile_preview_hide (MetaScreen *screen);
MetaWindow* meta_screen_get_mouse_window (MetaScreen *screen,
MetaWindow *not_this_one);
......
......@@ -587,7 +587,10 @@ meta_screen_new (MetaDisplay *display,
screen->xscreen);
screen->tab_popup = NULL;
screen->tile_preview = NULL;
screen->tile_preview_timeout_id = 0;
screen->stack = meta_stack_new (screen);
meta_prefs_add_listener (prefs_changed_callback, screen);
......@@ -696,7 +699,13 @@ meta_screen_free (MetaScreen *screen,
if (screen->xinerama_infos)
g_free (screen->xinerama_infos);
if (screen->tile_preview_timeout_id)
g_source_remove (screen->tile_preview_timeout_id);
if (screen->tile_preview)
meta_tile_preview_free (screen->tile_preview);
g_free (screen->screen_name);
g_free (screen);
......@@ -1389,6 +1398,89 @@ meta_screen_ensure_workspace_popup (MetaScreen *screen)
/* don't show tab popup, since proper space isn't selected yet */
}
static gboolean
meta_screen_tile_preview_update_timeout (gpointer data)
{
MetaScreen *screen = data;
MetaWindow *window = screen->display->grab_window;
gboolean composited = screen->display->compositor != NULL;
gboolean needs_preview = FALSE;
screen->tile_preview_timeout_id = 0;
if (!screen->tile_preview)
screen->tile_preview = meta_tile_preview_new (screen->number,
composited);
if (window)
{
switch (window->tile_mode)
{
case META_TILE_LEFT:
case META_TILE_RIGHT:
if (!META_WINDOW_TILED_SIDE_BY_SIDE (window))
needs_preview = TRUE;
break;
case META_TILE_MAXIMIZED:
if (!META_WINDOW_MAXIMIZED (window))
needs_preview = TRUE;
break;
default:
needs_preview = FALSE;
break;
}
}
if (needs_preview)
{
MetaRectangle tile_rect;