2020-07-08 12:17:13 -04:00
|
|
|
/* -*- 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);
|
|
|
|
|
2020-11-18 09:49:02 -05:00
|
|
|
clutter_seat_query_state (clutter_input_device_get_seat (device),
|
|
|
|
device, NULL, &point, NULL);
|
2020-07-08 12:17:13 -04:00
|
|
|
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);
|
|
|
|
}
|