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);
MetaBarrierFlags meta_barrier_get_flags (MetaBarrier *barrier);
G_END_DECLS
#endif /* META_BARRIER_PRIVATE_H */

View File

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

View File

@ -44,6 +44,7 @@ struct _MetaBarrierManagerNative
{
GHashTable *barriers;
GMutex mutex;
MetaBarrierImplNative *pointer_trap;
};
typedef enum
@ -504,6 +505,38 @@ clamp_to_barrier (MetaBarrierImplNative *self,
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
meta_barrier_manager_native_process_in_impl (MetaBarrierManagerNative *manager,
ClutterInputDevice *device,
@ -524,11 +557,18 @@ meta_barrier_manager_native_process_in_impl (MetaBarrierManagerNative *manager,
device, NULL, &prev_pos, NULL))
return;
g_mutex_lock (&manager->mutex);
prev_x = prev_pos.x;
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. */
if (prev_x < *x)
motion_dir |= META_BARRIER_DIRECTION_POSITIVE_X;
@ -548,7 +588,18 @@ meta_barrier_manager_native_process_in_impl (MetaBarrierManagerNative *manager,
*x, *y,
motion_dir,
&barrier_impl))
{
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
break;
}
@ -590,7 +641,10 @@ meta_barrier_impl_native_release (MetaBarrierImpl *impl,
if (self->state == META_BARRIER_STATE_HELD &&
event->event_id == self->trigger_serial)
{
self->state = META_BARRIER_STATE_RELEASE;
self->manager->pointer_trap = NULL;
}
}
static void
@ -599,6 +653,8 @@ meta_barrier_impl_native_destroy (MetaBarrierImpl *impl)
MetaBarrierImplNative *self = META_BARRIER_IMPL_NATIVE (impl);
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_mutex_unlock (&self->manager->mutex);
g_main_context_unref (self->main_context);

View File

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