mirror of
https://github.com/brl/mutter.git
synced 2025-06-14 01:09:30 +00:00
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:
@ -4,6 +4,7 @@
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
#include <X11/extensions/XInput2.h>
|
||||
#include <X11/extensions/Xfixes.h>
|
||||
#include <meta/util.h>
|
||||
#include <meta/barrier.h>
|
||||
@ -29,6 +30,15 @@ enum {
|
||||
|
||||
static GParamSpec *obj_props[PROP_LAST];
|
||||
|
||||
enum {
|
||||
HIT,
|
||||
LEFT,
|
||||
|
||||
LAST_SIGNAL,
|
||||
};
|
||||
|
||||
static guint obj_signals[LAST_SIGNAL];
|
||||
|
||||
struct _MetaBarrierPrivate
|
||||
{
|
||||
MetaDisplay *display;
|
||||
@ -133,6 +143,31 @@ meta_barrier_is_active (MetaBarrier *barrier)
|
||||
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
|
||||
meta_barrier_constructed (GObject *object)
|
||||
{
|
||||
@ -157,6 +192,12 @@ meta_barrier_constructed (GObject *object)
|
||||
priv->x2, priv->y2,
|
||||
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);
|
||||
}
|
||||
|
||||
@ -215,6 +256,42 @@ meta_barrier_class_init (MetaBarrierClass *klass)
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
@ -233,7 +310,10 @@ meta_barrier_destroy (MetaBarrier *barrier)
|
||||
return;
|
||||
|
||||
XFixesDestroyPointerBarrier (dpy, priv->xbarrier);
|
||||
g_hash_table_remove (priv->display->window_ids, &priv->xbarrier);
|
||||
priv->xbarrier = 0;
|
||||
|
||||
g_object_unref (barrier);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -241,3 +321,77 @@ meta_barrier_init (MetaBarrier *barrier)
|
||||
{
|
||||
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)
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include <meta/display.h>
|
||||
#include "keybindings-private.h"
|
||||
#include <meta/prefs.h>
|
||||
#include <meta/barrier.h>
|
||||
|
||||
#ifdef HAVE_STARTUP_NOTIFICATION
|
||||
#include <libsn/sn.h>
|
||||
@ -317,6 +318,12 @@ struct _MetaDisplay
|
||||
unsigned int have_damage : 1;
|
||||
#define META_DISPLAY_HAS_COMPOSITE(display) ((display)->have_composite)
|
||||
#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
|
||||
@ -449,4 +456,9 @@ void meta_display_overlay_key_activate (MetaDisplay *display);
|
||||
/* In above-tab-keycode.c */
|
||||
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
|
||||
|
@ -590,7 +590,7 @@ meta_display_open (void)
|
||||
|
||||
the_display->window_ids = g_hash_table_new (meta_unsigned_long_hash,
|
||||
meta_unsigned_long_equal);
|
||||
|
||||
|
||||
i = 0;
|
||||
while (i < N_IGNORED_CROSSING_SERIALS)
|
||||
{
|
||||
@ -790,8 +790,16 @@ meta_display_open (void)
|
||||
&the_display->xinput_event_base))
|
||||
{
|
||||
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)
|
||||
@ -1001,6 +1009,9 @@ meta_display_list_windows (MetaDisplay *display,
|
||||
{
|
||||
MetaWindow *window = value;
|
||||
|
||||
if (!META_IS_WINDOW (window))
|
||||
continue;
|
||||
|
||||
if (!window->override_redirect ||
|
||||
(flags & META_LIST_INCLUDE_OVERRIDE_REDIRECT) != 0)
|
||||
winlist = g_slist_prepend (winlist, window);
|
||||
@ -1832,6 +1843,12 @@ get_input_event (MetaDisplay *display,
|
||||
case XI_Leave:
|
||||
if (((XIEnterEvent *) input_event)->deviceid == META_VIRTUAL_CORE_POINTER_ID)
|
||||
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:
|
||||
break;
|
||||
}
|
||||
@ -2395,6 +2412,13 @@ event_callback (XEvent *event,
|
||||
|
||||
}
|
||||
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
|
||||
@ -2926,6 +2950,11 @@ event_get_modified_window (MetaDisplay *display,
|
||||
case XI_Enter:
|
||||
case XI_Leave:
|
||||
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:
|
||||
name = "XI_Leave";
|
||||
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)
|
||||
@ -5686,6 +5723,23 @@ meta_display_get_xinput_opcode (MetaDisplay *display)
|
||||
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)
|
||||
*
|
||||
|
@ -758,6 +758,13 @@ meta_screen_new (MetaDisplay *display,
|
||||
XISetMask (mask.mask, XI_FocusIn);
|
||||
XISetMask (mask.mask, XI_FocusOut);
|
||||
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);
|
||||
|
||||
event_mask = (SubstructureRedirectMask | SubstructureNotifyMask |
|
||||
@ -1230,6 +1237,9 @@ meta_screen_foreach_window (MetaScreen *screen,
|
||||
{
|
||||
MetaWindow *window = tmp->data;
|
||||
|
||||
if (!META_IS_WINDOW (window))
|
||||
continue;
|
||||
|
||||
if (window->screen == screen && !window->override_redirect)
|
||||
(* func) (screen, window, data);
|
||||
}
|
||||
|
Reference in New Issue
Block a user