summaryrefslogtreecommitdiff
path: root/src/core/edge-resistance.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/edge-resistance.c')
-rw-r--r--src/core/edge-resistance.c1310
1 files changed, 0 insertions, 1310 deletions
diff --git a/src/core/edge-resistance.c b/src/core/edge-resistance.c
deleted file mode 100644
index 3e42936e7..000000000
--- a/src/core/edge-resistance.c
+++ /dev/null
@@ -1,1310 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
-
-/* Edge resistance for move/resize operations */
-
-/*
- * Copyright (C) 2005, 2006 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, see <http://www.gnu.org/licenses/>.
- */
-
-#include "config.h"
-
-#include "core/edge-resistance.h"
-
-#include "core/boxes-private.h"
-#include "core/display-private.h"
-#include "core/meta-workspace-manager-private.h"
-#include "core/workspace-private.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 != 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;
-};
-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 void compute_resistance_and_snapping_edges (MetaDisplay *display);
-
-/* !WARNING!: this function can return invalid indices (namely, either -1 or
- * edges->len); this is by design, but you need to remember this.
- */
-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;
- gboolean edges_align;
-
- /* 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;
- edges_align = meta_rectangle_edge_aligns (new_rect, edge);
- 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;
-
- 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;
-
- 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 (MetaSide side, int increment)
-{
- switch (side)
- {
- case META_SIDE_LEFT:
- case META_SIDE_TOP:
- return increment < 0;
- case META_SIDE_RIGHT:
- case META_SIDE_BOTTOM:
- return increment > 0;
- default:
- g_assert_not_reached ();
- return FALSE;
- }
-}
-
-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 *old_rect,
- const MetaRectangle *new_rect,
- GArray *edges,
- ResistanceDataForAnEdge *resistance_data,
- GSourceFunc timeout_func,
- gboolean xdir,
- gboolean include_windows,
- gboolean keyboard_op)
-{
- int i, begin, end;
- int last_edge;
- gboolean increasing = new_pos > old_pos;
- int increment = increasing ? 1 : -1;
-
- const int PIXEL_DISTANCE_THRESHOLD_TOWARDS_WINDOW = 16;
- const int PIXEL_DISTANCE_THRESHOLD_AWAYFROM_WINDOW = 0;
- const int PIXEL_DISTANCE_THRESHOLD_TOWARDS_MONITOR = 32;
- const int PIXEL_DISTANCE_THRESHOLD_AWAYFROM_MONITOR = 0;
- const int PIXEL_DISTANCE_THRESHOLD_TOWARDS_SCREEN = 32;
- const int PIXEL_DISTANCE_THRESHOLD_AWAYFROM_SCREEN = 0;
- const int TIMEOUT_RESISTANCE_LENGTH_MS_WINDOW = 0;
- const int TIMEOUT_RESISTANCE_LENGTH_MS_MONITOR = 0;
- const int TIMEOUT_RESISTANCE_LENGTH_MS_SCREEN = 0;
-
- /* 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;
- g_clear_handle_id (&resistance_data->timeout_id, g_source_remove);
- }
-
- /* 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);
-
- /* begin and end can be outside the array index, if the window is partially
- * off the screen
- */
- last_edge = edges->len - 1;
- begin = CLAMP (begin, 0, last_edge);
- end = CLAMP (end, 0, last_edge);
-
- /* 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 = meta_rectangle_edge_aligns (new_rect, edge) ||
- meta_rectangle_edge_aligns (old_rect, edge);
-
- /* 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)
- {
- if ((old_pos < compare && compare < new_pos) ||
- (old_pos > compare && compare > new_pos))
- return compare;
- }
- else /* mouse op */
- {
- int threshold;
-
- /* 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:
- if (include_windows)
- timeout_length_ms = TIMEOUT_RESISTANCE_LENGTH_MS_WINDOW;
- break;
- case META_EDGE_MONITOR:
- timeout_length_ms = TIMEOUT_RESISTANCE_LENGTH_MS_MONITOR;
- break;
- case META_EDGE_SCREEN:
- 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);
- g_source_set_name_by_id (resistance_data->timeout_id,
- "[mutter] edge_resistance_timeout");
- 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 */
- threshold = 0;
- switch (edge->edge_type)
- {
- case META_EDGE_WINDOW:
- if (!include_windows)
- break;
- if (movement_towards_edge (edge->side_type, increment))
- threshold = PIXEL_DISTANCE_THRESHOLD_TOWARDS_WINDOW;
- else
- threshold = PIXEL_DISTANCE_THRESHOLD_AWAYFROM_WINDOW;
- break;
- case META_EDGE_MONITOR:
- if (movement_towards_edge (edge->side_type, increment))
- threshold = PIXEL_DISTANCE_THRESHOLD_TOWARDS_MONITOR;
- else
- threshold = PIXEL_DISTANCE_THRESHOLD_AWAYFROM_MONITOR;
- break;
- case META_EDGE_SCREEN:
- if (movement_towards_edge (edge->side_type, increment))
- threshold = PIXEL_DISTANCE_THRESHOLD_TOWARDS_SCREEN;
- else
- threshold = PIXEL_DISTANCE_THRESHOLD_AWAYFROM_SCREEN;
- break;
- }
-
- if (ABS (compare - new_pos) < threshold)
- return compare;
- }
-
- /* Go to the next edge in the range */
- i += increment;
- }
-
- return new_pos;
-}
-
-static int
-apply_edge_snapping (int old_pos,
- int new_pos,
- const MetaRectangle *new_rect,
- GArray *edges,
- gboolean xdir,
- gboolean keyboard_op)
-{
- int snap_to;
-
- if (old_pos == new_pos)
- return new_pos;
-
- snap_to = find_nearest_position (edges,
- new_pos,
- old_pos,
- new_rect,
- xdir,
- keyboard_op);
-
- /* 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 (snap_to - old_pos) >= 8 &&
- ABS (new_pos - old_pos) < 8)
- return old_pos;
- else
- /* Otherwise, return the snapping position found */
- return snap_to;
-}
-
-/* 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,
- MetaEdgeResistanceFlags flags,
- gboolean is_resize)
-{
- MetaEdgeResistanceData *edge_data;
- MetaRectangle modified_rect;
- gboolean modified;
- int new_left, new_right, new_top, new_bottom;
- gboolean auto_snap, keyboard_op;
-
- auto_snap = flags & META_EDGE_RESISTANCE_SNAP;
- keyboard_op = flags & META_EDGE_RESISTANCE_KEYBOARD_OP;
-
- if (display->grab_edge_resistance_data == NULL)
- compute_resistance_and_snapping_edges (display);
-
- edge_data = display->grab_edge_resistance_data;
-
- 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
- * 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,
- TRUE,
- keyboard_op);
-
- new_right = apply_edge_snapping (BOX_RIGHT (*old_outer),
- BOX_RIGHT (*new_outer),
- new_outer,
- 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,
- FALSE,
- keyboard_op);
-
- new_bottom = apply_edge_snapping (BOX_BOTTOM (*old_outer),
- BOX_BOTTOM (*new_outer),
- new_outer,
- edge_data->bottom_edges,
- 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
- {
- gboolean include_windows = flags & META_EDGE_RESISTANCE_WINDOWS;
-
- /* Disable edge resistance for resizes when windows have size
- * increment hints; see #346782. For all other cases, apply
- * them.
- */
- if (!is_resize || window->size_hints.width_inc == 1)
- {
- /* Now, apply the normal horizontal edge resistance */
- new_left = apply_edge_resistance (window,
- BOX_LEFT (*old_outer),
- BOX_LEFT (*new_outer),
- old_outer,
- new_outer,
- edge_data->left_edges,
- &edge_data->left_data,
- timeout_func,
- TRUE,
- include_windows,
- keyboard_op);
- new_right = apply_edge_resistance (window,
- BOX_RIGHT (*old_outer),
- BOX_RIGHT (*new_outer),
- old_outer,
- new_outer,
- edge_data->right_edges,
- &edge_data->right_data,
- timeout_func,
- TRUE,
- include_windows,
- keyboard_op);
- }
- else
- {
- new_left = new_outer->x;
- new_right = new_outer->x + new_outer->width;
- }
- /* Same for vertical resizes... */
- if (!is_resize || window->size_hints.height_inc == 1)
- {
- new_top = apply_edge_resistance (window,
- BOX_TOP (*old_outer),
- BOX_TOP (*new_outer),
- old_outer,
- new_outer,
- edge_data->top_edges,
- &edge_data->top_data,
- timeout_func,
- FALSE,
- include_windows,
- keyboard_op);
- new_bottom = apply_edge_resistance (window,
- BOX_BOTTOM (*old_outer),
- BOX_BOTTOM (*new_outer),
- old_outer,
- new_outer,
- edge_data->bottom_edges,
- &edge_data->bottom_data,
- timeout_func,
- FALSE,
- include_windows,
- keyboard_op);
- }
- else
- {
- new_top = new_outer->y;
- new_bottom = new_outer->y + new_outer->height;
- }
- }
-
- /* 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)
-{
- guint i,j;
- MetaEdgeResistanceData *edge_data = display->grab_edge_resistance_data;
- GHashTable *edges_to_be_freed;
-
- if (edge_data == NULL) /* Not currently cached */
- return;
-
- /* We first need to clean out any window edges */
- edges_to_be_freed = g_hash_table_new_full (g_direct_hash, g_direct_equal,
- g_free, NULL);
- for (i = 0; i < 4; i++)
- {
- GArray *tmp = NULL;
- MetaSide side;
- switch (i)
- {
- case 0:
- tmp = edge_data->left_edges;
- side = META_SIDE_LEFT;
- break;
- case 1:
- tmp = edge_data->right_edges;
- side = META_SIDE_RIGHT;
- break;
- case 2:
- tmp = edge_data->top_edges;
- side = META_SIDE_TOP;
- break;
- case 3:
- tmp = edge_data->bottom_edges;
- side = META_SIDE_BOTTOM;
- 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 &&
- edge->side_type == side)
- {
- /* The same edge will appear in two arrays, and we can't free
- * it yet we still need to compare edge->side_type for the other
- * array that it is in. So store it in a hash table for later
- * freeing. Could also do this in a simple linked list.
- */
- g_hash_table_insert (edges_to_be_freed, edge, edge);
- }
- }
- }
-
- /* Now free all the window edges (the key destroy function is g_free) */
- g_hash_table_destroy (edges_to_be_freed);
-
- /* 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);
- edge_data->left_edges = NULL;
- edge_data->right_edges = NULL;
- edge_data->top_edges = NULL;
- edge_data->bottom_edges = NULL;
-
- /* Cleanup the timeouts */
- if (edge_data->left_data.timeout_setup)
- g_clear_handle_id (&edge_data->left_data.timeout_id, g_source_remove);
- if (edge_data->right_data.timeout_setup)
- g_clear_handle_id (&edge_data->right_data.timeout_id, g_source_remove);
- if (edge_data->top_data.timeout_setup)
- g_clear_handle_id (&edge_data->top_data.timeout_id, g_source_remove);
- if (edge_data->bottom_data.timeout_setup)
- g_clear_handle_id (&edge_data->bottom_data.timeout_id, g_source_remove);
-
- 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_ignore_type (*a_edge, *b_edge);
-}
-
-static void
-cache_edges (MetaDisplay *display,
- GList *window_edges,
- GList *monitor_edges,
- GList *screen_edges)
-{
- MetaEdgeResistanceData *edge_data;
- GList *tmp;
- int num_left, num_right, num_top, num_bottom;
- int i;
-
- /*
- * 0th: Print debugging information to the log about the edges
- */
-#ifdef WITH_VERBOSE_MODE
- if (meta_is_verbose())
- {
- int max_edges = MAX (MAX( g_list_length (window_edges),
- g_list_length (monitor_edges)),
- g_list_length (screen_edges));
- char big_buffer[(EDGE_LENGTH+2)*max_edges];
-
- meta_rectangle_edge_list_to_string (window_edges, ", ", big_buffer);
- meta_topic (META_DEBUG_EDGE_RESISTANCE,
- "Window edges for resistance : %s", big_buffer);
-
- meta_rectangle_edge_list_to_string (monitor_edges, ", ", big_buffer);
- meta_topic (META_DEBUG_EDGE_RESISTANCE,
- "Monitor edges for resistance: %s", big_buffer);
-
- meta_rectangle_edge_list_to_string (screen_edges, ", ", big_buffer);
- meta_topic (META_DEBUG_EDGE_RESISTANCE,
- "Screen edges for resistance : %s", big_buffer);
- }
-#endif
-
- /*
- * 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 = monitor_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_SIDE_LEFT:
- num_left++;
- break;
- case META_SIDE_RIGHT:
- num_right++;
- break;
- case META_SIDE_TOP:
- num_top++;
- break;
- case META_SIDE_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_new0 (MetaEdgeResistanceData, 1);
- edge_data = display->grab_edge_resistance_data;
- edge_data->left_edges = g_array_sized_new (FALSE,
- FALSE,
- sizeof(MetaEdge*),
- num_left + num_right);
- edge_data->right_edges = g_array_sized_new (FALSE,
- FALSE,
- sizeof(MetaEdge*),
- num_left + num_right);
- edge_data->top_edges = g_array_sized_new (FALSE,
- FALSE,
- sizeof(MetaEdge*),
- num_top + num_bottom);
- edge_data->bottom_edges = g_array_sized_new (FALSE,
- FALSE,
- sizeof(MetaEdge*),
- num_top + num_bottom);
-
- /*
- * 3rd: Add the edges to the arrays
- */
- for (i = 0; i < 3; i++)
- {
- tmp = NULL;
- switch (i)
- {
- case 0:
- tmp = window_edges;
- break;
- case 1:
- tmp = monitor_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_SIDE_LEFT:
- case META_SIDE_RIGHT:
- g_array_append_val (edge_data->left_edges, edge);
- g_array_append_val (edge_data->right_edges, edge);
- break;
- case META_SIDE_TOP:
- case META_SIDE_BOTTOM:
- g_array_append_val (edge_data->top_edges, edge);
- 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;
-}
-
-static void
-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;
- MetaWorkspaceManager *workspace_manager = display->workspace_manager;
-
- g_assert (display->grab_window != NULL);
- meta_topic (META_DEBUG_WINDOW_OPS,
- "Computing edges to resist-movement or snap-to for %s.",
- display->grab_window->desc);
-
- /*
- * 1st: Get the list of relevant windows, from bottom to top
- */
- stacked_windows =
- meta_stack_list_windows (display->stack,
- workspace_manager->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_frame_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 = obscuring_windows = g_slist_reverse (obscuring_windows);
- rem_win_stacking = window_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_frame_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 display_rect = { 0 };
- MetaRectangle reduced;
-
- meta_display_get_size (display,
- &display_rect.width, &display_rect.height);
-
- /* 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_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_SIDE_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_SIDE_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_SIDE_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_SIDE_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 >= GPOINTER_TO_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
- */
- g_list_free (stacked_windows);
- /* Free the memory used by the obscuring windows/docks lists */
- g_slist_free (window_stacking);
- g_slist_free_full (obscuring_windows, g_free);
-
- /* 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
- * monitor edges in an array for quick access. Free the edges since
- * they've been cached elsewhere.
- */
- cache_edges (display,
- edges,
- workspace_manager->active_workspace->monitor_edges,
- workspace_manager->active_workspace->screen_edges);
- g_list_free (edges);
-
- /*
- * 6th: Initialize the resistance timeouts and buildups
- */
- initialize_grab_edge_resistance_data (display);
-}
-
-void
-meta_window_edge_resistance_for_move (MetaWindow *window,
- int *new_x,
- int *new_y,
- GSourceFunc timeout_func,
- MetaEdgeResistanceFlags flags)
-{
- MetaRectangle old_outer, proposed_outer, new_outer;
- MetaEdgeResistanceFlags saved_flags;
- gboolean is_resize, is_keyboard_op, snap;
-
- meta_window_get_frame_rect (window, &old_outer);
-
- proposed_outer = old_outer;
- proposed_outer.x = *new_x;
- proposed_outer.y = *new_y;
- new_outer = proposed_outer;
-
- snap = flags & META_EDGE_RESISTANCE_SNAP;
- is_keyboard_op = flags & META_EDGE_RESISTANCE_KEYBOARD_OP;
- saved_flags = flags & ~META_EDGE_RESISTANCE_KEYBOARD_OP;
-
- window->display->grab_last_edge_resistance_flags = saved_flags;
- is_resize = FALSE;
- if (apply_edge_resistance_to_each_side (window->display,
- window,
- &old_outer,
- &new_outer,
- timeout_func,
- flags,
- is_resize))
- {
- /* 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_outer.x + smaller_x_change +
- (BOX_LEFT (*reference) - BOX_LEFT (old_outer));
- *new_y = old_outer.y + smaller_y_change +
- (BOX_TOP (*reference) - BOX_TOP (old_outer));
-
- meta_topic (META_DEBUG_EDGE_RESISTANCE,
- "outer x & y move-to coordinate changed from %d,%d to %d,%d",
- proposed_outer.x, proposed_outer.y,
- *new_x, *new_y);
- }
-}
-
-void
-meta_window_edge_resistance_for_resize (MetaWindow *window,
- int *new_width,
- int *new_height,
- MetaGravity gravity,
- GSourceFunc timeout_func,
- MetaEdgeResistanceFlags flags)
-{
- MetaRectangle old_outer, new_outer;
- MetaEdgeResistanceFlags saved_flags;
- int proposed_outer_width, proposed_outer_height;
-
- meta_window_get_frame_rect (window, &old_outer);
- proposed_outer_width = *new_width;
- proposed_outer_height = *new_height;
- meta_rectangle_resize_with_gravity (&old_outer,
- &new_outer,
- gravity,
- proposed_outer_width,
- proposed_outer_height);
-
- saved_flags = flags & ~META_EDGE_RESISTANCE_KEYBOARD_OP;
- window->display->grab_last_edge_resistance_flags = saved_flags;
-
- if (apply_edge_resistance_to_each_side (window->display,
- window,
- &old_outer,
- &new_outer,
- timeout_func,
- flags,
- TRUE))
- {
- *new_width = new_outer.width;
- *new_height = new_outer.height;
-
- meta_topic (META_DEBUG_EDGE_RESISTANCE,
- "outer width & height got changed from %d,%d to %d,%d",
- proposed_outer_width, proposed_outer_height,
- new_outer.width, new_outer.height);
- }
-}