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