From 495c89401a35f97266018fe9938945861d970dbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 17 Jun 2015 12:10:52 +0800 Subject: [PATCH] Implement support for the wp_pointer_constraints protocol The wp_pointer_constraints protocol is a protocol which enables clients to manipulate the behavior of the pointer cursor associated with a seat. Currently available constraints are locking the pointer to a static position, and confining the pointer to a given region. Currently locking is fully implemented, and confining is implemented for rectangular confinement regions. What else is lacking is less troublesome semantics for enabling the lock or confinement; currently the only requirement implemented is that the window that appears focused is the one that may aquire the lock. This means that a pointer could be 'stolen' by creating a new window that receives active focus, or when using focus-follows-mouse, a pointer passes a window that has requested a lock. This semantics can be changed and the protocol itself allows any semantics as seems fit. https://bugzilla.gnome.org/show_bug.cgi?id=744104 --- .gitignore | 2 + src/Makefile.am | 10 + src/backends/meta-backend-private.h | 6 + src/backends/meta-backend.c | 11 + src/backends/meta-pointer-constraint.c | 57 ++ src/backends/meta-pointer-constraint.h | 60 ++ src/backends/native/meta-backend-native.c | 22 + .../meta-pointer-confinement-wayland.c | 200 +++++ .../meta-pointer-confinement-wayland.h | 45 + src/wayland/meta-pointer-lock-wayland.c | 72 ++ src/wayland/meta-pointer-lock-wayland.h | 42 + .../meta-wayland-pointer-constraints.c | 818 ++++++++++++++++++ .../meta-wayland-pointer-constraints.h | 60 ++ src/wayland/meta-wayland-pointer.c | 9 +- src/wayland/meta-wayland-pointer.h | 6 + src/wayland/meta-wayland-surface.c | 77 ++ src/wayland/meta-wayland-surface.h | 18 + src/wayland/meta-wayland.c | 1 + src/wayland/meta-window-wayland.c | 8 + 19 files changed, 1523 insertions(+), 1 deletion(-) create mode 100644 src/backends/meta-pointer-constraint.c create mode 100644 src/backends/meta-pointer-constraint.h create mode 100644 src/wayland/meta-pointer-confinement-wayland.c create mode 100644 src/wayland/meta-pointer-confinement-wayland.h create mode 100644 src/wayland/meta-pointer-lock-wayland.c create mode 100644 src/wayland/meta-pointer-lock-wayland.h create mode 100644 src/wayland/meta-wayland-pointer-constraints.c create mode 100644 src/wayland/meta-wayland-pointer-constraints.h diff --git a/.gitignore b/.gitignore index 62a274376..66df2b877 100644 --- a/.gitignore +++ b/.gitignore @@ -70,6 +70,8 @@ src/pointer-gestures-unstable-v*-protocol.c src/pointer-gestures-unstable-v*-server-protocol.h src/relative-pointer-unstable-v*-protocol.c src/relative-pointer-unstable-v*-server-protocol.h +src/pointer-constraints-unstable-v*-protocol.c +src/pointer-constraints-unstable-v*-server-protocol.h src/meta/meta-version.h doc/reference/*.args doc/reference/*.bak diff --git a/src/Makefile.am b/src/Makefile.am index fb0ccbb5d..ca2483b8b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -53,6 +53,8 @@ mutter_built_sources += \ xdg-shell-unstable-v5-server-protocol.h \ relative-pointer-unstable-v1-protocol.c \ relative-pointer-unstable-v1-server-protocol.h \ + pointer-constraints-unstable-v1-protocol.c \ + pointer-constraints-unstable-v1-server-protocol.h \ $(NULL) endif @@ -86,6 +88,8 @@ libmutter_la_SOURCES = \ backends/meta-monitor-manager-private.h \ backends/meta-monitor-manager-dummy.c \ backends/meta-monitor-manager-dummy.h \ + backends/meta-pointer-constraint.c \ + backends/meta-pointer-constraint.h \ backends/meta-stage.h \ backends/meta-stage.c \ backends/edid-parse.c \ @@ -268,6 +272,12 @@ libmutter_la_SOURCES += \ wayland/meta-wayland-keyboard.h \ wayland/meta-wayland-pointer.c \ wayland/meta-wayland-pointer.h \ + wayland/meta-wayland-pointer-constraints.c \ + wayland/meta-wayland-pointer-constraints.h \ + wayland/meta-pointer-lock-wayland.c \ + wayland/meta-pointer-lock-wayland.h \ + wayland/meta-pointer-confinement-wayland.c \ + wayland/meta-pointer-confinement-wayland.h \ wayland/meta-wayland-popup.c \ wayland/meta-wayland-popup.h \ wayland/meta-wayland-seat.c \ diff --git a/src/backends/meta-backend-private.h b/src/backends/meta-backend-private.h index 6fa468da6..73123ce53 100644 --- a/src/backends/meta-backend-private.h +++ b/src/backends/meta-backend-private.h @@ -34,6 +34,7 @@ #include #include "meta-cursor-renderer.h" #include "meta-monitor-manager-private.h" +#include "backends/meta-pointer-constraint.h" #define DEFAULT_XKB_RULES_FILE "evdev" #define DEFAULT_XKB_MODEL "pc105+inet" @@ -51,6 +52,8 @@ struct _MetaBackend GHashTable *device_monitors; gint current_device_id; + + MetaPointerConstraint *client_pointer_constraint; }; struct _MetaBackendClass @@ -124,4 +127,7 @@ gboolean meta_backend_get_relative_motion_deltas (MetaBackend *backend, double *dx_unaccel, double *dy_unaccel); +void meta_backend_set_client_pointer_constraint (MetaBackend *backend, + MetaPointerConstraint *constraint); + #endif /* META_BACKEND_PRIVATE_H */ diff --git a/src/backends/meta-backend.c b/src/backends/meta-backend.c index 05d160d96..e4ef6e3cc 100644 --- a/src/backends/meta-backend.c +++ b/src/backends/meta-backend.c @@ -571,6 +571,17 @@ meta_backend_get_relative_motion_deltas (MetaBackend *backend, dx_unaccel, dy_unaccel); } +void +meta_backend_set_client_pointer_constraint (MetaBackend *backend, + MetaPointerConstraint *constraint) +{ + g_assert (!constraint || (constraint && !backend->client_pointer_constraint)); + + g_clear_object (&backend->client_pointer_constraint); + if (constraint) + backend->client_pointer_constraint = g_object_ref (constraint); +} + static GType get_backend_type (void) { diff --git a/src/backends/meta-pointer-constraint.c b/src/backends/meta-pointer-constraint.c new file mode 100644 index 000000000..40ee3d32f --- /dev/null +++ b/src/backends/meta-pointer-constraint.c @@ -0,0 +1,57 @@ +/* -*- 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 + */ + +#include "config.h" + +#include "backends/meta-pointer-constraint.h" + +#include + +G_DEFINE_TYPE (MetaPointerConstraint, meta_pointer_constraint, G_TYPE_OBJECT); + +static void +meta_pointer_constraint_init (MetaPointerConstraint *constraint) +{ +} + +static void +meta_pointer_constraint_class_init (MetaPointerConstraintClass *klass) +{ +} + +void +meta_pointer_constraint_constrain (MetaPointerConstraint *constraint, + ClutterInputDevice *device, + guint32 time, + float prev_x, + float prev_y, + float *x, + float *y) +{ + META_POINTER_CONSTRAINT_GET_CLASS (constraint)->constrain (constraint, + device, + time, + prev_x, prev_y, + x, y); +} diff --git a/src/backends/meta-pointer-constraint.h b/src/backends/meta-pointer-constraint.h new file mode 100644 index 000000000..fd60816a6 --- /dev/null +++ b/src/backends/meta-pointer-constraint.h @@ -0,0 +1,60 @@ +/* -*- 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_POINTER_CONSTRAINT_H +#define META_POINTER_CONSTRAINT_H + +#include +#include + +G_BEGIN_DECLS + +#define META_TYPE_POINTER_CONSTRAINT (meta_pointer_constraint_get_type ()) +G_DECLARE_DERIVABLE_TYPE (MetaPointerConstraint, meta_pointer_constraint, + META, POINTER_CONSTRAINT, GObject); + +struct _MetaPointerConstraintClass +{ + GObjectClass parent_class; + + void (*constrain) (MetaPointerConstraint *constraint, + ClutterInputDevice *device, + guint32 time, + float prev_x, + float prev_y, + float *x, + float *y); +}; + +void meta_pointer_constraint_constrain (MetaPointerConstraint *constraint, + ClutterInputDevice *device, + guint32 time, + float prev_x, + float prev_y, + float *x, + float *y); + +G_END_DECLS + +#endif /* META_POINTER_CONSTRAINT_H */ diff --git a/src/backends/native/meta-backend-native.c b/src/backends/native/meta-backend-native.c index 16d0bac34..768b96b8a 100644 --- a/src/backends/native/meta-backend-native.c +++ b/src/backends/native/meta-backend-native.c @@ -36,6 +36,7 @@ #include "meta-monitor-manager-kms.h" #include "meta-cursor-renderer-native.h" #include "meta-launcher.h" +#include "backends/meta-pointer-constraint.h" #include @@ -139,6 +140,24 @@ constrain_to_barriers (ClutterInputDevice *device, new_x, new_y); } +static void +constrain_to_client_constraint (ClutterInputDevice *device, + guint32 time, + float prev_x, + float prev_y, + float *x, + float *y) +{ + MetaBackend *backend = meta_get_backend (); + MetaPointerConstraint *constraint = backend->client_pointer_constraint; + + if (!constraint) + return; + + meta_pointer_constraint_constrain (constraint, device, + time, prev_x, prev_y, x, y); +} + /* * The pointer constrain code is mostly a rip-off of the XRandR code from Xorg. * (from xserver/randr/rrcrtc.c, RRConstrainCursorHarder) @@ -207,6 +226,9 @@ pointer_constrain_callback (ClutterInputDevice *device, /* Constrain to barriers */ constrain_to_barriers (device, time, new_x, new_y); + /* Constrain to pointer lock */ + constrain_to_client_constraint (device, time, prev_x, prev_y, new_x, new_y); + monitor_manager = meta_monitor_manager_get (); monitors = meta_monitor_manager_get_monitor_infos (monitor_manager, &n_monitors); diff --git a/src/wayland/meta-pointer-confinement-wayland.c b/src/wayland/meta-pointer-confinement-wayland.c new file mode 100644 index 000000000..ec7433d2b --- /dev/null +++ b/src/wayland/meta-pointer-confinement-wayland.c @@ -0,0 +1,200 @@ +/* -*- 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 + */ + +#include "config.h" + +#include "wayland/meta-pointer-confinement-wayland.h" + +#include +#include + +#include "backends/meta-backend-private.h" +#include "wayland/meta-wayland-seat.h" +#include "wayland/meta-wayland-pointer.h" +#include "wayland/meta-wayland-pointer-constraints.h" +#include "wayland/meta-wayland-surface.h" +#include "backends/meta-pointer-constraint.h" +#include "compositor/meta-surface-actor-wayland.h" + +struct _MetaPointerConfinementWayland +{ + MetaPointerConstraint parent; + + MetaWaylandPointerConstraint *constraint; +}; + +G_DEFINE_TYPE (MetaPointerConfinementWayland, meta_pointer_confinement_wayland, + META_TYPE_POINTER_CONSTRAINT); + +static void +meta_pointer_confinement_wayland_constrain (MetaPointerConstraint *constraint, + ClutterInputDevice *device, + guint32 time, + float prev_x, + float prev_y, + float *x, + float *y) +{ + MetaPointerConfinementWayland *self = + META_POINTER_CONFINEMENT_WAYLAND (constraint); + MetaWaylandSurface *surface; + cairo_region_t *region; + cairo_rectangle_int_t extents; + float sx, sy; + float min_sx, max_sx; + float min_sy, max_sy; + + region = + meta_wayland_pointer_constraint_calculate_effective_region (self->constraint); + cairo_region_get_extents (region, &extents); + cairo_region_destroy (region); + + min_sx = extents.x; + max_sx = extents.x + extents.width - 1; + max_sy = extents.y + extents.height - 1; + min_sy = extents.y; + + surface = meta_wayland_pointer_constraint_get_surface (self->constraint); + meta_wayland_surface_get_relative_coordinates (surface, *x, *y, &sx, &sy); + + if (sx < min_sx) + sx = min_sx; + else if (sx > max_sx) + sx = max_sx; + + if (sy < min_sy) + sy = min_sy; + else if (sy > max_sy) + sy = max_sy; + + meta_wayland_surface_get_absolute_coordinates (surface, sx, sy, x, y); +} + +static void +meta_pointer_confinement_wayland_maybe_warp (MetaPointerConfinementWayland *self) +{ + MetaWaylandSeat *seat; + MetaWaylandSurface *surface; + wl_fixed_t sx; + wl_fixed_t sy; + cairo_region_t *region; + + seat = meta_wayland_pointer_constraint_get_seat (self->constraint); + surface = meta_wayland_pointer_constraint_get_surface (self->constraint); + meta_wayland_pointer_get_relative_coordinates (&seat->pointer, + surface, + &sx, &sy); + + region = + meta_wayland_pointer_constraint_calculate_effective_region (self->constraint); + + if (!cairo_region_contains_point (region, + wl_fixed_to_int (sx), + wl_fixed_to_int (sy))) + { + cairo_rectangle_int_t extents; + wl_fixed_t min_sx; + wl_fixed_t max_sx; + wl_fixed_t max_sy; + wl_fixed_t min_sy; + gboolean x_changed = TRUE, y_changed = TRUE; + + cairo_region_get_extents (region, &extents); + + min_sx = wl_fixed_from_int (extents.x); + max_sx = wl_fixed_from_int (extents.x + extents.width - 1); + max_sy = wl_fixed_from_int (extents.y + extents.height - 1); + min_sy = wl_fixed_from_int (extents.y); + + if (sx < min_sx) + sx = min_sx; + else if (sx > max_sx) + sx = max_sx; + else + x_changed = FALSE; + + if (sy < min_sy) + sy = min_sy; + else if (sy > max_sy) + sy = max_sy; + else + y_changed = FALSE; + + if (x_changed || y_changed) + { + float x, y; + + meta_wayland_surface_get_absolute_coordinates (surface, + wl_fixed_to_double (sx), + wl_fixed_to_double (sy), + &x, &y); + meta_backend_warp_pointer (meta_get_backend (), (int)x, (int)y); + } + } + + cairo_region_destroy (region); +} + +static void +surface_actor_painting (MetaSurfaceActorWayland *surface_actor, + MetaPointerConfinementWayland *self) +{ + meta_pointer_confinement_wayland_maybe_warp (self); +} + +MetaPointerConstraint * +meta_pointer_confinement_wayland_new (MetaWaylandPointerConstraint *constraint) +{ + GObject *object; + MetaPointerConfinementWayland *confinement; + MetaWaylandSurface *surface; + + object = g_object_new (META_TYPE_POINTER_CONFINEMENT_WAYLAND, NULL); + confinement = META_POINTER_CONFINEMENT_WAYLAND (object); + + confinement->constraint = constraint; + + surface = meta_wayland_pointer_constraint_get_surface (constraint); + g_signal_connect_object (surface->surface_actor, + "painting", + G_CALLBACK (surface_actor_painting), + confinement, + 0); + + return META_POINTER_CONSTRAINT (confinement); +} + +static void +meta_pointer_confinement_wayland_init (MetaPointerConfinementWayland *confinement_wayland) +{ +} + +static void +meta_pointer_confinement_wayland_class_init (MetaPointerConfinementWaylandClass *klass) +{ + MetaPointerConstraintClass *pointer_constraint_class = + META_POINTER_CONSTRAINT_CLASS (klass); + + pointer_constraint_class->constrain = meta_pointer_confinement_wayland_constrain; +} diff --git a/src/wayland/meta-pointer-confinement-wayland.h b/src/wayland/meta-pointer-confinement-wayland.h new file mode 100644 index 000000000..482a25a9a --- /dev/null +++ b/src/wayland/meta-pointer-confinement-wayland.h @@ -0,0 +1,45 @@ +/* -*- 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_POINTER_CONFINEMENT_WAYLAND_H +#define META_POINTER_CONFINEMENT_WAYLAND_H + +#include + +#include "backends/meta-pointer-constraint.h" +#include "wayland/meta-wayland-pointer-constraints.h" + +G_BEGIN_DECLS + +#define META_TYPE_POINTER_CONFINEMENT_WAYLAND (meta_pointer_confinement_wayland_get_type ()) +G_DECLARE_FINAL_TYPE (MetaPointerConfinementWayland, + meta_pointer_confinement_wayland, + META, POINTER_CONFINEMENT_WAYLAND, + MetaPointerConstraint); + +MetaPointerConstraint *meta_pointer_confinement_wayland_new (MetaWaylandPointerConstraint *constraint); + +G_END_DECLS + +#endif /* META_CONFINEMENT_WAYLAND_H */ diff --git a/src/wayland/meta-pointer-lock-wayland.c b/src/wayland/meta-pointer-lock-wayland.c new file mode 100644 index 000000000..a9098d529 --- /dev/null +++ b/src/wayland/meta-pointer-lock-wayland.c @@ -0,0 +1,72 @@ +/* -*- 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 + */ + +#include "config.h" + +#include "wayland/meta-pointer-lock-wayland.h" + +#include + +#include "backends/meta-pointer-constraint.h" + +struct _MetaPointerLockWayland +{ + MetaPointerConstraint parent; +}; + +G_DEFINE_TYPE (MetaPointerLockWayland, meta_pointer_lock_wayland, + META_TYPE_POINTER_CONSTRAINT); + +static void +meta_pointer_lock_wayland_constrain (MetaPointerConstraint *constraint, + ClutterInputDevice *device, + guint32 time, + float prev_x, + float prev_y, + float *x, + float *y) +{ + *x = prev_x; + *y = prev_y; +} + +MetaPointerConstraint * +meta_pointer_lock_wayland_new (void) +{ + return g_object_new (META_TYPE_POINTER_LOCK_WAYLAND, NULL); +} + +static void +meta_pointer_lock_wayland_init (MetaPointerLockWayland *lock_wayland) +{ +} + +static void +meta_pointer_lock_wayland_class_init (MetaPointerLockWaylandClass *klass) +{ + MetaPointerConstraintClass *pointer_constraint_class = + META_POINTER_CONSTRAINT_CLASS (klass); + + pointer_constraint_class->constrain = meta_pointer_lock_wayland_constrain; +} diff --git a/src/wayland/meta-pointer-lock-wayland.h b/src/wayland/meta-pointer-lock-wayland.h new file mode 100644 index 000000000..787b43269 --- /dev/null +++ b/src/wayland/meta-pointer-lock-wayland.h @@ -0,0 +1,42 @@ +/* -*- 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_POINTER_LOCK_WAYLAND_H +#define META_POINTER_LOCK_WAYLAND_H + +#include + +#include "backends/meta-pointer-constraint.h" + +G_BEGIN_DECLS + +#define META_TYPE_POINTER_LOCK_WAYLAND (meta_pointer_lock_wayland_get_type ()) +G_DECLARE_FINAL_TYPE (MetaPointerLockWayland, meta_pointer_lock_wayland, + META, POINTER_LOCK_WAYLAND, MetaPointerConstraint); + +MetaPointerConstraint *meta_pointer_lock_wayland_new (void); + +G_END_DECLS + +#endif /* META_LOCK_WAYLAND_H */ diff --git a/src/wayland/meta-wayland-pointer-constraints.c b/src/wayland/meta-wayland-pointer-constraints.c new file mode 100644 index 000000000..49fbb0c36 --- /dev/null +++ b/src/wayland/meta-wayland-pointer-constraints.c @@ -0,0 +1,818 @@ +/* -*- 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 + */ + +#include "config.h" + +#include "meta-wayland-pointer-constraints.h" + +#include + +#include "meta/meta-backend.h" +#include "meta-wayland-private.h" +#include "meta-wayland-seat.h" +#include "meta-wayland-pointer.h" +#include "meta-wayland-surface.h" +#include "meta-wayland-region.h" +#include "meta-pointer-lock-wayland.h" +#include "meta-pointer-confinement-wayland.h" +#include "window-private.h" +#include "backends/meta-backend-private.h" +#include "backends/native/meta-backend-native.h" +#include "backends/meta-pointer-constraint.h" + +#include "pointer-constraints-unstable-v1-server-protocol.h" + +static GQuark quark_pending_constraint_state = 0; + +struct _MetaWaylandPointerConstraint +{ + GObject parent; + + MetaWaylandSurface *surface; + gboolean is_enabled; + cairo_region_t *region; + struct wl_resource *resource; + MetaWaylandPointerGrab grab; + MetaWaylandSeat *seat; + enum zwp_pointer_constraints_v1_lifetime lifetime; + + gboolean hint_set; + wl_fixed_t x_hint; + wl_fixed_t y_hint; + + MetaPointerConstraint *constraint; +}; + +typedef struct +{ + MetaWaylandPointerConstraint *constraint; + cairo_region_t *region; + gulong applied_handler_id; +} MetaWaylandPendingConstraintState; + +typedef struct +{ + GList *pending_constraint_states; +} MetaWaylandPendingConstraintStateContainer; + +G_DEFINE_TYPE (MetaWaylandPointerConstraint, meta_wayland_pointer_constraint, + G_TYPE_OBJECT); + +static const struct zwp_locked_pointer_v1_interface locked_pointer_interface; +static const struct zwp_confined_pointer_v1_interface confined_pointer_interface; +static const MetaWaylandPointerGrabInterface locked_pointer_grab_interface; +static const MetaWaylandPointerGrabInterface confined_pointer_grab_interface; + +static cairo_region_t * +create_infinite_constraint_region (void) +{ + return cairo_region_create_rectangle (&(cairo_rectangle_int_t) { + .x = INT_MIN / 2, + .y = INT_MIN / 2, + .width = INT_MAX, + .height = INT_MAX, + }); +} + +static MetaWaylandPointerConstraint * +meta_wayland_pointer_constraint_new (MetaWaylandSurface *surface, + MetaWaylandSeat *seat, + MetaWaylandRegion *region, + enum zwp_pointer_constraints_v1_lifetime lifetime, + struct wl_resource *resource, + const MetaWaylandPointerGrabInterface *grab_interface) +{ + MetaWaylandPointerConstraint *constraint; + + constraint = g_object_new (META_TYPE_WAYLAND_POINTER_CONSTRAINT, NULL); + if (!constraint) + return NULL; + + constraint->surface = surface; + constraint->seat = seat; + constraint->lifetime = lifetime; + constraint->resource = resource; + constraint->grab.interface = grab_interface; + + if (region) + { + constraint->region = + cairo_region_copy (meta_wayland_region_peek_cairo_region (region)); + } + else + { + constraint->region = create_infinite_constraint_region (); + } + + return constraint; +} + +static gboolean +meta_wayland_pointer_constraint_is_enabled (MetaWaylandPointerConstraint *constraint) +{ + return constraint->is_enabled; +} + +static void +meta_wayland_pointer_constraint_notify_activated (MetaWaylandPointerConstraint *constraint) +{ + struct wl_resource *resource = constraint->resource; + + if (wl_resource_instance_of (resource, + &zwp_locked_pointer_v1_interface, + &locked_pointer_interface)) + { + zwp_locked_pointer_v1_send_locked (resource); + } + else if (wl_resource_instance_of (resource, + &zwp_confined_pointer_v1_interface, + &confined_pointer_interface)) + { + zwp_confined_pointer_v1_send_confined (resource); + } +} + +static void +meta_wayland_pointer_constraint_notify_deactivated (MetaWaylandPointerConstraint *constraint) +{ + struct wl_resource *resource = constraint->resource; + + if (wl_resource_instance_of (resource, + &zwp_locked_pointer_v1_interface, + &locked_pointer_interface)) + zwp_locked_pointer_v1_send_unlocked (resource); + else if (wl_resource_instance_of (resource, + &zwp_confined_pointer_v1_interface, + &confined_pointer_interface)) + zwp_confined_pointer_v1_send_unconfined (resource); +} + +static MetaPointerConstraint * +meta_wayland_pointer_constraint_create_pointer_constraint (MetaWaylandPointerConstraint *constraint) +{ + struct wl_resource *resource = constraint->resource; + + if (wl_resource_instance_of (resource, + &zwp_locked_pointer_v1_interface, + &locked_pointer_interface)) + { + return meta_pointer_lock_wayland_new (); + } + else if (wl_resource_instance_of (resource, + &zwp_confined_pointer_v1_interface, + &confined_pointer_interface)) + { + return meta_pointer_confinement_wayland_new (constraint); + } + g_assert_not_reached (); + return NULL; +} + +static void +meta_wayland_pointer_constraint_enable (MetaWaylandPointerConstraint *constraint) +{ + MetaBackend *backend = meta_get_backend (); + + g_assert (!constraint->is_enabled); + + constraint->is_enabled = TRUE; + meta_wayland_pointer_constraint_notify_activated (constraint); + meta_wayland_pointer_start_grab (&constraint->seat->pointer, + &constraint->grab); + + constraint->constraint = + meta_wayland_pointer_constraint_create_pointer_constraint (constraint); + meta_backend_set_client_pointer_constraint (backend, constraint->constraint); + g_object_add_weak_pointer (G_OBJECT (constraint->constraint), + (gpointer *) &constraint->constraint); + g_object_unref (constraint->constraint); +} + +static void +meta_wayland_pointer_constraint_disable (MetaWaylandPointerConstraint *constraint) +{ + meta_wayland_pointer_constraint_notify_deactivated (constraint); + meta_wayland_pointer_end_grab (constraint->grab.pointer); + meta_backend_set_client_pointer_constraint (meta_get_backend (), NULL); +} + +void +meta_wayland_pointer_constraint_destroy (MetaWaylandPointerConstraint *constraint) +{ + if (meta_wayland_pointer_constraint_is_enabled (constraint)) + meta_wayland_pointer_constraint_disable (constraint); + + wl_resource_set_user_data (constraint->resource, NULL); + cairo_region_destroy (constraint->region); + g_object_unref (constraint); +} + +static gboolean +is_within_constraint_region (MetaWaylandPointerConstraint *constraint, + wl_fixed_t sx, + wl_fixed_t sy) +{ + cairo_region_t *region; + gboolean is_within; + + region = meta_wayland_pointer_constraint_calculate_effective_region (constraint); + is_within = cairo_region_contains_point (constraint->region, + wl_fixed_to_int (sx), + wl_fixed_to_int (sy)); + cairo_region_destroy (region); + + return is_within; +} + +void +meta_wayland_pointer_constraint_maybe_enable (MetaWaylandPointerConstraint *constraint) +{ + MetaWaylandSeat *seat = constraint->seat; + wl_fixed_t sx, sy; + + if (constraint->is_enabled) + return; + + if (seat->keyboard.focus_surface != constraint->surface) + return; + + meta_wayland_pointer_get_relative_coordinates (&constraint->seat->pointer, + constraint->surface, + &sx, &sy); + if (!is_within_constraint_region (constraint, sx, sy)) + return; + + meta_wayland_pointer_constraint_enable (constraint); +} + +static void +meta_wayland_pointer_constraint_remove (MetaWaylandPointerConstraint *constraint) +{ + MetaWaylandSurface *surface = constraint->surface; + + meta_wayland_surface_remove_pointer_constraint (surface, constraint); + meta_wayland_pointer_constraint_destroy (constraint); +} + +void +meta_wayland_pointer_constraint_maybe_remove_for_seat (MetaWaylandSeat *seat, + MetaWindow *focus_window) +{ + MetaWaylandPointer *pointer = &seat->pointer; + + if ((pointer->grab->interface == &confined_pointer_grab_interface || + pointer->grab->interface == &locked_pointer_grab_interface) && + pointer->focus_surface && + pointer->focus_surface->window != focus_window) + { + MetaWaylandPointerConstraint *constraint = + wl_container_of (pointer->grab, constraint, grab); + + switch (constraint->lifetime) + { + case ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT: + meta_wayland_pointer_constraint_remove (constraint); + break; + + case ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT: + meta_wayland_pointer_constraint_disable (constraint); + break; + + default: + g_assert_not_reached (); + } + } +} + +void +meta_wayland_pointer_constraint_maybe_enable_for_window (MetaWindow *window) +{ + MetaWaylandSurface *surface = window->surface; + GList *it; + + for (it = surface->pointer_constraints; it; it = it->next) + { + MetaWaylandPointerConstraint *constraint = it->data; + + meta_wayland_pointer_constraint_maybe_enable (constraint); + } +} + +MetaWaylandSeat * +meta_wayland_pointer_constraint_get_seat (MetaWaylandPointerConstraint *constraint) +{ + return constraint->seat; +} + +cairo_region_t * +meta_wayland_pointer_constraint_calculate_effective_region (MetaWaylandPointerConstraint *constraint) +{ + cairo_region_t *region; + + region = cairo_region_copy (constraint->surface->input_region); + cairo_region_intersect (region, constraint->region); + + return region; +} + +cairo_region_t * +meta_wayland_pointer_constraint_get_region (MetaWaylandPointerConstraint *constraint) +{ + return constraint->region; +} + +MetaWaylandSurface * +meta_wayland_pointer_constraint_get_surface (MetaWaylandPointerConstraint *constraint) +{ + return constraint->surface; +} + +static void +pointer_constraint_resource_destroyed (struct wl_resource *resource) +{ + MetaWaylandPointerConstraint *constraint = + wl_resource_get_user_data (resource); + + if (!constraint) + return; + + meta_wayland_pointer_constraint_remove (constraint); +} + +static void +pending_constraint_state_free (MetaWaylandPendingConstraintState *constraint_pending) +{ + g_clear_pointer (&constraint_pending->region, cairo_region_destroy); + if (constraint_pending->constraint) + g_object_remove_weak_pointer (G_OBJECT (constraint_pending->constraint), + (gpointer *) &constraint_pending->constraint); +} + +static MetaWaylandPendingConstraintStateContainer * +get_pending_constraint_state_container (MetaWaylandPendingState *pending) +{ + return g_object_get_qdata (G_OBJECT (pending), + quark_pending_constraint_state); +} + +static MetaWaylandPendingConstraintState * +get_pending_constraint_state (MetaWaylandPointerConstraint *constraint) +{ + MetaWaylandPendingState *pending = constraint->surface->pending; + MetaWaylandPendingConstraintStateContainer *container; + GList *l; + + container = get_pending_constraint_state_container (pending); + for (l = container->pending_constraint_states; l; l = l->next) + { + MetaWaylandPendingConstraintState *constraint_pending = l->data; + + if (constraint_pending->constraint == constraint) + return constraint_pending; + } + + return NULL; +} + +static void +pending_constraint_state_container_free (MetaWaylandPendingConstraintStateContainer *container) +{ + g_list_free_full (container->pending_constraint_states, + (GDestroyNotify) pending_constraint_state_free); + g_free (container); +} + +static MetaWaylandPendingConstraintStateContainer * +ensure_pending_constraint_state_container (MetaWaylandPendingState *pending) +{ + MetaWaylandPendingConstraintStateContainer *container; + + container = get_pending_constraint_state_container (pending); + if (!container) + { + container = g_new0 (MetaWaylandPendingConstraintStateContainer, 1); + g_object_set_qdata_full (G_OBJECT (pending), + quark_pending_constraint_state, + container, + (GDestroyNotify) pending_constraint_state_container_free); + + } + + return container; +} + +static void +remove_pending_constraint_state (MetaWaylandPointerConstraint *constraint, + MetaWaylandPendingState *pending) +{ + MetaWaylandPendingConstraintStateContainer *container; + GList *l; + + container = get_pending_constraint_state_container (pending); + for (l = container->pending_constraint_states; l; l = l->next) + { + MetaWaylandPendingConstraintState *constraint_pending = l->data; + if (constraint_pending->constraint != constraint) + continue; + + pending_constraint_state_free (l->data); + container->pending_constraint_states = + g_list_remove_link (container->pending_constraint_states, l); + break; + } +} + +static void +pending_constraint_state_applied (MetaWaylandPendingState *pending, + MetaWaylandPendingConstraintState *constraint_pending) +{ + MetaWaylandPointerConstraint *constraint = constraint_pending->constraint; + + if (!constraint) + return; + + g_clear_pointer (&constraint->region, cairo_region_destroy); + if (constraint_pending->region) + { + constraint->region = constraint_pending->region; + constraint_pending->region = NULL; + } + else + { + constraint->region = create_infinite_constraint_region (); + } + + g_signal_handler_disconnect (pending, + constraint_pending->applied_handler_id); + remove_pending_constraint_state (constraint, pending); + + /* The pointer is potentially warped by the actor paint signal callback if + * the new region proved it necessary. + */ +} + +static MetaWaylandPendingConstraintState * +ensure_pending_constraint_state (MetaWaylandPointerConstraint *constraint) +{ + MetaWaylandPendingState *pending = constraint->surface->pending; + MetaWaylandPendingConstraintStateContainer *container; + MetaWaylandPendingConstraintState *constraint_pending; + + container = ensure_pending_constraint_state_container (pending); + constraint_pending = get_pending_constraint_state (constraint); + if (!constraint_pending) + { + constraint_pending = g_new0 (MetaWaylandPendingConstraintState, 1); + constraint_pending->constraint = constraint; + constraint_pending->applied_handler_id = + g_signal_connect (pending, "applied", + G_CALLBACK (pending_constraint_state_applied), + constraint_pending); + g_object_add_weak_pointer (G_OBJECT (constraint), + (gpointer *) &constraint_pending->constraint); + + container->pending_constraint_states = + g_list_append (container->pending_constraint_states, + constraint_pending); + } + + return constraint_pending; +} + +static void +meta_wayland_pointer_constraint_set_pending_region (MetaWaylandPointerConstraint *constraint, + MetaWaylandRegion *region) +{ + MetaWaylandPendingConstraintState *constraint_pending; + + constraint_pending = ensure_pending_constraint_state (constraint); + + g_clear_pointer (&constraint_pending->region, cairo_region_destroy); + if (region) + { + constraint_pending->region = + cairo_region_copy (meta_wayland_region_peek_cairo_region (region)); + } +} + +static void +init_pointer_constraint (struct wl_resource *resource, + uint32_t id, + MetaWaylandSurface *surface, + MetaWaylandSeat *seat, + MetaWaylandRegion *region, + enum zwp_pointer_constraints_v1_lifetime lifetime, + const struct wl_interface *interface, + const void *implementation, + const MetaWaylandPointerGrabInterface *grab_interface) +{ + struct wl_client *client = wl_resource_get_client (resource); + struct wl_resource *cr; + MetaWaylandPointerConstraint *constraint; + + if (meta_wayland_surface_get_pointer_constraint_for_seat (surface, seat)) + { + wl_resource_post_error (resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "the pointer as already requested to be " + "locked or confined on that surface"); + return; + } + + cr = wl_resource_create (client, interface, + wl_resource_get_version (resource), + id); + if (cr == NULL) + { + wl_client_post_no_memory (client); + return; + } + + constraint = meta_wayland_pointer_constraint_new (surface, seat, + region, + lifetime, + cr, grab_interface); + if (constraint == NULL) + { + wl_client_post_no_memory (client); + return; + } + + meta_wayland_surface_add_pointer_constraint (surface, constraint); + + wl_resource_set_implementation (cr, implementation, constraint, + pointer_constraint_resource_destroyed); + + meta_wayland_pointer_constraint_maybe_enable (constraint); +} + +static void +locked_pointer_destroy (struct wl_client *client, + struct wl_resource *resource) +{ + MetaWaylandPointerConstraint *constraint = + wl_resource_get_user_data (resource); + gboolean warp_pointer = FALSE; + int warp_x, warp_y; + + if (constraint && constraint->is_enabled && constraint->hint_set && + is_within_constraint_region (constraint, + constraint->x_hint, + constraint->y_hint)) + { + float sx, sy; + float x, y; + + sx = (float)wl_fixed_to_double (constraint->x_hint); + sy = (float)wl_fixed_to_double (constraint->y_hint); + meta_wayland_surface_get_absolute_coordinates (constraint->surface, + sx, sy, + &x, &y); + warp_pointer = TRUE; + warp_x = (int) x; + warp_y = (int) y; + } + wl_resource_destroy (resource); + + if (warp_pointer) + meta_backend_warp_pointer (meta_get_backend (), warp_x, warp_y); +} + +static void +locked_pointer_set_cursor_position_hint (struct wl_client *client, + struct wl_resource *resource, + wl_fixed_t surface_x, + wl_fixed_t surface_y) +{ + MetaWaylandPointerConstraint *constraint = + wl_resource_get_user_data (resource); + + /* Ignore a set cursor hint that was already sent after the constraint + * was cancelled. */ + if (!constraint->resource || constraint->resource != resource) + return; + + constraint->hint_set = TRUE; + constraint->x_hint = surface_x; + constraint->y_hint = surface_y; +} + +static void +locked_pointer_set_region (struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *region_resource) +{ + MetaWaylandPointerConstraint *constraint = + wl_resource_get_user_data (resource); + MetaWaylandRegion *region = + region_resource ? wl_resource_get_user_data (region_resource) : NULL; + + meta_wayland_pointer_constraint_set_pending_region (constraint, region); +} + +static const struct zwp_locked_pointer_v1_interface locked_pointer_interface = { + locked_pointer_destroy, + locked_pointer_set_cursor_position_hint, + locked_pointer_set_region, +}; + +static void +locked_pointer_grab_pointer_focus (MetaWaylandPointerGrab *grab, + MetaWaylandSurface *surface) +{ +} + +static void +locked_pointer_grab_pointer_motion (MetaWaylandPointerGrab *grab, + const ClutterEvent *event) +{ + meta_wayland_pointer_send_relative_motion (grab->pointer, event); +} + +static void +locked_pointer_grab_pointer_button (MetaWaylandPointerGrab *grab, + const ClutterEvent *event) +{ + meta_wayland_pointer_send_button (grab->pointer, event); +} + +static const MetaWaylandPointerGrabInterface locked_pointer_grab_interface = { + locked_pointer_grab_pointer_focus, + locked_pointer_grab_pointer_motion, + locked_pointer_grab_pointer_button, +}; + +static void +pointer_constraints_destroy (struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy (resource); +} + +static void +pointer_constraints_lock_pointer (struct wl_client *client, + struct wl_resource *resource, + uint32_t id, + struct wl_resource *surface_resource, + struct wl_resource *pointer_resource, + struct wl_resource *region_resource, + uint32_t lifetime) +{ + MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource); + MetaWaylandPointer *pointer = wl_resource_get_user_data (pointer_resource); + MetaWaylandSeat *seat = meta_wayland_pointer_get_seat (pointer); + MetaWaylandRegion *region = + region_resource ? wl_resource_get_user_data (region_resource) : NULL; + + init_pointer_constraint (resource, id, surface, seat, region, lifetime, + &zwp_locked_pointer_v1_interface, + &locked_pointer_interface, + &locked_pointer_grab_interface); +} + +static void +confined_pointer_grab_pointer_focus (MetaWaylandPointerGrab *grab, + MetaWaylandSurface *surface) +{ +} + +static void +confined_pointer_grab_pointer_motion (MetaWaylandPointerGrab *grab, + const ClutterEvent *event) +{ + MetaWaylandPointerConstraint *constraint = + wl_container_of (grab, constraint, grab); + MetaWaylandPointer *pointer = grab->pointer; + + g_assert (pointer->focus_surface); + g_assert (pointer->focus_surface == constraint->surface); + + meta_wayland_pointer_send_motion (pointer, event); +} + +static void +confined_pointer_grab_pointer_button (MetaWaylandPointerGrab *grab, + const ClutterEvent *event) +{ + meta_wayland_pointer_send_button (grab->pointer, event); +} + +static const MetaWaylandPointerGrabInterface confined_pointer_grab_interface = { + confined_pointer_grab_pointer_focus, + confined_pointer_grab_pointer_motion, + confined_pointer_grab_pointer_button, +}; + +static void +confined_pointer_destroy (struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy (resource); +} + +static void +confined_pointer_set_region (struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *region_resource) +{ + MetaWaylandPointerConstraint *constraint = + wl_resource_get_user_data (resource); + MetaWaylandRegion *region = + region_resource ? wl_resource_get_user_data (region_resource) : NULL; + + meta_wayland_pointer_constraint_set_pending_region (constraint, region); +} + +static const struct zwp_confined_pointer_v1_interface confined_pointer_interface = { + confined_pointer_destroy, + confined_pointer_set_region, +}; + +static void +pointer_constraints_confine_pointer (struct wl_client *client, + struct wl_resource *resource, + uint32_t id, + struct wl_resource *surface_resource, + struct wl_resource *pointer_resource, + struct wl_resource *region_resource, + uint32_t lifetime) +{ + MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource); + MetaWaylandPointer *pointer = wl_resource_get_user_data (pointer_resource); + MetaWaylandSeat *seat = meta_wayland_pointer_get_seat (pointer); + MetaWaylandRegion *region = + region_resource ? wl_resource_get_user_data (region_resource) : NULL; + + init_pointer_constraint (resource, id, surface, seat, region, lifetime, + &zwp_confined_pointer_v1_interface, + &confined_pointer_interface, + &confined_pointer_grab_interface); + +} + +static const struct zwp_pointer_constraints_v1_interface pointer_constraints = { + pointer_constraints_destroy, + pointer_constraints_lock_pointer, + pointer_constraints_confine_pointer, +}; + +static void +bind_pointer_constraints (struct wl_client *client, + void *data, + uint32_t version, + uint32_t id) +{ + MetaWaylandCompositor *compositor = data; + struct wl_resource *resource; + + resource = wl_resource_create (client, + &zwp_pointer_constraints_v1_interface, + 1, id); + + wl_resource_set_implementation (resource, + &pointer_constraints, + compositor, + NULL); +} + +void +meta_wayland_pointer_constraints_init (MetaWaylandCompositor *compositor) +{ + if (!wl_global_create (compositor->wayland_display, + &zwp_pointer_constraints_v1_interface, 1, + compositor, bind_pointer_constraints)) + g_error ("Could not create wp_pointer_constraints global"); +} + +static void +meta_wayland_pointer_constraint_init (MetaWaylandPointerConstraint *constraint) +{ +} + +static void +meta_wayland_pointer_constraint_class_init (MetaWaylandPointerConstraintClass *klass) +{ + quark_pending_constraint_state = + g_quark_from_static_string ("-meta-wayland-pointer-constraint-pending_state"); +} diff --git a/src/wayland/meta-wayland-pointer-constraints.h b/src/wayland/meta-wayland-pointer-constraints.h new file mode 100644 index 000000000..c467c830d --- /dev/null +++ b/src/wayland/meta-wayland-pointer-constraints.h @@ -0,0 +1,60 @@ +/* -*- 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_WAYLAND_POINTER_CONSTRAINTS_H +#define META_WAYLAND_POINTER_CONSTRAINTS_H + +#include "meta-wayland-types.h" +#include "meta/window.h" + +#include + +#define META_TYPE_WAYLAND_POINTER_CONSTRAINT (meta_wayland_pointer_constraint_get_type ()) +G_DECLARE_FINAL_TYPE (MetaWaylandPointerConstraint, + meta_wayland_pointer_constraint, + META, WAYLAND_POINTER_CONSTRAINT, + GObject); + +typedef struct _MetaWaylandPointerConstraint MetaWaylandPointerConstraint; + +void meta_wayland_pointer_constraints_init (MetaWaylandCompositor *compositor); + +void meta_wayland_pointer_constraint_maybe_enable (MetaWaylandPointerConstraint *constraint); + +void meta_wayland_pointer_constraint_destroy (MetaWaylandPointerConstraint *constraint); + +MetaWaylandSeat * meta_wayland_pointer_constraint_get_seat (MetaWaylandPointerConstraint *constraint); + +cairo_region_t * meta_wayland_pointer_constraint_calculate_effective_region (MetaWaylandPointerConstraint *constraint); + +cairo_region_t * meta_wayland_pointer_constraint_get_region (MetaWaylandPointerConstraint *constraint); + +MetaWaylandSurface * meta_wayland_pointer_constraint_get_surface (MetaWaylandPointerConstraint *constraint); + +void meta_wayland_pointer_constraint_maybe_remove_for_seat (MetaWaylandSeat *seat, + MetaWindow *focus_window); + +void meta_wayland_pointer_constraint_maybe_enable_for_window (MetaWindow *window); + +#endif /* META_WAYLAND_POINTER_CONSTRAINTS_H */ diff --git a/src/wayland/meta-wayland-pointer.c b/src/wayland/meta-wayland-pointer.c index 663e35f7d..09d0e986a 100644 --- a/src/wayland/meta-wayland-pointer.c +++ b/src/wayland/meta-wayland-pointer.c @@ -273,7 +273,7 @@ meta_wayland_pointer_broadcast_frame (MetaWaylandPointer *pointer) } } -static void +void meta_wayland_pointer_send_relative_motion (MetaWaylandPointer *pointer, const ClutterEvent *event) { @@ -1249,6 +1249,13 @@ meta_wayland_relative_pointer_init (MetaWaylandCompositor *compositor) g_error ("Could not create relative pointer manager global"); } +MetaWaylandSeat * +meta_wayland_pointer_get_seat (MetaWaylandPointer *pointer) +{ + MetaWaylandSeat *seat = wl_container_of (pointer, seat, pointer); + return seat; +} + static void cursor_surface_role_assigned (MetaWaylandSurfaceRole *surface_role) { diff --git a/src/wayland/meta-wayland-pointer.h b/src/wayland/meta-wayland-pointer.h index cfe1b460f..c016bc1ec 100644 --- a/src/wayland/meta-wayland-pointer.h +++ b/src/wayland/meta-wayland-pointer.h @@ -28,6 +28,7 @@ #include "meta-wayland-pointer-gesture-swipe.h" #include "meta-wayland-pointer-gesture-pinch.h" #include "meta-wayland-surface.h" +#include "meta-wayland-pointer-constraints.h" #include @@ -102,6 +103,9 @@ gboolean meta_wayland_pointer_handle_event (MetaWaylandPointer *pointer, void meta_wayland_pointer_send_motion (MetaWaylandPointer *pointer, const ClutterEvent *event); +void meta_wayland_pointer_send_relative_motion (MetaWaylandPointer *pointer, + const ClutterEvent *event); + void meta_wayland_pointer_send_button (MetaWaylandPointer *pointer, const ClutterEvent *event); @@ -145,4 +149,6 @@ void meta_wayland_pointer_unbind_pointer_client_resource (struct wl_resource *re void meta_wayland_relative_pointer_init (MetaWaylandCompositor *compositor); +MetaWaylandSeat *meta_wayland_pointer_get_seat (MetaWaylandPointer *pointer); + #endif /* META_WAYLAND_POINTER_H */ diff --git a/src/wayland/meta-wayland-surface.c b/src/wayland/meta-wayland-surface.c index 7fe7ab71c..129486c06 100644 --- a/src/wayland/meta-wayland-surface.c +++ b/src/wayland/meta-wayland-surface.c @@ -55,6 +55,14 @@ #include "meta-surface-actor-wayland.h" #include "meta-xwayland-private.h" +enum { + PENDING_STATE_SIGNAL_APPLIED, + + PENDING_STATE_SIGNAL_LAST_SIGNAL +}; + +static guint pending_state_signals[PENDING_STATE_SIGNAL_LAST_SIGNAL]; + typedef struct _MetaWaylandSurfaceRolePrivate { MetaWaylandSurface *surface; @@ -516,6 +524,14 @@ meta_wayland_pending_state_class_init (MetaWaylandPendingStateClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_wayland_pending_state_finalize; + + pending_state_signals[PENDING_STATE_SIGNAL_APPLIED] = + g_signal_new ("applied", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 0); } static void @@ -689,6 +705,10 @@ apply_pending_state (MetaWaylandSurface *surface, wl_list_init (&pending->frame_callback_list); } + g_signal_emit (pending, + pending_state_signals[PENDING_STATE_SIGNAL_APPLIED], + 0); + meta_surface_actor_wayland_sync_state ( META_SURFACE_ACTOR_WAYLAND (surface->surface_actor)); @@ -1072,6 +1092,9 @@ wl_surface_destructor (struct wl_resource *resource) if (surface->window) destroy_window (surface); + g_list_free_full (surface->pointer_constraints, + (GDestroyNotify) meta_wayland_pointer_constraint_destroy); + surface_set_buffer (surface, NULL); g_clear_object (&surface->pending); @@ -2468,6 +2491,39 @@ meta_wayland_surface_get_toplevel_window (MetaWaylandSurface *surface) return NULL; } +void +meta_wayland_surface_add_pointer_constraint (MetaWaylandSurface *surface, + MetaWaylandPointerConstraint *constraint) +{ + surface->pointer_constraints = g_list_append (surface->pointer_constraints, + constraint); +} + +void +meta_wayland_surface_remove_pointer_constraint (MetaWaylandSurface *surface, + MetaWaylandPointerConstraint *constraint) +{ + surface->pointer_constraints = g_list_remove (surface->pointer_constraints, + constraint); +} + +MetaWaylandPointerConstraint * +meta_wayland_surface_get_pointer_constraint_for_seat (MetaWaylandSurface *surface, + MetaWaylandSeat *seat) +{ + GList *iter; + + for (iter = surface->pointer_constraints; iter; iter = iter->next) + { + MetaWaylandPointerConstraint *constraint = iter->data; + + if (seat == meta_wayland_pointer_constraint_get_seat (constraint)) + return constraint; + } + + return NULL; +} + void meta_wayland_surface_get_relative_coordinates (MetaWaylandSurface *surface, float abs_x, @@ -2483,6 +2539,27 @@ meta_wayland_surface_get_relative_coordinates (MetaWaylandSurface *surface, *sy /= surface->scale; } +void +meta_wayland_surface_get_absolute_coordinates (MetaWaylandSurface *surface, + float sx, + float sy, + float *x, + float *y) +{ + ClutterActor *actor = + CLUTTER_ACTOR (meta_surface_actor_get_texture (surface->surface_actor)); + ClutterVertex sv = { + .x = sx * surface->scale, + .y = sy * surface->scale, + }; + ClutterVertex v = { 0 }; + + clutter_actor_apply_relative_transform_to_point (actor, NULL, &sv, &v); + + *x = v.x; + *y = v.y; +} + static void meta_wayland_surface_init (MetaWaylandSurface *surface) { diff --git a/src/wayland/meta-wayland-surface.h b/src/wayland/meta-wayland-surface.h index 3105eec27..7200bbfb7 100644 --- a/src/wayland/meta-wayland-surface.h +++ b/src/wayland/meta-wayland-surface.h @@ -31,6 +31,7 @@ #include "meta-wayland-types.h" #include "meta-surface-actor.h" #include "backends/meta-monitor-manager-private.h" +#include "meta-wayland-pointer-constraints.h" typedef struct _MetaWaylandPendingState MetaWaylandPendingState; @@ -217,6 +218,8 @@ struct _MetaWaylandSurface gboolean pending_pos; GSList *pending_placement_ops; } sub; + + GList *pointer_constraints; }; void meta_wayland_shell_init (MetaWaylandCompositor *compositor); @@ -261,12 +264,27 @@ void meta_wayland_surface_queue_pending_frame_callbacks (MetaWayl void meta_wayland_surface_queue_pending_state_frame_callbacks (MetaWaylandSurface *surface, MetaWaylandPendingState *pending); +void meta_wayland_surface_add_pointer_constraint (MetaWaylandSurface *surface, + MetaWaylandPointerConstraint *constraint); + +void meta_wayland_surface_remove_pointer_constraint (MetaWaylandSurface *surface, + MetaWaylandPointerConstraint *constraint); +MetaWaylandPointerConstraint * + meta_wayland_surface_get_pointer_constraint_for_seat (MetaWaylandSurface *surface, + MetaWaylandSeat *seat); + void meta_wayland_surface_get_relative_coordinates (MetaWaylandSurface *surface, float abs_x, float abs_y, float *sx, float *sy); +void meta_wayland_surface_get_absolute_coordinates (MetaWaylandSurface *surface, + float sx, + float sy, + float *x, + float *y); + MetaWaylandSurface * meta_wayland_surface_role_get_surface (MetaWaylandSurfaceRole *role); #endif diff --git a/src/wayland/meta-wayland.c b/src/wayland/meta-wayland.c index a878f323d..3199c9939 100644 --- a/src/wayland/meta-wayland.c +++ b/src/wayland/meta-wayland.c @@ -329,6 +329,7 @@ meta_wayland_init (void) meta_wayland_pointer_gestures_init (compositor); meta_wayland_seat_init (compositor); meta_wayland_relative_pointer_init (compositor); + meta_wayland_pointer_constraints_init (compositor); if (!meta_xwayland_start (&compositor->xwayland_manager, compositor->wayland_display)) g_error ("Failed to start X Wayland"); diff --git a/src/wayland/meta-window-wayland.c b/src/wayland/meta-window-wayland.c index 8129cb80c..46865ed6c 100644 --- a/src/wayland/meta-window-wayland.c +++ b/src/wayland/meta-window-wayland.c @@ -30,6 +30,7 @@ #include "window-private.h" #include "boxes-private.h" #include "stack-tracker.h" +#include "meta-wayland-private.h" #include "meta-wayland-surface.h" #include "compositor/meta-surface-actor-wayland.h" @@ -402,6 +403,11 @@ appears_focused_changed (GObject *object, gpointer user_data) { MetaWindow *window = META_WINDOW (object); + MetaWaylandCompositor *wayland_compositor; + + wayland_compositor = meta_wayland_compositor_get_default (); + meta_wayland_pointer_constraint_maybe_remove_for_seat (wayland_compositor->seat, + window); /* When we're unmanaging, we remove focus from the window, * causing this to fire. Don't do anything in that case. */ @@ -409,6 +415,8 @@ appears_focused_changed (GObject *object, return; surface_state_changed (window); + + meta_wayland_pointer_constraint_maybe_enable_for_window (window); } static void