diff --git a/src/Makefile.am b/src/Makefile.am index 458d77a6f..d397ee29b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -271,6 +271,9 @@ if HAVE_NATIVE_BACKEND libmutter_la_SOURCES += \ backends/native/meta-backend-native.c \ backends/native/meta-backend-native.h \ + backends/native/meta-backend-native-private.h \ + backends/native/meta-barrier-native.c \ + backends/native/meta-barrier-native.h \ backends/native/meta-cursor-renderer-native.c \ backends/native/meta-cursor-renderer-native.h \ backends/native/meta-idle-monitor-native.c \ diff --git a/src/backends/meta-barrier.c b/src/backends/meta-barrier.c index ab4fdbe06..20bc4eff3 100644 --- a/src/backends/meta-barrier.c +++ b/src/backends/meta-barrier.c @@ -12,6 +12,8 @@ #include #include +#include "backends/native/meta-backend-native.h" +#include "backends/native/meta-barrier-native.h" #include "backends/x11/meta-backend-x11.h" #include "backends/x11/meta-barrier-x11.h" #include "mutter-enum-types.h" @@ -166,6 +168,10 @@ meta_barrier_constructed (GObject *object) g_return_if_fail (priv->x1 == priv->x2 || priv->y1 == priv->y2); +#if defined(HAVE_NATIVE_BACKEND) + if (META_IS_BACKEND_NATIVE (meta_get_backend ())) + priv->impl = meta_barrier_impl_native_new (barrier); +#endif #if defined(HAVE_XI23) if (META_IS_BACKEND_X11 (meta_get_backend ()) && !meta_is_wayland_compositor ()) diff --git a/src/backends/native/meta-backend-native-private.h b/src/backends/native/meta-backend-native-private.h new file mode 100644 index 000000000..04583259e --- /dev/null +++ b/src/backends/native/meta-backend-native-private.h @@ -0,0 +1,32 @@ +/* -*- 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_BACKEND_NATIVE_PRIVATE_H +#define META_BACKEND_NATIVE_PRIVATE_H + +#include "backends/native/meta-barrier-native.h" + +MetaBarrierManagerNative *meta_backend_native_get_barrier_manager (MetaBackendNative *native); + +#endif /* META_BACKEND_NATIVE_PRIVATE_H */ diff --git a/src/backends/native/meta-backend-native.c b/src/backends/native/meta-backend-native.c index 22328f0ed..6d0e5db74 100644 --- a/src/backends/native/meta-backend-native.c +++ b/src/backends/native/meta-backend-native.c @@ -24,10 +24,13 @@ #include "config.h" +#include "meta-backend-native.h" +#include "meta-backend-native-private.h" + #include #include -#include "meta-backend-native.h" +#include "meta-barrier-native.h" #include "meta-idle-monitor-native.h" #include "meta-monitor-manager-kms.h" #include "meta-cursor-renderer-native.h" @@ -36,6 +39,10 @@ struct _MetaBackendNativePrivate { MetaLauncher *launcher; + + MetaBarrierManagerNative *barrier_manager; + + GSettings *keyboard_settings; }; typedef struct _MetaBackendNativePrivate MetaBackendNativePrivate; @@ -52,6 +59,22 @@ meta_backend_native_finalize (GObject *object) G_OBJECT_CLASS (meta_backend_native_parent_class)->finalize (object); } +static void +constrain_to_barriers (ClutterInputDevice *device, + guint32 time, + float *new_x, + float *new_y) +{ + MetaBackendNative *native = META_BACKEND_NATIVE (meta_get_backend ()); + MetaBackendNativePrivate *priv = + meta_backend_native_get_instance_private (native); + + meta_barrier_manager_native_process (priv->barrier_manager, + device, + time, + new_x, new_y); +} + /* * The pointer constrain code is mostly a rip-off of the XRandR code from Xorg. * (from xserver/randr/rrcrtc.c, RRConstrainCursorHarder) @@ -141,6 +164,9 @@ pointer_constrain_callback (ClutterInputDevice *device, unsigned int n_monitors; gboolean ret; + /* Constrain to barriers */ + constrain_to_barriers (device, time, new_x, new_y); + monitor_manager = meta_monitor_manager_get (); monitors = meta_monitor_manager_get_monitor_infos (monitor_manager, &n_monitors); @@ -269,6 +295,8 @@ meta_backend_native_init (MetaBackendNative *native) /* We're a display server, so start talking to weston-launch. */ priv->launcher = meta_launcher_new (); + + priv->barrier_manager = meta_barrier_manager_native_new (); } gboolean @@ -281,6 +309,15 @@ meta_activate_vt (int vt, GError **error) return meta_launcher_activate_vt (priv->launcher, vt, error); } +MetaBarrierManagerNative * +meta_backend_native_get_barrier_manager (MetaBackendNative *native) +{ + MetaBackendNativePrivate *priv = + meta_backend_native_get_instance_private (native); + + return priv->barrier_manager; +} + /** * meta_activate_session: * diff --git a/src/backends/native/meta-barrier-native.c b/src/backends/native/meta-barrier-native.c new file mode 100644 index 000000000..8f81d3440 --- /dev/null +++ b/src/backends/native/meta-barrier-native.c @@ -0,0 +1,744 @@ +/* -*- 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 + */ + +/** + * SECTION:barrier-native + * @Title: MetaBarrierImplNative + * @Short_Description: Pointer barriers implementation for the native backend + */ + +#include "config.h" + +#include + +#include +#include +#include "backends/meta-backend-private.h" +#include "backends/meta-barrier-private.h" +#include "backends/native/meta-backend-native.h" +#include "backends/native/meta-backend-native-private.h" +#include "backends/native/meta-barrier-native.h" + +struct _MetaBarrierManagerNative +{ + GHashTable *barriers; +}; + +typedef enum { + /* The barrier is active and responsive to pointer motion. */ + META_BARRIER_STATE_ACTIVE, + + /* An intermediate state after a pointer hit the pointer barrier. */ + META_BARRIER_STATE_HIT, + + /* The barrier was hit by a pointer and is still within the hit box and + * has not been released.*/ + META_BARRIER_STATE_HELD, + + /* The pointer was released by the user. If the following motion hits + * the barrier, it will pass through. */ + META_BARRIER_STATE_RELEASE, + + /* An intermediate state when the pointer has left the barrier. */ + META_BARRIER_STATE_LEFT, +} MetaBarrierState; + +struct _MetaBarrierImplNativePrivate +{ + MetaBarrier *barrier; + MetaBarrierManagerNative *manager; + + gboolean is_active; + MetaBarrierState state; + int trigger_serial; + guint32 last_event_time; + MetaBarrierDirection blocked_dir; +}; + +G_DEFINE_TYPE_WITH_PRIVATE (MetaBarrierImplNative, meta_barrier_impl_native, + META_TYPE_BARRIER_IMPL) + +static int +next_serial (void) +{ + static int barrier_serial = 1; + + barrier_serial++; + + /* If it wraps, avoid 0 as it's not a valid serial. */ + if (barrier_serial == 0) + barrier_serial++; + + 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; +} + +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; +} + +static void +dismiss_pointer (MetaBarrierImplNative *self) +{ + MetaBarrierImplNativePrivate *priv = + meta_barrier_impl_native_get_instance_private (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 +calculate_barrier_hit_box (MetaBarrier *barrier) +{ + Line2 hit_box = barrier_to_line (barrier); + + if (is_barrier_horizontal (barrier)) + { + if (is_barrier_blocking_directions (barrier, + META_BARRIER_DIRECTION_POSITIVE_Y)) + hit_box.a.y -= 2.0f; + if (is_barrier_blocking_directions (barrier, + META_BARRIER_DIRECTION_NEGATIVE_Y)) + hit_box.b.y += 2.0f; + } + else + { + if (is_barrier_blocking_directions (barrier, + META_BARRIER_DIRECTION_POSITIVE_X)) + hit_box.a.x -= 2.0f; + if (is_barrier_blocking_directions (barrier, + META_BARRIER_DIRECTION_NEGATIVE_X)) + hit_box.b.x += 2.0f; + } + + return hit_box; +} + +static gboolean +is_within_box (Line2 box, Vector2 point) +{ + return (point.x >= box.a.x && point.x < box.b.x && + point.y >= box.a.y && point.y < box.b.y); +} + +static void +maybe_release_barrier (gpointer key, + gpointer value, + gpointer user_data) +{ + MetaBarrierImplNative *self = key; + MetaBarrierImplNativePrivate *priv = + meta_barrier_impl_native_get_instance_private (self); + MetaBarrier *barrier = priv->barrier; + Line2 *motion = user_data; + Line2 hit_box; + + if (priv->state != META_BARRIER_STATE_HELD) + return; + + /* 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)) + { + dismiss_pointer (self); + return; + } + } + else + { + if (motion->b.y > MAX (barrier->priv->y1, barrier->priv->y2) || + motion->b.y < MIN (barrier->priv->y1, barrier->priv->y2)) + { + dismiss_pointer (self); + return; + } + } + + /* Release if we don't intersect and end up outside of hit box. */ + hit_box = calculate_barrier_hit_box (barrier); + if (!is_within_box (hit_box, motion->b)) + { + dismiss_pointer (self); + return; + } +} + +static void +maybe_release_barriers (MetaBarrierManagerNative *manager, + float prev_x, + float prev_y, + float x, + float y) +{ + Line2 motion = { + .a = { + .x = prev_x, + .y = prev_y, + }, + .b = { + .x = x, + .y = y, + }, + }; + + g_hash_table_foreach (manager->barriers, + maybe_release_barrier, + &motion); +} + +typedef struct _MetaClosestBarrierData +{ + struct + { + Line2 motion; + MetaBarrierDirection directions; + } in; + + struct + { + float closest_distance_2; + MetaBarrierImplNative *barrier_impl; + } out; +} MetaClosestBarrierData; + +static void +update_closest_barrier (gpointer key, + gpointer value, + gpointer user_data) +{ + MetaBarrierImplNative *self = key; + MetaBarrierImplNativePrivate *priv = + meta_barrier_impl_native_get_instance_private (self); + MetaBarrier *barrier = priv->barrier; + MetaClosestBarrierData *data = user_data; + Line2 barrier_line; + Vector2 intersection; + float dx, dy; + float distance_2; + + /* Ignore if the barrier is not blocking in any of the motions directions. */ + if (!is_barrier_blocking_directions (barrier, data->in.directions)) + return; + + /* Ignore if the barrier released the pointer. */ + if (priv->state == META_BARRIER_STATE_RELEASE) + return; + + /* Ignore if we are moving away from barrier. */ + if (priv->state == META_BARRIER_STATE_HELD && + (data->in.directions & priv->blocked_dir) == 0) + return; + + /* 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)) + return; + + /* Calculate the distance to the barrier and keep track of the closest + * barrier. */ + dx = intersection.x - data->in.motion.a.x; + dy = intersection.y - data->in.motion.a.y; + distance_2 = dx*dx + dy*dy; + if (data->out.barrier_impl == NULL || + distance_2 < data->out.closest_distance_2) + { + data->out.barrier_impl = self; + data->out.closest_distance_2 = distance_2; + } +} + +static gboolean +get_closest_barrier (MetaBarrierManagerNative *manager, + float prev_x, + float prev_y, + float x, + float y, + MetaBarrierDirection motion_dir, + MetaBarrierImplNative **barrier_impl) +{ + MetaClosestBarrierData closest_barrier_data; + + closest_barrier_data = (MetaClosestBarrierData) { + .in = { + .motion = { + .a = { + .x = prev_x, + .y = prev_y, + }, + .b = { + .x = x, + .y = y, + }, + }, + .directions = motion_dir, + }, + }; + + g_hash_table_foreach (manager->barriers, + update_closest_barrier, + &closest_barrier_data); + + if (closest_barrier_data.out.barrier_impl != NULL) + { + *barrier_impl = closest_barrier_data.out.barrier_impl; + return TRUE; + } + else + { + return FALSE; + } +} + +typedef struct _MetaBarrierEventData +{ + guint32 time; + float prev_x; + float prev_y; + float x; + float y; + float dx; + float dy; +} MetaBarrierEventData; + +static void +emit_barrier_event (MetaBarrierImplNative *self, + guint32 time, + float prev_x, + float prev_y, + float x, + float y, + float dx, + float dy) +{ + MetaBarrierImplNativePrivate *priv = + meta_barrier_impl_native_get_instance_private (self); + MetaBarrier *barrier = priv->barrier; + MetaBarrierEvent *event = g_slice_new0 (MetaBarrierEvent); + MetaBarrierState old_state = priv->state; + + switch (priv->state) + { + case META_BARRIER_STATE_HIT: + priv->state = META_BARRIER_STATE_HELD; + priv->trigger_serial = next_serial (); + event->dt = 0; + + break; + case META_BARRIER_STATE_RELEASE: + case META_BARRIER_STATE_LEFT: + priv->state = META_BARRIER_STATE_ACTIVE; + + /* Intentional fall-through. */ + case META_BARRIER_STATE_HELD: + event->dt = time - priv->last_event_time; + + break; + case META_BARRIER_STATE_ACTIVE: + g_assert_not_reached (); /* Invalid state. */ + } + + event->ref_count = 1; + event->event_id = priv->trigger_serial; + event->time = time; + + event->x = x; + event->y = y; + event->dx = dx; + event->dy = dy; + + event->grabbed = priv->state == META_BARRIER_STATE_HELD; + event->released = old_state == META_BARRIER_STATE_RELEASE; + + priv->last_event_time = time; + + if (priv->state == META_BARRIER_STATE_HELD) + _meta_barrier_emit_hit_signal (barrier, event); + else + _meta_barrier_emit_left_signal (barrier, event); + + meta_barrier_event_unref (event); +} + +static void +maybe_emit_barrier_event (gpointer key, gpointer value, gpointer user_data) +{ + MetaBarrierImplNative *self = key; + MetaBarrierImplNativePrivate *priv = + meta_barrier_impl_native_get_instance_private (self); + MetaBarrierEventData *data = user_data; + + switch (priv->state) { + case META_BARRIER_STATE_ACTIVE: + break; + case META_BARRIER_STATE_HIT: + case META_BARRIER_STATE_HELD: + case META_BARRIER_STATE_RELEASE: + case META_BARRIER_STATE_LEFT: + emit_barrier_event (self, + data->time, + data->prev_x, + data->prev_y, + data->x, + data->y, + data->dx, + data->dy); + break; + } +} + +/* Clamp (x, y) to the barrier and remove clamped direction from motion_dir. */ +static void +clamp_to_barrier (MetaBarrierImplNative *self, + MetaBarrierDirection *motion_dir, + float *x, + float *y) +{ + MetaBarrierImplNativePrivate *priv = + meta_barrier_impl_native_get_instance_private (self); + MetaBarrier *barrier = priv->barrier; + + if (is_barrier_horizontal (barrier)) + { + if (*motion_dir & META_BARRIER_DIRECTION_POSITIVE_Y) + *y = barrier->priv->y1; + else if (*motion_dir & META_BARRIER_DIRECTION_NEGATIVE_Y) + *y = barrier->priv->y1; + + priv->blocked_dir = *motion_dir & (META_BARRIER_DIRECTION_POSITIVE_Y | + META_BARRIER_DIRECTION_NEGATIVE_Y); + *motion_dir &= ~(META_BARRIER_DIRECTION_POSITIVE_Y | + META_BARRIER_DIRECTION_NEGATIVE_Y); + } + else + { + if (*motion_dir & META_BARRIER_DIRECTION_POSITIVE_X) + *x = barrier->priv->x1; + else if (*motion_dir & META_BARRIER_DIRECTION_NEGATIVE_X) + *x = barrier->priv->x1; + + priv->blocked_dir = *motion_dir & (META_BARRIER_DIRECTION_POSITIVE_X | + META_BARRIER_DIRECTION_NEGATIVE_X); + *motion_dir &= ~(META_BARRIER_DIRECTION_POSITIVE_X | + META_BARRIER_DIRECTION_NEGATIVE_X); + } + + priv->state = META_BARRIER_STATE_HIT; +} + +void +meta_barrier_manager_native_process (MetaBarrierManagerNative *manager, + ClutterInputDevice *device, + guint32 time, + float *x, + float *y) +{ + ClutterPoint prev_pos; + float prev_x; + float prev_y; + float orig_x = *x; + float orig_y = *y; + MetaBarrierDirection motion_dir = 0; + MetaBarrierEventData barrier_event_data; + MetaBarrierImplNative *barrier_impl; + + if (!clutter_input_device_get_coords (device, NULL, &prev_pos)) + return; + + prev_x = prev_pos.x; + prev_y = prev_pos.y; + + /* Get the direction of the motion vector. */ + if (prev_x < *x) + motion_dir |= META_BARRIER_DIRECTION_POSITIVE_X; + else if (prev_x > *x) + motion_dir |= META_BARRIER_DIRECTION_NEGATIVE_X; + if (prev_y < *y) + motion_dir |= META_BARRIER_DIRECTION_POSITIVE_Y; + else if (prev_y > *y) + motion_dir |= META_BARRIER_DIRECTION_NEGATIVE_Y; + + /* Clamp to the closest barrier in any direction until either there are no + * more barriers to clamp to or all directions have been clamped. */ + while (motion_dir != 0) + { + if (get_closest_barrier (manager, + prev_x, prev_y, + *x, *y, + motion_dir, + &barrier_impl)) + clamp_to_barrier (barrier_impl, &motion_dir, x, y); + else + break; + } + + /* Potentially release active barrier movements. */ + maybe_release_barriers (manager, prev_x, prev_y, *x, *y); + + /* Initiate or continue barrier interaction. */ + barrier_event_data = (MetaBarrierEventData) { + .time = time, + .prev_x = prev_x, + .prev_y = prev_y, + .x = *x, + .y = *y, + .dx = orig_x - prev_x, + .dy = orig_y - prev_y, + }; + + g_hash_table_foreach (manager->barriers, + maybe_emit_barrier_event, + &barrier_event_data); +} + +static gboolean +_meta_barrier_impl_native_is_active (MetaBarrierImpl *impl) +{ + MetaBarrierImplNative *self = META_BARRIER_IMPL_NATIVE (impl); + MetaBarrierImplNativePrivate *priv = + meta_barrier_impl_native_get_instance_private (self); + + return priv->is_active; +} + +static void +_meta_barrier_impl_native_release (MetaBarrierImpl *impl, + MetaBarrierEvent *event) +{ + MetaBarrierImplNative *self = META_BARRIER_IMPL_NATIVE (impl); + MetaBarrierImplNativePrivate *priv = + meta_barrier_impl_native_get_instance_private (self); + + if (priv->state == META_BARRIER_STATE_HELD && + event->event_id == priv->trigger_serial) + priv->state = META_BARRIER_STATE_RELEASE; +} + +static void +_meta_barrier_impl_native_destroy (MetaBarrierImpl *impl) +{ + MetaBarrierImplNative *self = META_BARRIER_IMPL_NATIVE (impl); + MetaBarrierImplNativePrivate *priv = + meta_barrier_impl_native_get_instance_private (self); + + g_hash_table_remove (priv->manager->barriers, self); + priv->is_active = FALSE; +} + +MetaBarrierImpl * +meta_barrier_impl_native_new (MetaBarrier *barrier) +{ + MetaBarrierImplNative *self; + MetaBarrierImplNativePrivate *priv; + MetaBackendNative *native; + MetaBarrierManagerNative *manager; + + self = g_object_new (META_TYPE_BARRIER_IMPL_NATIVE, NULL); + priv = meta_barrier_impl_native_get_instance_private (self); + + priv->barrier = barrier; + priv->is_active = TRUE; + + native = META_BACKEND_NATIVE (meta_get_backend ()); + manager = meta_backend_native_get_barrier_manager (native); + priv->manager = manager; + g_hash_table_add (manager->barriers, self); + + return META_BARRIER_IMPL (self); +} + +static void +meta_barrier_impl_native_class_init (MetaBarrierImplNativeClass *klass) +{ + MetaBarrierImplClass *impl_class = META_BARRIER_IMPL_CLASS (klass); + + impl_class->is_active = _meta_barrier_impl_native_is_active; + impl_class->release = _meta_barrier_impl_native_release; + impl_class->destroy = _meta_barrier_impl_native_destroy; +} + +static void +meta_barrier_impl_native_init (MetaBarrierImplNative *self) +{ +} + +MetaBarrierManagerNative * +meta_barrier_manager_native_new (void) +{ + MetaBarrierManagerNative *manager; + + manager = g_new0 (MetaBarrierManagerNative, 1); + + manager->barriers = g_hash_table_new (NULL, NULL); + + return manager; +} diff --git a/src/backends/native/meta-barrier-native.h b/src/backends/native/meta-barrier-native.h new file mode 100644 index 000000000..0fc414a16 --- /dev/null +++ b/src/backends/native/meta-barrier-native.h @@ -0,0 +1,68 @@ +/* -*- 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_BARRIER_NATIVE_H +#define META_BARRIER_NATIVE_H + +#include "backends/meta-barrier-private.h" + +G_BEGIN_DECLS + +#define META_TYPE_BARRIER_IMPL_NATIVE (meta_barrier_impl_native_get_type ()) +#define META_BARRIER_IMPL_NATIVE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_BARRIER_IMPL_NATIVE, MetaBarrierImplNative)) +#define META_BARRIER_IMPL_NATIVE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_BARRIER_IMPL_NATIVE, MetaBarrierImplNativeClass)) +#define META_IS_BARRIER_IMPL_NATIVE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_BARRIER_IMPL_NATIVE)) +#define META_IS_BARRIER_IMPL_NATIVE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_BARRIER_IMPL_NATIVE)) +#define META_BARRIER_IMPL_NATIVE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_BARRIER_IMPL_NATIVE, MetaBarrierImplNativeClass)) + +typedef struct _MetaBarrierImplNative MetaBarrierImplNative; +typedef struct _MetaBarrierImplNativeClass MetaBarrierImplNativeClass; +typedef struct _MetaBarrierImplNativePrivate MetaBarrierImplNativePrivate; + +typedef struct _MetaBarrierManagerNative MetaBarrierManagerNative; + +struct _MetaBarrierImplNative +{ + MetaBarrierImpl parent; +}; + +struct _MetaBarrierImplNativeClass +{ + MetaBarrierImplClass parent_class; +}; + +GType meta_barrier_impl_native_get_type (void) G_GNUC_CONST; + +MetaBarrierImpl *meta_barrier_impl_native_new (MetaBarrier *barrier); + +MetaBarrierManagerNative *meta_barrier_manager_native_new (void); +void meta_barrier_manager_native_process (MetaBarrierManagerNative *manager, + ClutterInputDevice *device, + guint32 time, + float *x, + float *y); + +G_END_DECLS + +#endif /* META_BARRIER_NATIVE_H */ diff --git a/src/core/display.c b/src/core/display.c index 3c0e2ab06..f3bd73ffc 100644 --- a/src/core/display.c +++ b/src/core/display.c @@ -50,6 +50,7 @@ #include "meta-idle-monitor-dbus.h" #include "meta-cursor-tracker-private.h" #include +#include "backends/native/meta-backend-native.h" #include "backends/x11/meta-backend-x11.h" #include @@ -2906,9 +2907,11 @@ meta_display_get_xinput_opcode (MetaDisplay *display) * meta_display_supports_extended_barriers: * @display: a #MetaDisplay * - * Returns: whether the X server supports extended barrier - * features as defined in version 2.3 of the XInput 2 - * specification. + * Returns: whether pointer barriers can be supported. + * + * When running as an X compositor the X server needs XInput 2 + * version 2.3. When running as a display server it is supported + * when running on the native backend. * * Clients should use this method to determine whether their * interfaces should depend on new barrier features. @@ -2916,7 +2919,18 @@ meta_display_get_xinput_opcode (MetaDisplay *display) gboolean meta_display_supports_extended_barriers (MetaDisplay *display) { - return META_DISPLAY_HAS_XINPUT_23 (display) && !meta_is_wayland_compositor (); +#ifdef HAVE_NATIVE_BACKEND + if (META_IS_BACKEND_NATIVE (meta_get_backend ())) + return TRUE; +#endif + + if (META_IS_BACKEND_X11 (meta_get_backend ())) + { + return (META_DISPLAY_HAS_XINPUT_23 (display) && + !meta_is_wayland_compositor()); + } + + g_assert_not_reached (); } /**