diff options
Diffstat (limited to 'src/backends/native/meta-pointer-constraint-native.c')
-rw-r--r-- | src/backends/native/meta-pointer-constraint-native.c | 694 |
1 files changed, 0 insertions, 694 deletions
diff --git a/src/backends/native/meta-pointer-constraint-native.c b/src/backends/native/meta-pointer-constraint-native.c deleted file mode 100644 index 6a1c066ad..000000000 --- a/src/backends/native/meta-pointer-constraint-native.c +++ /dev/null @@ -1,694 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* - * Copyright (C) 2015-2020 Red Hat - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - * - * Written by: - * Jonas Ã…dahl <jadahl@gmail.com> - */ - -#include "config.h" - -#include <glib-object.h> -#include <wayland-server.h> - -#include "core/meta-border.h" -#include "meta-pointer-constraint-native.h" - -struct _MetaPointerConstraintImplNative -{ - MetaPointerConstraintImpl parent; - MetaPointerConstraint *constraint; - cairo_region_t *region; -}; - -G_DEFINE_TYPE (MetaPointerConstraintImplNative, - meta_pointer_constraint_impl_native, - META_TYPE_POINTER_CONSTRAINT_IMPL); - -typedef struct _MetaBox -{ - int x1; - int y1; - int x2; - int y2; -} MetaBox; - -static MetaBorder * -add_border (GArray *borders, - float x1, - float y1, - float x2, - float y2, - MetaBorderMotionDirection blocking_directions) -{ - MetaBorder border; - - border = (MetaBorder) { - .line = (MetaLine2) { - .a = (MetaVector2) { - .x = x1, - .y = y1, - }, - .b = (MetaVector2) { - .x = x2, - .y = y2, - }, - }, - .blocking_directions = blocking_directions, - }; - - g_array_append_val (borders, border); - - return &g_array_index (borders, MetaBorder, borders->len - 1); -} - -static gint -compare_lines_x (gconstpointer a, - gconstpointer b) -{ - const MetaBorder *border_a = a; - const MetaBorder *border_b = b; - - if (border_a->line.a.x == border_b->line.a.x) - return border_a->line.b.x < border_b->line.b.x; - else - return border_a->line.a.x > border_b->line.a.x; -} - -static void -add_non_overlapping_edges (MetaBox *boxes, - unsigned int band_above_start, - unsigned int band_below_start, - unsigned int band_below_end, - GArray *borders) -{ - unsigned int i; - GArray *band_merge; - MetaBorder *border; - MetaBorder *prev_border; - MetaBorder *new_border; - - band_merge = g_array_new (FALSE, FALSE, sizeof *border); - - /* Add bottom band of previous row, and top band of current row, and - * sort them so lower left x coordinate comes first. If there are two - * borders with the same left x coordinate, the wider one comes first. - */ - for (i = band_above_start; i < band_below_start; i++) - { - MetaBox *box = &boxes[i]; - add_border (band_merge, box->x1, box->y2, box->x2, box->y2, - META_BORDER_MOTION_DIRECTION_POSITIVE_Y); - } - for (i = band_below_start; i < band_below_end; i++) - { - MetaBox *box= &boxes[i]; - add_border (band_merge, box->x1, box->y1, box->x2, box->y1, - META_BORDER_MOTION_DIRECTION_NEGATIVE_Y); - } - g_array_sort (band_merge, compare_lines_x); - - /* Combine the two combined bands so that any overlapping border is - * eliminated. */ - prev_border = NULL; - for (i = 0; i < band_merge->len; i++) - { - border = &g_array_index (band_merge, MetaBorder, i); - - g_assert (border->line.a.y == border->line.b.y); - g_assert (!prev_border || - prev_border->line.a.y == border->line.a.y); - g_assert (!prev_border || - (prev_border->line.a.x != border->line.a.x || - prev_border->line.b.x != border->line.b.x)); - g_assert (!prev_border || - prev_border->line.a.x <= border->line.a.x); - - if (prev_border && - prev_border->line.a.x == border->line.a.x) - { - /* - * ------------ + - * ------- = - * [ ]----- - */ - prev_border->line.a.x = border->line.b.x; - } - else if (prev_border && - prev_border->line.b.x == border->line.b.x) - { - /* - * ------------ + - * ------ = - * ------[ ] - */ - prev_border->line.b.x = border->line.a.x; - } - else if (prev_border && - prev_border->line.b.x == border->line.a.x) - { - /* - * -------- + - * ------ = - * -------------- - */ - prev_border->line.b.x = border->line.b.x; - } - else if (prev_border && - prev_border->line.b.x >= border->line.a.x) - { - /* - * --------------- + - * ------ = - * -----[ ]---- - */ - new_border = add_border (borders, - border->line.b.x, - border->line.b.y, - prev_border->line.b.x, - prev_border->line.b.y, - prev_border->blocking_directions); - prev_border->line.b.x = border->line.a.x; - prev_border = new_border; - } - else - { - g_assert (!prev_border || - prev_border->line.b.x < border->line.a.x); - /* - * First border or non-overlapping. - * - * ----- + - * ----- = - * ----- ----- - */ - g_array_append_val (borders, *border); - prev_border = &g_array_index (borders, MetaBorder, borders->len - 1); - } - } - - g_array_free (band_merge, FALSE); -} - -static void -add_band_bottom_edges (MetaBox *boxes, - int band_start, - int band_end, - GArray *borders) -{ - int i; - - for (i = band_start; i < band_end; i++) - { - add_border (borders, - boxes[i].x1, boxes[i].y2, - boxes[i].x2, boxes[i].y2, - META_BORDER_MOTION_DIRECTION_POSITIVE_Y); - } -} - -static void -region_to_outline (cairo_region_t *region, - GArray *borders) -{ - MetaBox *boxes; - int num_boxes; - int i; - int top_most, bottom_most; - int current_roof; - int prev_top; - int band_start, prev_band_start; - - /* - * Remove any overlapping lines from the set of rectangles. Note that - * pixman regions are grouped as rows of rectangles, where rectangles - * in one row never touch or overlap and are all of the same height. - * - * -------- --- -------- --- - * | | | | | | | | - * ----------====---- --- ----------- ----- --- - * | | => | | - * ----==========--------- ----- ---------- - * | | | | - * ------------------- ------------------- - * - */ - - num_boxes = cairo_region_num_rectangles (region); - boxes = g_new (MetaBox, num_boxes); - for (i = 0; i < num_boxes; i++) - { - cairo_rectangle_int_t rect; - cairo_region_get_rectangle (region, i, &rect); - boxes[i] = (MetaBox) { - .x1 = rect.x, - .y1 = rect.y, - .x2 = rect.x + rect.width, - .y2 = rect.y + rect.height, - }; - } - prev_top = 0; - top_most = boxes[0].y1; - current_roof = top_most; - bottom_most = boxes[num_boxes - 1].y2; - band_start = 0; - prev_band_start = 0; - for (i = 0; i < num_boxes; i++) - { - /* Detect if there is a vertical empty space, and add the lower - * level of the previous band if so was the case. */ - if (i > 0 && - boxes[i].y1 != prev_top && - boxes[i].y1 != boxes[i - 1].y2) - { - current_roof = boxes[i].y1; - add_band_bottom_edges (boxes, - band_start, - i, - borders); - } - - /* Special case adding the last band, since it won't be handled - * by the band change detection below. */ - if (boxes[i].y1 != current_roof && i == num_boxes - 1) - { - if (boxes[i].y1 != prev_top) - { - /* The last band is a single box, so we don't - * have a prev_band_start to tell us when the - * previous band started. */ - add_non_overlapping_edges (boxes, - band_start, - i, - i + 1, - borders); - } - else - { - add_non_overlapping_edges (boxes, - prev_band_start, - band_start, - i + 1, - borders); - } - } - - /* Detect when passing a band and combine the top border of the - * just passed band with the bottom band of the previous band. - */ - if (boxes[i].y1 != top_most && boxes[i].y1 != prev_top) - { - /* Combine the two passed bands. */ - if (prev_top != current_roof) - { - add_non_overlapping_edges (boxes, - prev_band_start, - band_start, - i, - borders); - } - - prev_band_start = band_start; - band_start = i; - } - - /* Add the top border if the box is part of the current roof. */ - if (boxes[i].y1 == current_roof) - { - add_border (borders, - boxes[i].x1, boxes[i].y1, - boxes[i].x2, boxes[i].y1, - META_BORDER_MOTION_DIRECTION_NEGATIVE_Y); - } - - /* Add the bottom border of the last band. */ - if (boxes[i].y2 == bottom_most) - { - add_border (borders, - boxes[i].x1, boxes[i].y2, - boxes[i].x2, boxes[i].y2, - META_BORDER_MOTION_DIRECTION_POSITIVE_Y); - } - - /* Always add the left border. */ - add_border (borders, - boxes[i].x1, boxes[i].y1, - boxes[i].x1, boxes[i].y2, - META_BORDER_MOTION_DIRECTION_NEGATIVE_X); - - /* Always add the right border. */ - add_border (borders, - boxes[i].x2, boxes[i].y1, - boxes[i].x2, boxes[i].y2, - META_BORDER_MOTION_DIRECTION_POSITIVE_X); - - prev_top = boxes[i].y1; - } - - g_free (boxes); -} - -static MetaBorder * -get_closest_border (GArray *borders, - MetaLine2 *motion, - uint32_t directions) -{ - MetaBorder *border; - MetaVector2 intersection; - MetaVector2 delta; - float distance_2; - MetaBorder *closest_border = NULL; - float closest_distance_2 = DBL_MAX; - unsigned int i; - - for (i = 0; i < borders->len; i++) - { - border = &g_array_index (borders, MetaBorder, i); - - if (!meta_border_is_blocking_directions (border, directions)) - continue; - - if (!meta_line2_intersects_with (&border->line, motion, &intersection)) - continue; - - delta = meta_vector2_subtract (intersection, motion->a); - distance_2 = delta.x*delta.x + delta.y*delta.y; - if (distance_2 < closest_distance_2) - { - closest_border = border; - closest_distance_2 = distance_2; - } - } - - return closest_border; -} - -static void -clamp_to_border (MetaBorder *border, - MetaLine2 *motion, - uint32_t *motion_dir) -{ - /* - * When clamping either rightward or downward motions, the motion needs to be - * clamped so that the destination coordinate does not end up on the border - * (see weston_pointer_clamp_event_to_region). Do this by clamping such - * motions to the border minus the smallest possible wl_fixed_t value. - * - * When clamping in either leftward or upward motion, the resulting coordinate - * needs to be clamped so that it is enough on the inside to avoid the - * inaccuracies of clutter's stage to actor transformation algorithm (the one - * used in clutter_actor_transform_stage_point) to make it end up outside the - * next motion. It also needs to be clamped so that to the wl_fixed_t - * coordinate may still be right on the border (i.e. at .0). Testing shows - * that the smallest wl_fixed_t value divided by 10 is small enough to make - * the wl_fixed_t coordinate .0 and large enough to avoid the inaccuracies of - * clutters transform algorithm. - */ - if (meta_border_is_horizontal (border)) - { - if (*motion_dir & META_BORDER_MOTION_DIRECTION_POSITIVE_Y) - motion->b.y = border->line.a.y - wl_fixed_to_double (1); - else - motion->b.y = border->line.a.y + wl_fixed_to_double (1) / 10; - *motion_dir &= ~(META_BORDER_MOTION_DIRECTION_POSITIVE_Y | - META_BORDER_MOTION_DIRECTION_NEGATIVE_Y); - } - else - { - if (*motion_dir & META_BORDER_MOTION_DIRECTION_POSITIVE_X) - motion->b.x = border->line.a.x - wl_fixed_to_double (1); - else - motion->b.x = border->line.a.x + wl_fixed_to_double (1) / 10; - *motion_dir &= ~(META_BORDER_MOTION_DIRECTION_POSITIVE_X | - META_BORDER_MOTION_DIRECTION_NEGATIVE_X); - } -} - -static uint32_t -get_motion_directions (MetaLine2 *motion) -{ - uint32_t directions = 0; - - if (motion->a.x < motion->b.x) - directions |= META_BORDER_MOTION_DIRECTION_POSITIVE_X; - else if (motion->a.x > motion->b.x) - directions |= META_BORDER_MOTION_DIRECTION_NEGATIVE_X; - if (motion->a.y < motion->b.y) - directions |= META_BORDER_MOTION_DIRECTION_POSITIVE_Y; - else if (motion->a.y > motion->b.y) - directions |= META_BORDER_MOTION_DIRECTION_NEGATIVE_Y; - - return directions; -} - -static void -meta_pointer_constraint_impl_native_constraint (MetaPointerConstraintImpl *constraint_impl, - ClutterInputDevice *device, - uint32_t time, - float prev_x, - float prev_y, - float *x_inout, - float *y_inout) -{ - MetaPointerConstraintImplNative *constraint_impl_native; - cairo_region_t *region; - float x, y; - GArray *borders; - MetaLine2 motion; - MetaBorder *closest_border; - uint32_t directions; - - constraint_impl_native = META_POINTER_CONSTRAINT_IMPL_NATIVE (constraint_impl); - - region = cairo_region_reference (constraint_impl_native->region); - x = *x_inout; - y = *y_inout; - - /* For motions in a positive direction on any axis, append the smallest - * possible value representable in a Wayland absolute coordinate. This is - * in order to avoid not clamping motion that as a floating point number - * won't be clamped, but will be rounded up to be outside of the range - * of wl_fixed_t. */ - if (x > prev_x) - x += (float) wl_fixed_to_double(1); - if (y > prev_y) - y += (float) wl_fixed_to_double(1); - - borders = g_array_new (FALSE, FALSE, sizeof (MetaBorder)); - - /* - * Generate borders given the confine region we are to use. The borders - * are defined to be the outer region of the allowed area. This means - * top/left borders are "within" the allowed area, while bottom/right - * borders are outside. This needs to be considered when clamping - * confined motion vectors. - */ - region_to_outline (region, borders); - cairo_region_destroy (region); - - motion = (MetaLine2) { - .a = (MetaVector2) { - .x = prev_x, - .y = prev_y, - }, - .b = (MetaVector2) { - .x = x, - .y = y, - }, - }; - directions = get_motion_directions (&motion); - - while (directions) - { - closest_border = get_closest_border (borders, - &motion, - directions); - if (closest_border) - clamp_to_border (closest_border, &motion, &directions); - else - break; - } - - *x_inout = motion.b.x; - *y_inout = motion.b.y; - g_array_free (borders, FALSE); -} - -static float -point_to_border_distance_2 (MetaBorder *border, - float x, - float y) -{ - float orig_x, orig_y; - float dx, dy; - - if (meta_border_is_horizontal (border)) - { - if (x < border->line.a.x) - orig_x = border->line.a.x; - else if (x > border->line.b.x) - orig_x = border->line.b.x; - else - orig_x = x; - orig_y = border->line.a.y; - } - else - { - if (y < border->line.a.y) - orig_y = border->line.a.y; - else if (y > border->line.b.y) - orig_y = border->line.b.y; - else - orig_y = y; - orig_x = border->line.a.x; - } - - dx = fabsf (orig_x - x); - dy = fabsf (orig_y - y); - return dx*dx + dy*dy; -} - -static void -closest_point_behind_border (MetaBorder *border, - float *sx, - float *sy) -{ - switch (border->blocking_directions) - { - case META_BORDER_MOTION_DIRECTION_POSITIVE_X: - case META_BORDER_MOTION_DIRECTION_NEGATIVE_X: - if (border->blocking_directions == META_BORDER_MOTION_DIRECTION_POSITIVE_X) - *sx = border->line.a.x - wl_fixed_to_double (1); - else - *sx = border->line.a.x + wl_fixed_to_double (1); - if (*sy < border->line.a.y) - *sy = border->line.a.y + wl_fixed_to_double (1); - else if (*sy > border->line.b.y) - *sy = border->line.b.y - wl_fixed_to_double (1); - break; - case META_BORDER_MOTION_DIRECTION_POSITIVE_Y: - case META_BORDER_MOTION_DIRECTION_NEGATIVE_Y: - if (border->blocking_directions == META_BORDER_MOTION_DIRECTION_POSITIVE_Y) - *sy = border->line.a.y - wl_fixed_to_double (1); - else - *sy = border->line.a.y + wl_fixed_to_double (1); - if (*sx < border->line.a.x) - *sx = border->line.a.x + wl_fixed_to_double (1); - else if (*sx > (border->line.b.x)) - *sx = border->line.b.x - wl_fixed_to_double (1); - break; - } -} - -static void -meta_pointer_constraint_impl_native_ensure_constrained (MetaPointerConstraintImpl *constraint_impl, - ClutterInputDevice *device) -{ - MetaPointerConstraintImplNative *constraint_impl_native; - graphene_point_t point; - cairo_region_t *region; - float x; - float y; - - constraint_impl_native = META_POINTER_CONSTRAINT_IMPL_NATIVE (constraint_impl); - region = cairo_region_reference (constraint_impl_native->region); - - clutter_seat_query_state (clutter_input_device_get_seat (device), - device, NULL, &point, NULL); - x = point.x; - y = point.y; - - if (!cairo_region_contains_point (region, (int) x, (int) y)) - { - GArray *borders; - float closest_distance_2 = FLT_MAX; - MetaBorder *closest_border = NULL; - ClutterSeat *seat; - unsigned int i; - - borders = g_array_new (FALSE, FALSE, sizeof (MetaBorder)); - - region_to_outline (region, borders); - - for (i = 0; i < borders->len; i++) - { - MetaBorder *border = &g_array_index (borders, MetaBorder, i); - float distance_2; - - distance_2 = point_to_border_distance_2 (border, x, y); - if (distance_2 < closest_distance_2) - { - closest_border = border; - closest_distance_2 = distance_2; - } - } - - closest_point_behind_border (closest_border, &x, &y); - - seat = clutter_backend_get_default_seat (clutter_get_default_backend ()); - clutter_seat_warp_pointer (seat, x, y); - } - - cairo_region_destroy (region); -} - -static void -meta_pointer_constraint_impl_native_finalize (GObject *object) -{ - MetaPointerConstraintImplNative *constraint_impl_native; - - constraint_impl_native = META_POINTER_CONSTRAINT_IMPL_NATIVE (object); - g_clear_pointer (&constraint_impl_native->region, cairo_region_destroy); - - G_OBJECT_CLASS (meta_pointer_constraint_impl_native_parent_class)->finalize (object); -} - -static void -meta_pointer_constraint_impl_native_init (MetaPointerConstraintImplNative *constraint_impl_native) -{ -} - -static void -meta_pointer_constraint_impl_native_class_init (MetaPointerConstraintImplNativeClass *klass) -{ - MetaPointerConstraintImplClass *constraint_impl_class; - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = meta_pointer_constraint_impl_native_finalize; - - constraint_impl_class = META_POINTER_CONSTRAINT_IMPL_CLASS (klass); - constraint_impl_class->constrain = meta_pointer_constraint_impl_native_constraint; - constraint_impl_class->ensure_constrained = - meta_pointer_constraint_impl_native_ensure_constrained; -} - - -MetaPointerConstraintImpl * -meta_pointer_constraint_impl_native_new (MetaPointerConstraint *constraint, - const cairo_region_t *region) -{ - MetaPointerConstraintImplNative *constraint_impl; - - constraint_impl = g_object_new (META_TYPE_POINTER_CONSTRAINT_IMPL_NATIVE, - NULL); - constraint_impl->constraint = constraint; - constraint_impl->region = cairo_region_copy (region); - - return META_POINTER_CONSTRAINT_IMPL (constraint_impl); -} |