From f0f638d2bdd40b20b6928d31eb6ce45e33dab106 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Fri, 3 Jul 2015 15:01:58 +0800 Subject: [PATCH] Move out generic math parts out of the native barrier implementation In order to reuse some vector math for pointer confinement, move out those parts to its own file, introducing the types old types "MetaVector2" and "MetaLine2" outside of meta-barrier-native.c, as well as introducing MetaBorder which is a line, with a blocking direction. https://bugzilla.gnome.org/show_bug.cgi?id=744104 --- src/Makefile.am | 2 + src/backends/meta-barrier-private.h | 11 +- src/backends/meta-barrier.c | 25 +-- src/backends/native/meta-barrier-native.c | 181 ++++------------------ src/backends/x11/meta-barrier-x11.c | 13 +- src/core/meta-border.c | 154 ++++++++++++++++++ src/core/meta-border.h | 84 ++++++++++ 7 files changed, 292 insertions(+), 178 deletions(-) create mode 100644 src/core/meta-border.c create mode 100644 src/core/meta-border.h diff --git a/src/Makefile.am b/src/Makefile.am index 71aa02674..fb0ccbb5d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -112,6 +112,8 @@ libmutter_la_SOURCES = \ core/boxes.c \ core/boxes-private.h \ meta/boxes.h \ + core/meta-border.c \ + core/meta-border.h \ compositor/clutter-utils.c \ compositor/clutter-utils.h \ compositor/cogl-utils.c \ diff --git a/src/backends/meta-barrier-private.h b/src/backends/meta-barrier-private.h index 8e1d223de..8c711172c 100644 --- a/src/backends/meta-barrier-private.h +++ b/src/backends/meta-barrier-private.h @@ -26,6 +26,8 @@ #ifndef META_BARRIER_PRIVATE_H #define META_BARRIER_PRIVATE_H +#include "core/meta-border.h" + G_BEGIN_DECLS #define META_TYPE_BARRIER_IMPL (meta_barrier_impl_get_type ()) @@ -67,14 +69,7 @@ G_END_DECLS struct _MetaBarrierPrivate { MetaDisplay *display; - - int x1; - int y1; - int x2; - int y2; - - MetaBarrierDirection directions; - + MetaBorder border; MetaBarrierImpl *impl; }; diff --git a/src/backends/meta-barrier.c b/src/backends/meta-barrier.c index f5422ee1b..a1e1180fe 100644 --- a/src/backends/meta-barrier.c +++ b/src/backends/meta-barrier.c @@ -61,19 +61,20 @@ meta_barrier_get_property (GObject *object, g_value_set_object (value, priv->display); break; case PROP_X1: - g_value_set_int (value, priv->x1); + g_value_set_int (value, priv->border.line.a.x); break; case PROP_Y1: - g_value_set_int (value, priv->y1); + g_value_set_int (value, priv->border.line.a.y); break; case PROP_X2: - g_value_set_int (value, priv->x2); + g_value_set_int (value, priv->border.line.b.x); break; case PROP_Y2: - g_value_set_int (value, priv->y2); + g_value_set_int (value, priv->border.line.b.y); break; case PROP_DIRECTIONS: - g_value_set_flags (value, priv->directions); + g_value_set_flags (value, + meta_border_get_allows_directions (&priv->border)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -95,19 +96,20 @@ meta_barrier_set_property (GObject *object, priv->display = g_value_get_object (value); break; case PROP_X1: - priv->x1 = g_value_get_int (value); + priv->border.line.a.x = g_value_get_int (value); break; case PROP_Y1: - priv->y1 = g_value_get_int (value); + priv->border.line.a.y = g_value_get_int (value); break; case PROP_X2: - priv->x2 = g_value_get_int (value); + priv->border.line.b.x = g_value_get_int (value); break; case PROP_Y2: - priv->y2 = g_value_get_int (value); + priv->border.line.b.y = g_value_get_int (value); break; case PROP_DIRECTIONS: - priv->directions = g_value_get_flags (value); + meta_border_set_allows_directions (&priv->border, + g_value_get_flags (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -166,7 +168,8 @@ meta_barrier_constructed (GObject *object) MetaBarrier *barrier = META_BARRIER (object); MetaBarrierPrivate *priv = barrier->priv; - g_return_if_fail (priv->x1 == priv->x2 || priv->y1 == priv->y2); + g_return_if_fail (priv->border.line.a.x == priv->border.line.b.x || + priv->border.line.a.y == priv->border.line.b.y); #if defined(HAVE_NATIVE_BACKEND) if (META_IS_BACKEND_NATIVE (meta_get_backend ())) diff --git a/src/backends/native/meta-barrier-native.c b/src/backends/native/meta-barrier-native.c index 8f81d3440..c6a6be6c1 100644 --- a/src/backends/native/meta-barrier-native.c +++ b/src/backends/native/meta-barrier-native.c @@ -93,126 +93,18 @@ next_serial (void) return barrier_serial; } -typedef struct _Vector2 -{ - float x, y; -} Vector2; - -static float -vector2_cross_product (Vector2 a, Vector2 b) -{ - return a.x * b.y - a.y * b.x; -} - -static Vector2 -vector2_add (Vector2 a, Vector2 b) -{ - return (Vector2) { - .x = a.x + b.x, - .y = a.y + b.y, - }; -} - -static Vector2 -vector2_subtract (Vector2 a, Vector2 b) -{ - return (Vector2) { - .x = a.x - b.x, - .y = a.y - b.y, - }; -} - -static Vector2 -vector2_multiply_constant (float c, Vector2 a) -{ - return (Vector2) { - .x = c * a.x, - .y = c * a.y, - }; -} - -typedef struct _Line2 -{ - Vector2 a; - Vector2 b; -} Line2; - -static gboolean -lines_intersect (Line2 *line1, Line2 *line2, Vector2 *intersection) -{ - Vector2 p = line1->a; - Vector2 r = vector2_subtract (line1->b, line1->a); - Vector2 q = line2->a; - Vector2 s = vector2_subtract (line2->b, line2->a); - float rxs; - float sxr; - float t; - float u; - - /* - * The line (p, r) and (q, s) intersects where - * - * p + t r = q + u s - * - * Calculate t: - * - * (p + t r) × s = (q + u s) × s - * p × s + t (r × s) = q × s + u (s × s) - * p × s + t (r × s) = q × s - * t (r × s) = q × s - p × s - * t (r × s) = (q - p) × s - * t = ((q - p) × s) / (r × s) - * - * Using the same method, for u we get: - * - * u = ((p - q) × r) / (s × r) - */ - - rxs = vector2_cross_product (r, s); - sxr = vector2_cross_product (s, r); - - /* If r × s = 0 then the lines are either parallel or collinear. */ - if (fabs ( rxs) < DBL_MIN) - return FALSE; - - t = vector2_cross_product (vector2_subtract (q, p), s) / rxs; - u = vector2_cross_product (vector2_subtract (p, q), r) / sxr; - - - /* The lines only intersect if 0 ≤ t ≤ 1 and 0 ≤ u ≤ 1. */ - if (t < 0.0 || t > 1.0 || u < 0.0 || u > 1.0) - return FALSE; - - *intersection = vector2_add (p, vector2_multiply_constant (t, r)); - - return TRUE; -} - static gboolean is_barrier_horizontal (MetaBarrier *barrier) { - return barrier->priv->y1 == barrier->priv->y2; + return meta_border_is_horizontal (&barrier->priv->border); } static gboolean is_barrier_blocking_directions (MetaBarrier *barrier, MetaBarrierDirection directions) { - /* Barriers doesn't block parallel motions. */ - if (is_barrier_horizontal (barrier)) - { - if ((directions & (META_BARRIER_DIRECTION_POSITIVE_Y | - META_BARRIER_DIRECTION_NEGATIVE_Y)) == 0) - return FALSE; - } - else - { - if ((directions & (META_BARRIER_DIRECTION_POSITIVE_X | - META_BARRIER_DIRECTION_NEGATIVE_X)) == 0) - return FALSE; - } - - return (barrier->priv->directions & directions) != directions; + return meta_border_is_blocking_directions (&barrier->priv->border, + directions); } static void @@ -224,31 +116,16 @@ dismiss_pointer (MetaBarrierImplNative *self) priv->state = META_BARRIER_STATE_LEFT; } -static Line2 -barrier_to_line (MetaBarrier *barrier) -{ - return (Line2) { - .a = (Vector2) { - .x = MIN (barrier->priv->x1, barrier->priv->x2), - .y = MIN (barrier->priv->y1, barrier->priv->y2), - }, - .b = (Vector2) { - .x = MAX (barrier->priv->x1, barrier->priv->x2), - .y = MAX (barrier->priv->y1, barrier->priv->y2), - }, - }; -} - /* * Calculate the hit box for a held motion. The hit box is a 2 px wide region * in the opposite direction of every direction the barrier blocks. The purpose * of this is to allow small movements without receiving a "left" signal. This * heuristic comes from the X.org pointer barrier implementation. */ -static Line2 +static MetaLine2 calculate_barrier_hit_box (MetaBarrier *barrier) { - Line2 hit_box = barrier_to_line (barrier); + MetaLine2 hit_box = barrier->priv->border.line; if (is_barrier_horizontal (barrier)) { @@ -273,7 +150,8 @@ calculate_barrier_hit_box (MetaBarrier *barrier) } static gboolean -is_within_box (Line2 box, Vector2 point) +is_within_box (MetaLine2 box, + MetaVector2 point) { return (point.x >= box.a.x && point.x < box.b.x && point.y >= box.a.y && point.y < box.b.y); @@ -288,8 +166,8 @@ maybe_release_barrier (gpointer key, MetaBarrierImplNativePrivate *priv = meta_barrier_impl_native_get_instance_private (self); MetaBarrier *barrier = priv->barrier; - Line2 *motion = user_data; - Line2 hit_box; + MetaLine2 *motion = user_data; + MetaLine2 hit_box; if (priv->state != META_BARRIER_STATE_HELD) return; @@ -297,8 +175,10 @@ maybe_release_barrier (gpointer key, /* Release if we end up outside barrier end points. */ if (is_barrier_horizontal (barrier)) { - if (motion->b.x > MAX (barrier->priv->x1, barrier->priv->x2) || - motion->b.x < MIN (barrier->priv->x1, barrier->priv->x2)) + if (motion->b.x > MAX (barrier->priv->border.line.a.x, + barrier->priv->border.line.b.x) || + motion->b.x < MIN (barrier->priv->border.line.a.x, + barrier->priv->border.line.b.x)) { dismiss_pointer (self); return; @@ -306,8 +186,10 @@ maybe_release_barrier (gpointer key, } else { - if (motion->b.y > MAX (barrier->priv->y1, barrier->priv->y2) || - motion->b.y < MIN (barrier->priv->y1, barrier->priv->y2)) + if (motion->b.y > MAX (barrier->priv->border.line.a.y, + barrier->priv->border.line.b.y) || + motion->b.y < MIN (barrier->priv->border.line.a.y, + barrier->priv->border.line.b.y)) { dismiss_pointer (self); return; @@ -330,7 +212,7 @@ maybe_release_barriers (MetaBarrierManagerNative *manager, float x, float y) { - Line2 motion = { + MetaLine2 motion = { .a = { .x = prev_x, .y = prev_y, @@ -350,7 +232,7 @@ typedef struct _MetaClosestBarrierData { struct { - Line2 motion; + MetaLine2 motion; MetaBarrierDirection directions; } in; @@ -371,8 +253,7 @@ update_closest_barrier (gpointer key, meta_barrier_impl_native_get_instance_private (self); MetaBarrier *barrier = priv->barrier; MetaClosestBarrierData *data = user_data; - Line2 barrier_line; - Vector2 intersection; + MetaVector2 intersection; float dx, dy; float distance_2; @@ -391,17 +272,9 @@ update_closest_barrier (gpointer key, /* Check if the motion intersects with the barrier, and retrieve the * intersection point if any. */ - barrier_line = (Line2) { - .a = { - .x = barrier->priv->x1, - .y = barrier->priv->y1 - }, - .b = { - .x = barrier->priv->x2, - .y = barrier->priv->y2 - }, - }; - if (!lines_intersect (&barrier_line, &data->in.motion, &intersection)) + if (!meta_line2_intersects_with (&barrier->priv->border.line, + &data->in.motion, + &intersection)) return; /* Calculate the distance to the barrier and keep track of the closest @@ -570,9 +443,9 @@ clamp_to_barrier (MetaBarrierImplNative *self, if (is_barrier_horizontal (barrier)) { if (*motion_dir & META_BARRIER_DIRECTION_POSITIVE_Y) - *y = barrier->priv->y1; + *y = barrier->priv->border.line.a.y; else if (*motion_dir & META_BARRIER_DIRECTION_NEGATIVE_Y) - *y = barrier->priv->y1; + *y = barrier->priv->border.line.a.y; priv->blocked_dir = *motion_dir & (META_BARRIER_DIRECTION_POSITIVE_Y | META_BARRIER_DIRECTION_NEGATIVE_Y); @@ -582,9 +455,9 @@ clamp_to_barrier (MetaBarrierImplNative *self, else { if (*motion_dir & META_BARRIER_DIRECTION_POSITIVE_X) - *x = barrier->priv->x1; + *x = barrier->priv->border.line.a.x; else if (*motion_dir & META_BARRIER_DIRECTION_NEGATIVE_X) - *x = barrier->priv->x1; + *x = barrier->priv->border.line.a.x; priv->blocked_dir = *motion_dir & (META_BARRIER_DIRECTION_POSITIVE_X | META_BARRIER_DIRECTION_NEGATIVE_X); diff --git a/src/backends/x11/meta-barrier-x11.c b/src/backends/x11/meta-barrier-x11.c index 1a813c43d..054e5cdc6 100644 --- a/src/backends/x11/meta-barrier-x11.c +++ b/src/backends/x11/meta-barrier-x11.c @@ -107,6 +107,7 @@ meta_barrier_impl_x11_new (MetaBarrier *barrier) MetaDisplay *display = barrier->priv->display; Display *dpy; Window root; + unsigned int allowed_motion_dirs; if (display == NULL) { @@ -121,12 +122,14 @@ meta_barrier_impl_x11_new (MetaBarrier *barrier) dpy = display->xdisplay; root = DefaultRootWindow (dpy); + allowed_motion_dirs = + meta_border_get_allows_directions (&barrier->priv->border); priv->xbarrier = XFixesCreatePointerBarrier (dpy, root, - barrier->priv->x1, - barrier->priv->y1, - barrier->priv->x2, - barrier->priv->y2, - barrier->priv->directions, + barrier->priv->border.line.a.x, + barrier->priv->border.line.a.y, + barrier->priv->border.line.b.x, + barrier->priv->border.line.b.y, + allowed_motion_dirs, 0, NULL); g_hash_table_insert (display->xids, &priv->xbarrier, barrier); diff --git a/src/core/meta-border.c b/src/core/meta-border.c new file mode 100644 index 000000000..cc996fef3 --- /dev/null +++ b/src/core/meta-border.c @@ -0,0 +1,154 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +/* + * Copyright (C) 2015 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 + */ + +#include "config.h" + +#include "core/meta-border.h" + +#include + +static inline float +meta_vector2_cross_product (const MetaVector2 a, + const MetaVector2 b) +{ + return a.x * b.y - a.y * b.x; +} + +static inline MetaVector2 +meta_vector2_add (const MetaVector2 a, + const MetaVector2 b) +{ + return (MetaVector2) { + .x = a.x + b.x, + .y = a.y + b.y, + }; +} + +static inline MetaVector2 +meta_vector2_multiply_constant (const float c, + const MetaVector2 a) +{ + return (MetaVector2) { + .x = c * a.x, + .y = c * a.y, + }; +} + +gboolean +meta_line2_intersects_with (const MetaLine2 *line1, + const MetaLine2 *line2, + MetaVector2 *intersection) +{ + MetaVector2 p = line1->a; + MetaVector2 r = meta_vector2_subtract (line1->b, line1->a); + MetaVector2 q = line2->a; + MetaVector2 s = meta_vector2_subtract (line2->b, line2->a); + float rxs; + float sxr; + float t; + float u; + + /* + * The line (p, r) and (q, s) intersects where + * + * p + t r = q + u s + * + * Calculate t: + * + * (p + t r) × s = (q + u s) × s + * p × s + t (r × s) = q × s + u (s × s) + * p × s + t (r × s) = q × s + * t (r × s) = q × s - p × s + * t (r × s) = (q - p) × s + * t = ((q - p) × s) / (r × s) + * + * Using the same method, for u we get: + * + * u = ((p - q) × r) / (s × r) + */ + + rxs = meta_vector2_cross_product (r, s); + sxr = meta_vector2_cross_product (s, r); + + /* If r × s = 0 then the lines are either parallel or collinear. */ + if (fabs (rxs) < DBL_MIN) + return FALSE; + + t = meta_vector2_cross_product (meta_vector2_subtract (q, p), s) / rxs; + u = meta_vector2_cross_product (meta_vector2_subtract (p, q), r) / sxr; + + /* The lines only intersect if 0 ≤ t ≤ 1 and 0 ≤ u ≤ 1. */ + if (t < 0.0 || t > 1.0 || u < 0.0 || u > 1.0) + return FALSE; + + *intersection = meta_vector2_add (p, meta_vector2_multiply_constant (t, r)); + + return TRUE; +} + +gboolean +meta_border_is_horizontal (MetaBorder *border) +{ + return border->line.a.y == border->line.b.y; +} + +gboolean +meta_border_is_blocking_directions (MetaBorder *border, + MetaBorderMotionDirection directions) +{ + if (meta_border_is_horizontal (border)) + { + if ((directions & (META_BORDER_MOTION_DIRECTION_POSITIVE_Y | + META_BORDER_MOTION_DIRECTION_NEGATIVE_Y)) == 0) + return FALSE; + } + else + { + if ((directions & (META_BORDER_MOTION_DIRECTION_POSITIVE_X | + META_BORDER_MOTION_DIRECTION_NEGATIVE_X)) == 0) + return FALSE; + } + + return (~border->blocking_directions & directions) != directions; +} + +unsigned int +meta_border_get_allows_directions (MetaBorder *border) +{ + return ~border->blocking_directions & + (META_BORDER_MOTION_DIRECTION_POSITIVE_X | + META_BORDER_MOTION_DIRECTION_POSITIVE_Y | + META_BORDER_MOTION_DIRECTION_NEGATIVE_X | + META_BORDER_MOTION_DIRECTION_NEGATIVE_Y); +} + +void +meta_border_set_allows_directions (MetaBorder *border, unsigned int directions) +{ + border->blocking_directions = + ~directions & (META_BORDER_MOTION_DIRECTION_POSITIVE_X | + META_BORDER_MOTION_DIRECTION_POSITIVE_Y | + META_BORDER_MOTION_DIRECTION_NEGATIVE_X | + META_BORDER_MOTION_DIRECTION_NEGATIVE_Y); +} diff --git a/src/core/meta-border.h b/src/core/meta-border.h new file mode 100644 index 000000000..dd76db5c9 --- /dev/null +++ b/src/core/meta-border.h @@ -0,0 +1,84 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +/* + * Copyright (C) 2015 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 + */ + +#ifndef META_BORDER_H +#define META_BORDER_H + +#include + +typedef enum +{ + META_BORDER_MOTION_DIRECTION_POSITIVE_X = 1 << 0, + META_BORDER_MOTION_DIRECTION_POSITIVE_Y = 1 << 1, + META_BORDER_MOTION_DIRECTION_NEGATIVE_X = 1 << 2, + META_BORDER_MOTION_DIRECTION_NEGATIVE_Y = 1 << 3, +} MetaBorderMotionDirection; + +typedef struct _MetaVector2 +{ + float x; + float y; +} MetaVector2; + +typedef struct _MetaLine2 +{ + MetaVector2 a; + MetaVector2 b; +} MetaLine2; + +typedef struct _MetaBorder +{ + MetaLine2 line; + MetaBorderMotionDirection blocking_directions; +} MetaBorder; + +static inline MetaVector2 +meta_vector2_subtract (const MetaVector2 a, + const MetaVector2 b) +{ + return (MetaVector2) { + .x = a.x - b.x, + .y = a.y - b.y, + }; +} + +gboolean +meta_line2_intersects_with (const MetaLine2 *line1, + const MetaLine2 *line2, + MetaVector2 *intersection); + +gboolean +meta_border_is_horizontal (MetaBorder *border); + +gboolean +meta_border_is_blocking_directions (MetaBorder *border, + MetaBorderMotionDirection directions); + +unsigned int +meta_border_get_allows_directions (MetaBorder *border); + +void +meta_border_set_allows_directions (MetaBorder *border, unsigned int directions); + +#endif /* META_BORDER_H */