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 */