barrier: Add ways to make barriers sticky

A sticky barrier means that a pointer in motion intersecting a barrier
doesn't move once having hit it. The intention with this is to allow an
input capture clients to continue a motion once a barrier is hit.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2628>
This commit is contained in:
Jonas Ådahl 2022-03-31 21:24:46 +02:00 committed by Carlos Garnacho
parent a8b013b006
commit 221ac2af76
4 changed files with 96 additions and 4 deletions

View File

@ -58,6 +58,8 @@ MetaBackend * meta_barrier_get_backend (MetaBarrier *barrier);
MetaBorder * meta_barrier_get_border (MetaBarrier *barrier); MetaBorder * meta_barrier_get_border (MetaBarrier *barrier);
MetaBarrierFlags meta_barrier_get_flags (MetaBarrier *barrier);
G_END_DECLS G_END_DECLS
#endif /* META_BARRIER_PRIVATE_H */ #endif /* META_BARRIER_PRIVATE_H */

View File

@ -28,6 +28,7 @@ typedef struct _MetaBarrierPrivate
MetaBackend *backend; MetaBackend *backend;
MetaBorder border; MetaBorder border;
MetaBarrierImpl *impl; MetaBarrierImpl *impl;
MetaBarrierFlags flags;
} MetaBarrierPrivate; } MetaBarrierPrivate;
static void initable_iface_init (GInitableIface *initable_iface); static void initable_iface_init (GInitableIface *initable_iface);
@ -60,6 +61,7 @@ enum
PROP_X2, PROP_X2,
PROP_Y2, PROP_Y2,
PROP_DIRECTIONS, PROP_DIRECTIONS,
PROP_FLAGS,
PROP_LAST, PROP_LAST,
}; };
@ -125,6 +127,9 @@ meta_barrier_get_property (GObject *object,
g_value_set_flags (value, g_value_set_flags (value,
meta_border_get_allows_directions (&priv->border)); meta_border_get_allows_directions (&priv->border));
break; break;
case PROP_FLAGS:
g_value_set_flags (value, priv->flags);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -170,6 +175,9 @@ meta_barrier_set_property (GObject *object,
meta_border_set_allows_directions (&priv->border, meta_border_set_allows_directions (&priv->border,
g_value_get_flags (value)); g_value_get_flags (value));
break; break;
case PROP_FLAGS:
priv->flags = g_value_get_flags (value);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -362,6 +370,15 @@ meta_barrier_class_init (MetaBarrierClass *klass)
G_PARAM_READWRITE | G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY | G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS); G_PARAM_STATIC_STRINGS);
obj_props[PROP_FLAGS] =
g_param_spec_flags ("flags",
"Flags",
"Flags for manipulating barrier behavior",
META_TYPE_BARRIER_FLAGS,
META_BARRIER_FLAG_NONE,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, PROP_LAST, obj_props); g_object_class_install_properties (object_class, PROP_LAST, obj_props);
@ -426,6 +443,7 @@ meta_barrier_new (MetaBackend *backend,
int x2, int x2,
int y2, int y2,
MetaBarrierDirection directions, MetaBarrierDirection directions,
MetaBarrierFlags flags,
GError **error) GError **error)
{ {
return g_initable_new (META_TYPE_BARRIER, return g_initable_new (META_TYPE_BARRIER,
@ -436,6 +454,7 @@ meta_barrier_new (MetaBackend *backend,
"x2", x2, "x2", x2,
"y2", y2, "y2", y2,
"directions", directions, "directions", directions,
"flags", flags,
NULL); NULL);
} }
@ -469,6 +488,14 @@ meta_barrier_get_border (MetaBarrier *barrier)
return &priv->border; return &priv->border;
} }
MetaBarrierFlags
meta_barrier_get_flags (MetaBarrier *barrier)
{
MetaBarrierPrivate *priv = meta_barrier_get_instance_private (barrier);
return priv->flags;
}
static void static void
meta_barrier_impl_class_init (MetaBarrierImplClass *klass) meta_barrier_impl_class_init (MetaBarrierImplClass *klass)
{ {

View File

@ -44,6 +44,7 @@ struct _MetaBarrierManagerNative
{ {
GHashTable *barriers; GHashTable *barriers;
GMutex mutex; GMutex mutex;
MetaBarrierImplNative *pointer_trap;
}; };
typedef enum typedef enum
@ -504,6 +505,38 @@ clamp_to_barrier (MetaBarrierImplNative *self,
self->state = META_BARRIER_STATE_HIT; self->state = META_BARRIER_STATE_HIT;
} }
static gboolean
stick_to_barrier (MetaBarrierImplNative *self,
MetaBarrierDirection motion_dir,
float prev_x,
float prev_y,
float *x,
float *y)
{
MetaLine2 motion = {
.a = { .x = prev_x, .y = prev_y },
.b = { .x = *x, .y = *y },
};
MetaBorder *border = meta_barrier_get_border (self->barrier);
MetaVector2 intersection;
if (meta_line2_intersects_with (&motion, &border->line,
&intersection))
{
*x = intersection.x;
*y = intersection.y;
self->blocked_dir = motion_dir;
self->state = META_BARRIER_STATE_HIT;
self->manager->pointer_trap = self;
return TRUE;
}
else
{
return FALSE;
}
}
void void
meta_barrier_manager_native_process_in_impl (MetaBarrierManagerNative *manager, meta_barrier_manager_native_process_in_impl (MetaBarrierManagerNative *manager,
ClutterInputDevice *device, ClutterInputDevice *device,
@ -524,11 +557,18 @@ meta_barrier_manager_native_process_in_impl (MetaBarrierManagerNative *manager,
device, NULL, &prev_pos, NULL)) device, NULL, &prev_pos, NULL))
return; return;
g_mutex_lock (&manager->mutex);
prev_x = prev_pos.x; prev_x = prev_pos.x;
prev_y = prev_pos.y; prev_y = prev_pos.y;
if (manager->pointer_trap)
{
*x = prev_pos.x;
*y = prev_pos.y;
return;
}
g_mutex_lock (&manager->mutex);
/* Get the direction of the motion vector. */ /* Get the direction of the motion vector. */
if (prev_x < *x) if (prev_x < *x)
motion_dir |= META_BARRIER_DIRECTION_POSITIVE_X; motion_dir |= META_BARRIER_DIRECTION_POSITIVE_X;
@ -548,7 +588,18 @@ meta_barrier_manager_native_process_in_impl (MetaBarrierManagerNative *manager,
*x, *y, *x, *y,
motion_dir, motion_dir,
&barrier_impl)) &barrier_impl))
clamp_to_barrier (barrier_impl, &motion_dir, x, y); {
MetaBarrier *barrier = barrier_impl->barrier;
if (meta_barrier_get_flags (barrier) & META_BARRIER_FLAG_STICKY)
{
if (stick_to_barrier (barrier_impl, motion_dir,
prev_x, prev_y, x, y))
break;
}
clamp_to_barrier (barrier_impl, &motion_dir, x, y);
}
else else
break; break;
} }
@ -590,7 +641,10 @@ meta_barrier_impl_native_release (MetaBarrierImpl *impl,
if (self->state == META_BARRIER_STATE_HELD && if (self->state == META_BARRIER_STATE_HELD &&
event->event_id == self->trigger_serial) event->event_id == self->trigger_serial)
self->state = META_BARRIER_STATE_RELEASE; {
self->state = META_BARRIER_STATE_RELEASE;
self->manager->pointer_trap = NULL;
}
} }
static void static void
@ -599,6 +653,8 @@ meta_barrier_impl_native_destroy (MetaBarrierImpl *impl)
MetaBarrierImplNative *self = META_BARRIER_IMPL_NATIVE (impl); MetaBarrierImplNative *self = META_BARRIER_IMPL_NATIVE (impl);
g_mutex_lock (&self->manager->mutex); g_mutex_lock (&self->manager->mutex);
if (self->manager->pointer_trap == self)
self->manager->pointer_trap = NULL;
g_hash_table_remove (self->manager->barriers, self); g_hash_table_remove (self->manager->barriers, self);
g_mutex_unlock (&self->manager->mutex); g_mutex_unlock (&self->manager->mutex);
g_main_context_unref (self->main_context); g_main_context_unref (self->main_context);

View File

@ -26,6 +26,12 @@ typedef enum
META_BARRIER_DIRECTION_NEGATIVE_Y = 1 << 3, META_BARRIER_DIRECTION_NEGATIVE_Y = 1 << 3,
} MetaBarrierDirection; } MetaBarrierDirection;
typedef enum
{
META_BARRIER_FLAG_NONE = 1 << 0,
META_BARRIER_FLAG_STICKY = 1 << 1,
} MetaBarrierFlags;
#define META_TYPE_BARRIER (meta_barrier_get_type ()) #define META_TYPE_BARRIER (meta_barrier_get_type ())
META_EXPORT META_EXPORT
G_DECLARE_DERIVABLE_TYPE (MetaBarrier, meta_barrier, G_DECLARE_DERIVABLE_TYPE (MetaBarrier, meta_barrier,
@ -45,6 +51,7 @@ MetaBarrier * meta_barrier_new (MetaBackend *backend,
int x2, int x2,
int y2, int y2,
MetaBarrierDirection directions, MetaBarrierDirection directions,
MetaBarrierFlags flags,
GError **error); GError **error);
META_EXPORT META_EXPORT