barrier: Add support for new barrier features in XInput 2.3

XInput 2.3 adds support for "barrier events", which let us know when
a pointer barrier has been hit, and when the pointer has stopped
hitting the barrier, and lets us "release" the barrier, temporarily
letting the pointer pass through the barrier. These features can be
combined to allow for certain pointer gestures, such as "pushing"
against the bottom of the screen, or stopping the pointer on monitor
edges while dragging slowly for increased edge precision.

This commit should allow graceful fallback if servers with
XInput 2.3 aren't supported.

https://bugzilla.gnome.org/show_bug.cgi?id=677215
This commit is contained in:
Jasper St. Pierre 2012-07-30 15:57:53 -03:00
parent 8b21df92f0
commit 57c31a56f4
7 changed files with 278 additions and 3 deletions

View File

@ -201,6 +201,9 @@ fi
PKG_CHECK_MODULES(MUTTER, $MUTTER_PC_MODULES) PKG_CHECK_MODULES(MUTTER, $MUTTER_PC_MODULES)
PKG_CHECK_EXISTS([xi >= 1.6.99.1],
AC_DEFINE([HAVE_XI23],[1],[Define if you have support for XInput 2.3 or greater]))
# This is used for plugins # This is used for plugins
AC_SUBST(CLUTTER_PACKAGE) AC_SUBST(CLUTTER_PACKAGE)
PKG_CHECK_MODULES(CLUTTER, $CLUTTER_PACKAGE) PKG_CHECK_MODULES(CLUTTER, $CLUTTER_PACKAGE)

View File

@ -4,6 +4,7 @@
#include <glib-object.h> #include <glib-object.h>
#include <X11/extensions/XInput2.h>
#include <X11/extensions/Xfixes.h> #include <X11/extensions/Xfixes.h>
#include <meta/util.h> #include <meta/util.h>
#include <meta/barrier.h> #include <meta/barrier.h>
@ -29,6 +30,15 @@ enum {
static GParamSpec *obj_props[PROP_LAST]; static GParamSpec *obj_props[PROP_LAST];
enum {
HIT,
LEFT,
LAST_SIGNAL,
};
static guint obj_signals[LAST_SIGNAL];
struct _MetaBarrierPrivate struct _MetaBarrierPrivate
{ {
MetaDisplay *display; MetaDisplay *display;
@ -133,6 +143,31 @@ meta_barrier_is_active (MetaBarrier *barrier)
return barrier->priv->xbarrier != 0; return barrier->priv->xbarrier != 0;
} }
/**
* meta_barrier_release:
* @barrier: The barrier to release
* @event: The event to release the pointer for
*
* In XI2.3, pointer barriers provide a feature where they can
* be temporarily released so that the pointer goes through
* them. Pass a #MetaBarrierEvent to release the barrier for
* this event sequence.
*/
void
meta_barrier_release (MetaBarrier *barrier,
MetaBarrierEvent *event)
{
#ifdef HAVE_XI23
MetaBarrierPrivate *priv = barrier->priv;
if (META_DISPLAY_HAS_XINPUT_23 (priv->display))
{
XIBarrierReleasePointer (priv->display->xdisplay,
META_VIRTUAL_CORE_POINTER_ID,
priv->xbarrier, event->event_id);
}
#endif /* HAVE_XI23 */
}
static void static void
meta_barrier_constructed (GObject *object) meta_barrier_constructed (GObject *object)
{ {
@ -157,6 +192,12 @@ meta_barrier_constructed (GObject *object)
priv->x2, priv->y2, priv->x2, priv->y2,
priv->directions, 0, NULL); priv->directions, 0, NULL);
/* Take a ref that we'll release when the XID dies inside destroy(),
* so that the object stays alive and doesn't get GC'd. */
g_object_ref (barrier);
g_hash_table_insert (priv->display->window_ids, &priv->xbarrier, barrier);
G_OBJECT_CLASS (meta_barrier_parent_class)->constructed (object); G_OBJECT_CLASS (meta_barrier_parent_class)->constructed (object);
} }
@ -215,6 +256,42 @@ meta_barrier_class_init (MetaBarrierClass *klass)
g_object_class_install_properties (object_class, PROP_LAST, obj_props); g_object_class_install_properties (object_class, PROP_LAST, obj_props);
/**
* MetaBarrier::hit:
* @barrier: The #MetaBarrier that was hit
* @event: A #MetaBarrierEvent that has the details of how
* the barrier was hit.
*
* When a pointer barrier is hit, this will trigger. This
* requires an XI2-enabled server.
*/
obj_signals[HIT] =
g_signal_new ("hit",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 1,
META_TYPE_BARRIER_EVENT);
/**
* MetaBarrier::left:
* @barrier: The #MetaBarrier that was left
* @event: A #MetaBarrierEvent that has the details of how
* the barrier was left.
*
* When a pointer barrier hitbox was left, this will trigger.
* This requires an XI2-enabled server.
*/
obj_signals[LEFT] =
g_signal_new ("left",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 1,
META_TYPE_BARRIER_EVENT);
g_type_class_add_private (object_class, sizeof(MetaBarrierPrivate)); g_type_class_add_private (object_class, sizeof(MetaBarrierPrivate));
} }
@ -233,7 +310,10 @@ meta_barrier_destroy (MetaBarrier *barrier)
return; return;
XFixesDestroyPointerBarrier (dpy, priv->xbarrier); XFixesDestroyPointerBarrier (dpy, priv->xbarrier);
g_hash_table_remove (priv->display->window_ids, &priv->xbarrier);
priv->xbarrier = 0; priv->xbarrier = 0;
g_object_unref (barrier);
} }
static void static void
@ -241,3 +321,77 @@ meta_barrier_init (MetaBarrier *barrier)
{ {
barrier->priv = G_TYPE_INSTANCE_GET_PRIVATE (barrier, META_TYPE_BARRIER, MetaBarrierPrivate); barrier->priv = G_TYPE_INSTANCE_GET_PRIVATE (barrier, META_TYPE_BARRIER, MetaBarrierPrivate);
} }
#ifdef HAVE_XI23
static void
meta_barrier_fire_event (MetaBarrier *barrier,
XIBarrierEvent *xevent)
{
MetaBarrierEvent *event = g_slice_new0 (MetaBarrierEvent);
event->ref_count = 1;
event->event_id = xevent->eventid;
event->dt = xevent->dtime;
event->x = xevent->root_x;
event->y = xevent->root_y;
event->dx = xevent->dx;
event->dy = xevent->dy;
event->released = (xevent->flags & XIBarrierPointerReleased) != 0;
event->grabbed = (xevent->flags & XIBarrierDeviceIsGrabbed) != 0;
switch (xevent->evtype)
{
case XI_BarrierHit:
g_signal_emit (barrier, obj_signals[HIT], 0, event);
break;
case XI_BarrierLeave:
g_signal_emit (barrier, obj_signals[LEFT], 0, event);
break;
default:
g_assert_not_reached ();
}
}
gboolean
meta_display_process_barrier_event (MetaDisplay *display,
XIBarrierEvent *xev)
{
MetaBarrier *barrier;
barrier = g_hash_table_lookup (display->window_ids, &xev->barrier);
if (barrier != NULL)
{
meta_barrier_fire_event (barrier, xev);
return TRUE;
}
return FALSE;
}
#endif /* HAVE_XI23 */
static MetaBarrierEvent *
meta_barrier_event_ref (MetaBarrierEvent *event)
{
g_return_val_if_fail (event != NULL, NULL);
g_return_val_if_fail (event->ref_count > 0, NULL);
g_atomic_int_inc ((volatile int *)&event->ref_count);
return event;
}
static void
meta_barrier_event_unref (MetaBarrierEvent *event)
{
g_return_if_fail (event != NULL);
g_return_if_fail (event->ref_count > 0);
if (g_atomic_int_dec_and_test ((volatile int *)&event->ref_count))
g_slice_free (MetaBarrierEvent, event);
}
G_DEFINE_BOXED_TYPE (MetaBarrierEvent,
meta_barrier_event,
meta_barrier_event_ref,
meta_barrier_event_unref)

View File

@ -39,6 +39,7 @@
#include <meta/display.h> #include <meta/display.h>
#include "keybindings-private.h" #include "keybindings-private.h"
#include <meta/prefs.h> #include <meta/prefs.h>
#include <meta/barrier.h>
#ifdef HAVE_STARTUP_NOTIFICATION #ifdef HAVE_STARTUP_NOTIFICATION
#include <libsn/sn.h> #include <libsn/sn.h>
@ -317,6 +318,12 @@ struct _MetaDisplay
unsigned int have_damage : 1; unsigned int have_damage : 1;
#define META_DISPLAY_HAS_COMPOSITE(display) ((display)->have_composite) #define META_DISPLAY_HAS_COMPOSITE(display) ((display)->have_composite)
#define META_DISPLAY_HAS_DAMAGE(display) ((display)->have_damage) #define META_DISPLAY_HAS_DAMAGE(display) ((display)->have_damage)
#ifdef HAVE_XI23
gboolean have_xinput_23 : 1;
#define META_DISPLAY_HAS_XINPUT_23(display) ((display)->have_xinput_23)
#else
#define META_DISPLAY_HAS_XINPUT_23(display) FALSE
#endif /* HAVE_XI23 */
}; };
struct _MetaDisplayClass struct _MetaDisplayClass
@ -449,4 +456,9 @@ void meta_display_overlay_key_activate (MetaDisplay *display);
/* In above-tab-keycode.c */ /* In above-tab-keycode.c */
guint meta_display_get_above_tab_keycode (MetaDisplay *display); guint meta_display_get_above_tab_keycode (MetaDisplay *display);
#ifdef HAVE_XI23
gboolean meta_display_process_barrier_event (MetaDisplay *display,
XIBarrierEvent *event);
#endif /* HAVE_XI23 */
#endif #endif

View File

@ -590,7 +590,7 @@ meta_display_open (void)
the_display->window_ids = g_hash_table_new (meta_unsigned_long_hash, the_display->window_ids = g_hash_table_new (meta_unsigned_long_hash,
meta_unsigned_long_equal); meta_unsigned_long_equal);
i = 0; i = 0;
while (i < N_IGNORED_CROSSING_SERIALS) while (i < N_IGNORED_CROSSING_SERIALS)
{ {
@ -790,8 +790,16 @@ meta_display_open (void)
&the_display->xinput_event_base)) &the_display->xinput_event_base))
{ {
if (XIQueryVersion (the_display->xdisplay, &major, &minor) == Success) if (XIQueryVersion (the_display->xdisplay, &major, &minor) == Success)
if (((major * 10) + minor) >= 22) {
has_xi = TRUE; int version = (major * 10) + minor;
if (version >= 22)
has_xi = TRUE;
#ifdef HAVE_XI23
if (version >= 23)
the_display->have_xinput_23 = TRUE;
#endif /* HAVE_XI23 */
}
} }
if (!has_xi) if (!has_xi)
@ -1001,6 +1009,9 @@ meta_display_list_windows (MetaDisplay *display,
{ {
MetaWindow *window = value; MetaWindow *window = value;
if (!META_IS_WINDOW (window))
continue;
if (!window->override_redirect || if (!window->override_redirect ||
(flags & META_LIST_INCLUDE_OVERRIDE_REDIRECT) != 0) (flags & META_LIST_INCLUDE_OVERRIDE_REDIRECT) != 0)
winlist = g_slist_prepend (winlist, window); winlist = g_slist_prepend (winlist, window);
@ -1832,6 +1843,12 @@ get_input_event (MetaDisplay *display,
case XI_Leave: case XI_Leave:
if (((XIEnterEvent *) input_event)->deviceid == META_VIRTUAL_CORE_POINTER_ID) if (((XIEnterEvent *) input_event)->deviceid == META_VIRTUAL_CORE_POINTER_ID)
return input_event; return input_event;
break;
case XI_BarrierHit:
case XI_BarrierLeave:
if (((XIBarrierEvent *) input_event)->deviceid == META_VIRTUAL_CORE_POINTER_ID)
return input_event;
break;
default: default:
break; break;
} }
@ -2395,6 +2412,13 @@ event_callback (XEvent *event,
} }
break; break;
#ifdef HAVE_XI23
case XI_BarrierHit:
case XI_BarrierLeave:
if (meta_display_process_barrier_event (display, (XIBarrierEvent *) input_event))
filter_out_event = bypass_compositor = TRUE;
break;
#endif /* HAVE_XI23 */
} }
} }
else else
@ -2926,6 +2950,11 @@ event_get_modified_window (MetaDisplay *display,
case XI_Enter: case XI_Enter:
case XI_Leave: case XI_Leave:
return ((XIEnterEvent *) input_event)->event; return ((XIEnterEvent *) input_event)->event;
#ifdef HAVE_XI23
case XI_BarrierHit:
case XI_BarrierLeave:
return ((XIBarrierEvent *) input_event)->event;
#endif /* HAVE_XI23 */
} }
} }
@ -3201,6 +3230,14 @@ meta_spew_xi2_event (MetaDisplay *display,
case XI_Leave: case XI_Leave:
name = "XI_Leave"; name = "XI_Leave";
break; break;
#ifdef HAVE_XI23
case XI_BarrierHit:
name = "XI_BarrierHit";
break;
case XI_BarrierLeave:
name = "XI_BarrierLeave";
break;
#endif /* HAVE_XI23 */
} }
switch (input_event->evtype) switch (input_event->evtype)
@ -5686,6 +5723,23 @@ meta_display_get_xinput_opcode (MetaDisplay *display)
return display->xinput_opcode; return display->xinput_opcode;
} }
/**
* 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.
*
* Clients should use this method to determine whether their
* interfaces should depend on new barrier features.
*/
gboolean
meta_display_supports_extended_barriers (MetaDisplay *display)
{
return META_DISPLAY_HAS_XINPUT_23 (display);
}
/** /**
* meta_display_get_xdisplay: (skip) * meta_display_get_xdisplay: (skip)
* *

View File

@ -758,6 +758,13 @@ meta_screen_new (MetaDisplay *display,
XISetMask (mask.mask, XI_FocusIn); XISetMask (mask.mask, XI_FocusIn);
XISetMask (mask.mask, XI_FocusOut); XISetMask (mask.mask, XI_FocusOut);
XISetMask (mask.mask, XI_Motion); XISetMask (mask.mask, XI_Motion);
#ifdef HAVE_XI23
if (META_DISPLAY_HAS_XINPUT_23 (display))
{
XISetMask (mask.mask, XI_BarrierHit);
XISetMask (mask.mask, XI_BarrierLeave);
}
#endif /* HAVE_XI23 */
XISelectEvents (xdisplay, xroot, &mask, 1); XISelectEvents (xdisplay, xroot, &mask, 1);
event_mask = (SubstructureRedirectMask | SubstructureNotifyMask | event_mask = (SubstructureRedirectMask | SubstructureNotifyMask |
@ -1230,6 +1237,9 @@ meta_screen_foreach_window (MetaScreen *screen,
{ {
MetaWindow *window = tmp->data; MetaWindow *window = tmp->data;
if (!META_IS_WINDOW (window))
continue;
if (window->screen == screen && !window->override_redirect) if (window->screen == screen && !window->override_redirect)
(* func) (screen, window, data); (* func) (screen, window, data);
} }

View File

@ -20,6 +20,8 @@ typedef struct _MetaBarrier MetaBarrier;
typedef struct _MetaBarrierClass MetaBarrierClass; typedef struct _MetaBarrierClass MetaBarrierClass;
typedef struct _MetaBarrierPrivate MetaBarrierPrivate; typedef struct _MetaBarrierPrivate MetaBarrierPrivate;
typedef struct _MetaBarrierEvent MetaBarrierEvent;
struct _MetaBarrier struct _MetaBarrier
{ {
GObject parent; GObject parent;
@ -36,6 +38,8 @@ GType meta_barrier_get_type (void) G_GNUC_CONST;
gboolean meta_barrier_is_active (MetaBarrier *barrier); gboolean meta_barrier_is_active (MetaBarrier *barrier);
void meta_barrier_destroy (MetaBarrier *barrier); void meta_barrier_destroy (MetaBarrier *barrier);
void meta_barrier_release (MetaBarrier *barrier,
MetaBarrierEvent *event);
/* Keep in sync with XFixes */ /* Keep in sync with XFixes */
typedef enum { typedef enum {
@ -45,6 +49,43 @@ typedef enum {
META_BARRIER_DIRECTION_NEGATIVE_Y = 1 << 3, META_BARRIER_DIRECTION_NEGATIVE_Y = 1 << 3,
} MetaBarrierDirection; } MetaBarrierDirection;
/**
* MetaBarrierEvent:
* @event_id: A unique integer ID identifying a
* consecutive series of motions at or along the barrier
* @dt: Server time, in milliseconds, since the last event
* sent for this barrier
* @x: The cursor X position in screen coordinates
* @y: The cursor Y position in screen coordinates.
* @dx: If the cursor hadn't been constrained, the delta
* of X movement past the barrier, in screen coordinates
* @dy: If the cursor hadn't been constrained, the delta
* of X movement past the barrier, in screen coordinates
* @released: A boolean flag, %TRUE if this event generated
* by the pointer leaving the barrier as a result of a client
* calling meta_barrier_release() (will be set only for
* MetaBarrier::leave signals)
* @grabbed: A boolean flag, %TRUE if the pointer was grabbed
* at the time this event was sent
*/
struct _MetaBarrierEvent {
/* < private > */
volatile guint ref_count;
/* < public > */
int event_id;
int dt;
double x;
double y;
double dx;
double dy;
gboolean released;
gboolean grabbed;
};
#define META_TYPE_BARRIER_EVENT (meta_barrier_event_get_type ())
GType meta_barrier_event_get_type (void) G_GNUC_CONST;
G_END_DECLS G_END_DECLS
#endif /* __META_BARRIER_H__ */ #endif /* __META_BARRIER_H__ */

View File

@ -68,6 +68,7 @@ void meta_display_get_compositor_version (MetaDisplay *display,
int *major, int *major,
int *minor); int *minor);
int meta_display_get_xinput_opcode (MetaDisplay *display); int meta_display_get_xinput_opcode (MetaDisplay *display);
gboolean meta_display_supports_extended_barriers (MetaDisplay *display);
Display *meta_display_get_xdisplay (MetaDisplay *display); Display *meta_display_get_xdisplay (MetaDisplay *display);
MetaCompositor *meta_display_get_compositor (MetaDisplay *display); MetaCompositor *meta_display_get_compositor (MetaDisplay *display);
GSList *meta_display_get_screens (MetaDisplay *display); GSList *meta_display_get_screens (MetaDisplay *display);