summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNeil Roberts <neil@linux.intel.com>2013-05-03 18:51:22 +0100
committerRobert Bragg <robert@linux.intel.com>2013-08-08 15:26:20 +0100
commita98ff9ff6d9bdf2be5fe9a096febd9590d0e2a30 (patch)
treee296eed7fa718d4576ae6a0d021b31dcea53eb5d
parent16fa7fc99a80125e941dd9b68d9e662f0f11b1a9 (diff)
downloadmutter-a98ff9ff6d9bdf2be5fe9a096febd9590d0e2a30.tar.gz
wayland: Add basic input support
This copies the basic input support from the Clayland demo compositor. It adds a basic wl_seat implementation which can convert Clutter mouse events to Wayland events. For this to work all of the wayland surface actors need to be made reactive. The wayland keyboard input focus surface is updated whenever Mutter sees a FocusIn event so that it will stay in synch with whatever surface Mutter wants as the focus. Wayland surfaces don't get this event so for now it will just give them focus whenever they are clicked as a hack to test the code. Authored-by: Neil Roberts <neil@linux.intel.com> Authored-by: Giovanni Campagna <gcampagna@src.gnome.org>
-rw-r--r--src/Makefile.am10
-rw-r--r--src/compositor/meta-window-actor.c3
-rw-r--r--src/core/display-private.h3
-rw-r--r--src/core/display.c49
-rw-r--r--src/wayland/meta-wayland-data-device.c548
-rw-r--r--src/wayland/meta-wayland-data-device.h42
-rw-r--r--src/wayland/meta-wayland-keyboard.c512
-rw-r--r--src/wayland/meta-wayland-keyboard.h75
-rw-r--r--src/wayland/meta-wayland-pointer.c260
-rw-r--r--src/wayland/meta-wayland-pointer.h49
-rw-r--r--src/wayland/meta-wayland-private.h203
-rw-r--r--src/wayland/meta-wayland-seat.c503
-rw-r--r--src/wayland/meta-wayland-seat.h47
-rw-r--r--src/wayland/meta-wayland.c280
14 files changed, 2568 insertions, 16 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index e25d4a8ee..a323a207e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -179,7 +179,15 @@ libmutter_la_SOURCES += \
wayland/meta-wayland.c \
wayland/meta-wayland-private.h \
wayland/meta-xwayland-private.h \
- wayland/meta-xwayland.c
+ wayland/meta-xwayland.c \
+ wayland/meta-wayland-data-device.c \
+ wayland/meta-wayland-data-device.h \
+ wayland/meta-wayland-keyboard.c \
+ wayland/meta-wayland-keyboard.h \
+ wayland/meta-wayland-pointer.c \
+ wayland/meta-wayland-pointer.h \
+ wayland/meta-wayland-seat.c \
+ wayland/meta-wayland-seat.h
endif
libmutter_la_LDFLAGS = -no-undefined
diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c
index 54f6838aa..c905fbd77 100644
--- a/src/compositor/meta-window-actor.c
+++ b/src/compositor/meta-window-actor.c
@@ -380,6 +380,9 @@ meta_window_actor_constructed (GObject *object)
clutter_actor_add_child (CLUTTER_ACTOR (self), priv->actor);
+ if (meta_is_wayland_compositor ())
+ clutter_actor_set_reactive (priv->actor, TRUE);
+
/*
* Since we are holding a pointer to this actor independently of the
* ClutterContainer internals, and provide a public API to access it,
diff --git a/src/core/display-private.h b/src/core/display-private.h
index 86284fe10..2ca9c6759 100644
--- a/src/core/display-private.h
+++ b/src/core/display-private.h
@@ -467,6 +467,9 @@ gboolean meta_display_modifiers_accelerator_activate (MetaDisplay *display);
/* In above-tab-keycode.c */
guint meta_display_get_above_tab_keycode (MetaDisplay *display);
+gboolean meta_display_handle_event (MetaDisplay *display,
+ XEvent *event);
+
#ifdef HAVE_XI23
gboolean meta_display_process_barrier_event (MetaDisplay *display,
XIBarrierEvent *event);
diff --git a/src/core/display.c b/src/core/display.c
index 5e19f1439..53693685d 100644
--- a/src/core/display.c
+++ b/src/core/display.c
@@ -2137,10 +2137,9 @@ handle_window_focus_event (MetaDisplay *display,
}
/**
- * event_callback:
+ * meta_display_handle_event:
+ * @display: The MetaDisplay that events are coming from
* @event: The event that just happened
- * @data: The #MetaDisplay that events are coming from, cast to a gpointer
- * so that it can be sent to a callback
*
* This is the most important function in the whole program. It is the heart,
* it is the nexus, it is the Grand Central Station of Mutter's world.
@@ -2150,21 +2149,18 @@ handle_window_focus_event (MetaDisplay *display,
* busy around here. Most of this function is a ginormous switch statement
* dealing with all the kinds of events that might turn up.
*/
-static gboolean
-event_callback (XEvent *event,
- gpointer data)
+gboolean
+meta_display_handle_event (MetaDisplay *display,
+ XEvent *event)
{
MetaWindow *window;
MetaWindow *property_for_window;
- MetaDisplay *display;
Window modified;
gboolean frame_was_receiver;
gboolean bypass_compositor;
gboolean filter_out_event;
XIEvent *input_event;
- display = data;
-
#ifdef WITH_VERBOSE_MODE
if (dump_events)
meta_spew_event (display, event);
@@ -2655,6 +2651,15 @@ event_callback (XEvent *event,
}
break;
case XI_FocusIn:
+#ifdef HAVE_WAYLAND
+ if (meta_is_wayland_compositor ())
+ {
+ MetaWaylandCompositor *compositor =
+ meta_wayland_compositor_get_default ();
+ meta_wayland_compositor_set_input_focus (compositor, window);
+ }
+#endif
+ /* fall through */
case XI_FocusOut:
/* libXi does not properly copy the serial to the XIEnterEvent, so pull it
* from the parent XAnyEvent.
@@ -3202,6 +3207,32 @@ event_callback (XEvent *event,
return filter_out_event;
}
+static gboolean
+event_callback (XEvent *event,
+ gpointer data)
+{
+ MetaDisplay *display = data;
+
+ /* Under Wayland we want to filter out mouse motion events so we can
+ synthesize them from the Clutter events instead. This is
+ necessary because the position in the mouse events is passed to
+ the X server relative to the position of the surface. The X
+ server then translates these back to screen coordinates based on
+ the window position. If we rely on this translatation when
+ dragging a window around then the window will jump around
+ erratically because of the lag between updating the window
+ position from the surface position. Instead we bypass the
+ translation altogether by directly using the Clutter events */
+#ifdef HAVE_WAYLAND
+ if (meta_is_wayland_compositor () &&
+ event->type == GenericEvent &&
+ event->xcookie.evtype == XI_Motion)
+ return FALSE;
+#endif
+
+ return meta_display_handle_event (display, event);
+}
+
/* Return the window this has to do with, if any, rather
* than the frame or root window that was selecting
* for substructure
diff --git a/src/wayland/meta-wayland-data-device.c b/src/wayland/meta-wayland-data-device.c
new file mode 100644
index 000000000..d976352df
--- /dev/null
+++ b/src/wayland/meta-wayland-data-device.c
@@ -0,0 +1,548 @@
+/*
+ * Copyright © 2011 Kristian Høgsberg
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no representations
+ * about the suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+/* The file is based on src/data-device.c from Weston */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <glib.h>
+
+#include "meta-wayland-data-device.h"
+#include "meta-wayland-seat.h"
+#include "meta-wayland-pointer.h"
+
+static void
+data_offer_accept (struct wl_client *client,
+ struct wl_resource *resource,
+ guint32 serial,
+ const char *mime_type)
+{
+ MetaWaylandDataOffer *offer = wl_resource_get_user_data (resource);
+
+ /* FIXME: Check that client is currently focused by the input
+ * device that is currently dragging this data source. Should
+ * this be a wl_data_device request? */
+
+ if (offer->source)
+ offer->source->accept (offer->source, serial, mime_type);
+}
+
+static void
+data_offer_receive (struct wl_client *client, struct wl_resource *resource,
+ const char *mime_type, int32_t fd)
+{
+ MetaWaylandDataOffer *offer = wl_resource_get_user_data (resource);
+
+ if (offer->source)
+ offer->source->send (offer->source, mime_type, fd);
+ else
+ close (fd);
+}
+
+static void
+data_offer_destroy (struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy (resource);
+}
+
+static const struct wl_data_offer_interface data_offer_interface = {
+ data_offer_accept,
+ data_offer_receive,
+ data_offer_destroy,
+};
+
+static void
+destroy_data_offer (struct wl_resource *resource)
+{
+ MetaWaylandDataOffer *offer = wl_resource_get_user_data (resource);
+
+ if (offer->source)
+ wl_list_remove (&offer->source_destroy_listener.link);
+ free (offer);
+}
+
+static void
+destroy_offer_data_source (struct wl_listener *listener, void *data)
+{
+ MetaWaylandDataOffer *offer;
+
+ offer = wl_container_of (listener, offer, source_destroy_listener);
+
+ offer->source = NULL;
+}
+
+static struct wl_resource *
+meta_wayland_data_source_send_offer (MetaWaylandDataSource *source,
+ struct wl_resource *target)
+{
+ MetaWaylandDataOffer *offer;
+ char **p;
+
+ offer = malloc (sizeof *offer);
+ if (offer == NULL)
+ return NULL;
+
+ offer->source = source;
+ offer->source_destroy_listener.notify = destroy_offer_data_source;
+
+ offer->resource = wl_client_add_object (wl_resource_get_client (target),
+ &wl_data_offer_interface,
+ &data_offer_interface,
+ 0,
+ offer);
+ wl_resource_set_destructor (offer->resource, destroy_data_offer);
+ wl_resource_add_destroy_listener (source->resource,
+ &offer->source_destroy_listener);
+
+ wl_data_device_send_data_offer (target, offer->resource);
+
+ wl_array_for_each (p, &source->mime_types)
+ wl_data_offer_send_offer (offer->resource, *p);
+
+ return offer->resource;
+}
+
+static void
+data_source_offer (struct wl_client *client,
+ struct wl_resource *resource, const char *type)
+{
+ MetaWaylandDataSource *source = wl_resource_get_user_data (resource);
+ char **p;
+
+ p = wl_array_add (&source->mime_types, sizeof *p);
+ if (p)
+ *p = strdup (type);
+ if (!p || !*p)
+ wl_resource_post_no_memory (resource);
+}
+
+static void
+data_source_destroy (struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy (resource);
+}
+
+static struct wl_data_source_interface data_source_interface = {
+ data_source_offer,
+ data_source_destroy
+};
+
+static void
+destroy_drag_focus (struct wl_listener *listener, void *data)
+{
+ MetaWaylandSeat *seat = wl_container_of (listener, seat, drag_focus_listener);
+
+ seat->drag_focus_resource = NULL;
+}
+
+static void
+drag_grab_focus (MetaWaylandPointerGrab *grab,
+ MetaWaylandSurface *surface,
+ wl_fixed_t x,
+ wl_fixed_t y)
+{
+ MetaWaylandSeat *seat = wl_container_of (grab, seat, drag_grab);
+ struct wl_resource *resource, *offer = NULL;
+ struct wl_display *display;
+ guint32 serial;
+
+ if (seat->drag_focus_resource)
+ {
+ wl_data_device_send_leave (seat->drag_focus_resource);
+ wl_list_remove (&seat->drag_focus_listener.link);
+ seat->drag_focus_resource = NULL;
+ seat->drag_focus = NULL;
+ }
+
+ if (!surface)
+ return;
+
+ if (!seat->drag_data_source &&
+ wl_resource_get_client (surface->resource) != seat->drag_client)
+ return;
+
+ resource =
+ wl_resource_find_for_client (&seat->drag_resource_list,
+ wl_resource_get_client (surface->resource));
+ if (!resource)
+ return;
+
+ display = wl_client_get_display (wl_resource_get_client (resource));
+ serial = wl_display_next_serial (display);
+
+ if (seat->drag_data_source)
+ offer = meta_wayland_data_source_send_offer (seat->drag_data_source,
+ resource);
+
+ wl_data_device_send_enter (resource, serial, surface->resource,
+ x, y, offer);
+
+ seat->drag_focus = surface;
+ seat->drag_focus_listener.notify = destroy_drag_focus;
+ wl_resource_add_destroy_listener (resource, &seat->drag_focus_listener);
+ seat->drag_focus_resource = resource;
+ grab->focus = surface;
+}
+
+static void
+drag_grab_motion (MetaWaylandPointerGrab *grab,
+ guint32 time, wl_fixed_t x, wl_fixed_t y)
+{
+ MetaWaylandSeat *seat = wl_container_of (grab, seat, drag_grab);
+
+ if (seat->drag_focus_resource)
+ wl_data_device_send_motion (seat->drag_focus_resource, time, x, y);
+}
+
+static void
+data_device_end_drag_grab (MetaWaylandSeat *seat)
+{
+ if (seat->drag_surface)
+ {
+ seat->drag_surface = NULL;
+ wl_signal_emit (&seat->drag_icon_signal, NULL);
+ wl_list_remove (&seat->drag_icon_listener.link);
+ }
+
+ drag_grab_focus (&seat->drag_grab, NULL,
+ wl_fixed_from_int (0), wl_fixed_from_int (0));
+
+ meta_wayland_pointer_end_grab (&seat->pointer);
+
+ seat->drag_data_source = NULL;
+ seat->drag_client = NULL;
+}
+
+static void
+drag_grab_button (MetaWaylandPointerGrab *grab,
+ guint32 time, guint32 button, guint32 state_w)
+{
+ MetaWaylandSeat *seat = wl_container_of (grab, seat, drag_grab);
+ enum wl_pointer_button_state state = state_w;
+
+ if (seat->drag_focus_resource &&
+ seat->pointer.grab_button == button &&
+ state == WL_POINTER_BUTTON_STATE_RELEASED)
+ wl_data_device_send_drop (seat->drag_focus_resource);
+
+ if (seat->pointer.button_count == 0 &&
+ state == WL_POINTER_BUTTON_STATE_RELEASED)
+ {
+ if (seat->drag_data_source)
+ wl_list_remove (&seat->drag_data_source_listener.link);
+ data_device_end_drag_grab (seat);
+ }
+}
+
+static const MetaWaylandPointerGrabInterface drag_grab_interface = {
+ drag_grab_focus,
+ drag_grab_motion,
+ drag_grab_button,
+};
+
+static void
+destroy_data_device_source (struct wl_listener *listener, void *data)
+{
+ MetaWaylandSeat *seat =
+ wl_container_of (listener, seat, drag_data_source_listener);
+
+ data_device_end_drag_grab (seat);
+}
+
+static void
+destroy_data_device_icon (struct wl_listener *listener, void *data)
+{
+ MetaWaylandSeat *seat =
+ wl_container_of (listener, seat, drag_icon_listener);
+
+ seat->drag_surface = NULL;
+}
+
+static void
+data_device_start_drag (struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *source_resource,
+ struct wl_resource *origin_resource,
+ struct wl_resource *icon_resource, guint32 serial)
+{
+ MetaWaylandSeat *seat = wl_resource_get_user_data (resource);
+
+ /* FIXME: Check that client has implicit grab on the origin
+ * surface that matches the given time. */
+
+ /* FIXME: Check that the data source type array isn't empty. */
+
+ seat->drag_grab.interface = &drag_grab_interface;
+
+ seat->drag_client = client;
+ seat->drag_data_source = NULL;
+
+ if (source_resource)
+ {
+ seat->drag_data_source = wl_resource_get_user_data (source_resource);
+ seat->drag_data_source_listener.notify = destroy_data_device_source;
+ wl_resource_add_destroy_listener (source_resource,
+ &seat->drag_data_source_listener);
+ }
+
+ if (icon_resource)
+ {
+ seat->drag_surface = wl_resource_get_user_data (icon_resource);
+ seat->drag_icon_listener.notify = destroy_data_device_icon;
+ wl_resource_add_destroy_listener (icon_resource,
+ &seat->drag_icon_listener);
+ wl_signal_emit (&seat->drag_icon_signal, icon_resource);
+ }
+
+ meta_wayland_pointer_set_focus (&seat->pointer, NULL,
+ wl_fixed_from_int (0),
+ wl_fixed_from_int (0));
+ meta_wayland_pointer_start_grab (&seat->pointer, &seat->drag_grab);
+}
+
+static void
+destroy_selection_data_source (struct wl_listener *listener, void *data)
+{
+ MetaWaylandSeat *seat =
+ wl_container_of (listener, seat, selection_data_source_listener);
+ struct wl_resource *data_device;
+ struct wl_resource *focus = NULL;
+
+ seat->selection_data_source = NULL;
+
+ focus = seat->keyboard.focus_resource;
+
+ if (focus)
+ {
+ data_device =
+ wl_resource_find_for_client (&seat->drag_resource_list,
+ wl_resource_get_client (focus));
+ if (data_device)
+ wl_data_device_send_selection (data_device, NULL);
+ }
+
+ wl_signal_emit (&seat->selection_signal, seat);
+}
+
+void
+meta_wayland_seat_set_selection (MetaWaylandSeat *seat,
+ MetaWaylandDataSource *source,
+ guint32 serial)
+{
+ struct wl_resource *data_device, *offer;
+ struct wl_resource *focus = NULL;
+
+ if (seat->selection_data_source &&
+ seat->selection_serial - serial < UINT32_MAX / 2)
+ return;
+
+ if (seat->selection_data_source)
+ {
+ seat->selection_data_source->cancel (seat->selection_data_source);
+ wl_list_remove (&seat->selection_data_source_listener.link);
+ seat->selection_data_source = NULL;
+ }
+
+ seat->selection_data_source = source;
+ seat->selection_serial = serial;
+
+ focus = seat->keyboard.focus_resource;
+
+ if (focus)
+ {
+ data_device =
+ wl_resource_find_for_client (&seat->drag_resource_list,
+ wl_resource_get_client (focus));
+ if (data_device && source)
+ {
+ offer =
+ meta_wayland_data_source_send_offer (seat->selection_data_source,
+ data_device);
+ wl_data_device_send_selection (data_device, offer);
+ }
+ else if (data_device)
+ {
+ wl_data_device_send_selection (data_device, NULL);
+ }
+ }
+
+ wl_signal_emit (&seat->selection_signal, seat);
+
+ if (source)
+ {
+ seat->selection_data_source_listener.notify =
+ destroy_selection_data_source;
+ wl_resource_add_destroy_listener (source->resource,
+ &seat->selection_data_source_listener);
+ }
+}
+
+static void
+data_device_set_selection (struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *source_resource,
+ guint32 serial)
+{
+ if (!source_resource)
+ return;
+
+ /* FIXME: Store serial and check against incoming serial here. */
+ meta_wayland_seat_set_selection (wl_resource_get_user_data (resource),
+ wl_resource_get_user_data (source_resource),
+ serial);
+}
+
+static const struct wl_data_device_interface data_device_interface = {
+ data_device_start_drag,
+ data_device_set_selection,
+};
+
+static void
+destroy_data_source (struct wl_resource *resource)
+{
+ MetaWaylandDataSource *source = wl_container_of (resource, source, resource);
+ char **p;
+
+ wl_array_for_each (p, &source->mime_types) free (*p);
+
+ wl_array_release (&source->mime_types);
+}
+
+static void
+client_source_accept (MetaWaylandDataSource *source,
+ guint32 time, const char *mime_type)
+{
+ wl_data_source_send_target (source->resource, mime_type);
+}
+
+static void
+client_source_send (MetaWaylandDataSource *source,
+ const char *mime_type, int32_t fd)
+{
+ wl_data_source_send_send (source->resource, mime_type, fd);
+ close (fd);
+}
+
+static void
+client_source_cancel (MetaWaylandDataSource *source)
+{
+ wl_data_source_send_cancelled (source->resource);
+}
+
+static void
+create_data_source (struct wl_client *client,
+ struct wl_resource *resource, guint32 id)
+{
+ MetaWaylandDataSource *source;
+
+ source = malloc (sizeof *source);
+ if (source == NULL)
+ {
+ wl_resource_post_no_memory (resource);
+ return;
+ }
+
+ source->resource = wl_client_add_object (client,
+ &wl_data_source_interface,
+ &data_source_interface,
+ id,
+ source);
+ wl_resource_set_destructor (source->resource, destroy_data_source);
+
+ source->accept = client_source_accept;
+ source->send = client_source_send;
+ source->cancel = client_source_cancel;
+
+ wl_array_init (&source->mime_types);
+}
+
+static void
+unbind_data_device (struct wl_resource *resource)
+{
+ wl_list_remove (wl_resource_get_link (resource));
+}
+
+static void
+get_data_device (struct wl_client *client,
+ struct wl_resource *manager_resource,
+ guint32 id, struct wl_resource *seat_resource)
+{
+ MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource);
+ struct wl_resource *resource;
+
+ resource = wl_client_add_object (client, &wl_data_device_interface,
+ &data_device_interface, id, seat);
+
+ wl_list_insert (&seat->drag_resource_list, wl_resource_get_link (resource));
+ wl_resource_set_destructor (resource, unbind_data_device);
+}
+
+static const struct wl_data_device_manager_interface manager_interface = {
+ create_data_source,
+ get_data_device
+};
+
+static void
+bind_manager (struct wl_client *client,
+ void *data, guint32 version, guint32 id)
+{
+ wl_client_add_object (client, &wl_data_device_manager_interface,
+ &manager_interface, id, NULL);
+}
+
+void
+meta_wayland_data_device_set_keyboard_focus (MetaWaylandSeat *seat)
+{
+ struct wl_resource *data_device, *focus, *offer;
+ MetaWaylandDataSource *source;
+
+ focus = seat->keyboard.focus_resource;
+ if (!focus)
+ return;
+
+ data_device = wl_resource_find_for_client (&seat->drag_resource_list,
+ wl_resource_get_client (focus));
+ if (!data_device)
+ return;
+
+ source = seat->selection_data_source;
+ if (source)
+ {
+ offer = meta_wayland_data_source_send_offer (source, data_device);
+ wl_data_device_send_selection (data_device, offer);
+ }
+}
+
+int
+meta_wayland_data_device_manager_init (struct wl_display *display)
+{
+ if (wl_display_add_global (display,
+ &wl_data_device_manager_interface,
+ NULL, bind_manager) == NULL)
+ return -1;
+
+ return 0;
+}
diff --git a/src/wayland/meta-wayland-data-device.h b/src/wayland/meta-wayland-data-device.h
new file mode 100644
index 000000000..58635531c
--- /dev/null
+++ b/src/wayland/meta-wayland-data-device.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright © 2008 Kristian Høgsberg
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no representations
+ * about the suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#ifndef __META_WAYLAND_DATA_DEVICE_H__
+#define __META_WAYLAND_DATA_DEVICE_H__
+
+#include <wayland-server.h>
+
+#include "meta-wayland-seat.h"
+
+void
+meta_wayland_data_device_set_keyboard_focus (MetaWaylandSeat *seat);
+
+int
+meta_wayland_data_device_manager_init (struct wl_display *display);
+
+void
+meta_wayland_seat_set_selection (MetaWaylandSeat *seat,
+ MetaWaylandDataSource *source,
+ uint32_t serial);
+
+
+#endif /* __META_WAYLAND_DATA_DEVICE_H__ */
diff --git a/src/wayland/meta-wayland-keyboard.c b/src/wayland/meta-wayland-keyboard.c
new file mode 100644
index 000000000..c43de914e
--- /dev/null
+++ b/src/wayland/meta-wayland-keyboard.c
@@ -0,0 +1,512 @@
+/*
+ * Wayland Support
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * 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.
+ */
+
+/*
+ * Copyright © 2010-2011 Intel Corporation
+ * Copyright © 2008-2011 Kristian Høgsberg
+ * Copyright © 2012 Collabora, Ltd.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* The file is based on src/input.c from Weston */
+
+#define _GNU_SOURCE
+
+#include "config.h"
+
+#include <glib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#include "meta-wayland-keyboard.h"
+
+static MetaWaylandSeat *
+meta_wayland_keyboard_get_seat (MetaWaylandKeyboard *keyboard)
+{
+ MetaWaylandSeat *seat = wl_container_of (keyboard, seat, keyboard);
+
+ return seat;
+}
+
+static int
+create_anonymous_file (off_t size,
+ GError **error)
+{
+ static const char template[] = "mutter-shared-XXXXXX";
+ char *path;
+ int fd, flags;
+
+ fd = g_file_open_tmp (template, &path, error);
+
+ if (fd == -1)
+ return -1;
+
+ unlink (path);
+ g_free (path);
+
+ flags = fcntl (fd, F_GETFD);
+ if (flags == -1)
+ goto err;
+
+ if (fcntl (fd, F_SETFD, flags | FD_CLOEXEC) == -1)
+ goto err;
+
+ if (ftruncate (fd, size) < 0)
+ goto err;
+
+ return fd;
+
+ err:
+ g_set_error_literal (error,
+ G_FILE_ERROR,
+ g_file_error_from_errno (errno),
+ strerror (errno));
+ close (fd);
+
+ return -1;
+}
+
+static gboolean
+meta_wayland_xkb_info_new_keymap (MetaWaylandXkbInfo *xkb_info)
+{
+ GError *error = NULL;
+ char *keymap_str;
+
+ xkb_info->shift_mod =
+ xkb_map_mod_get_index (xkb_info->keymap, XKB_MOD_NAME_SHIFT);
+ xkb_info->caps_mod =
+ xkb_map_mod_get_index (xkb_info->keymap, XKB_MOD_NAME_CAPS);
+ xkb_info->ctrl_mod =
+ xkb_map_mod_get_index (xkb_info->keymap, XKB_MOD_NAME_CTRL);
+ xkb_info->alt_mod =
+ xkb_map_mod_get_index (xkb_info->keymap, XKB_MOD_NAME_ALT);
+ xkb_info->mod2_mod = xkb_map_mod_get_index (xkb_info->keymap, "Mod2");
+ xkb_info->mod3_mod = xkb_map_mod_get_index (xkb_info->keymap, "Mod3");
+ xkb_info->super_mod =
+ xkb_map_mod_get_index (xkb_info->keymap, XKB_MOD_NAME_LOGO);
+ xkb_info->mod5_mod = xkb_map_mod_get_index (xkb_info->keymap, "Mod5");
+
+ keymap_str = xkb_map_get_as_string (xkb_info->keymap);
+ if (keymap_str == NULL)
+ {
+ g_warning ("failed to get string version of keymap\n");
+ return FALSE;
+ }
+ xkb_info->keymap_size = strlen (keymap_str) + 1;
+
+ xkb_info->keymap_fd = create_anonymous_file (xkb_info->keymap_size, &error);
+ if (xkb_info->keymap_fd < 0)
+ {
+ g_warning ("creating a keymap file for %lu bytes failed: %s\n",
+ (unsigned long) xkb_info->keymap_size,
+ error->message);
+ g_clear_error (&error);
+ goto err_keymap_str;
+ }
+
+ xkb_info->keymap_area = mmap (NULL, xkb_info->keymap_size,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED, xkb_info->keymap_fd, 0);
+ if (xkb_info->keymap_area == MAP_FAILED)
+ {
+ g_warning ("failed to mmap() %lu bytes\n",
+ (unsigned long) xkb_info->keymap_size);
+ goto err_dev_zero;
+ }
+ strcpy (xkb_info->keymap_area, keymap_str);
+ free (keymap_str);
+
+ return TRUE;
+
+err_dev_zero:
+ close (xkb_info->keymap_fd);
+ xkb_info->keymap_fd = -1;
+err_keymap_str:
+ free (keymap_str);
+ return FALSE;
+}
+
+static gboolean
+meta_wayland_keyboard_build_global_keymap (struct xkb_context *xkb_context,
+ struct xkb_rule_names *xkb_names,
+ MetaWaylandXkbInfo *xkb_info)
+{
+ xkb_info->keymap = xkb_map_new_from_names (xkb_context,
+ xkb_names,
+ 0 /* flags */);
+ if (xkb_info->keymap == NULL)
+ {
+ g_warning ("failed to compile global XKB keymap\n"
+ " tried rules %s, model %s, layout %s, variant %s, "
+ "options %s\n",
+ xkb_names->rules,
+ xkb_names->model,
+ xkb_names->layout,
+ xkb_names->variant,
+ xkb_names->options);
+ return FALSE;
+ }
+
+ if (!meta_wayland_xkb_info_new_keymap (xkb_info))
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+lose_keyboard_focus (struct wl_listener *listener, void *data)
+{
+ MetaWaylandKeyboard *keyboard =
+ wl_container_of (listener, keyboard, focus_listener);
+
+ keyboard->focus_resource = NULL;
+}
+
+static void
+default_grab_key (MetaWaylandKeyboardGrab *grab,
+ uint32_t time, uint32_t key, uint32_t state)
+{
+ MetaWaylandKeyboard *keyboard = grab->keyboard;
+ struct wl_resource *resource;
+ uint32_t serial;
+
+ resource = keyboard->focus_resource;
+ if (resource)
+ {
+ struct wl_client *client = wl_resource_get_client (resource);
+ struct wl_display *display = wl_client_get_display (client);
+ serial = wl_display_next_serial (display);
+ wl_keyboard_send_key (resource, serial, time, key, state);
+ }
+}
+
+static struct wl_resource *
+find_resource_for_surface (struct wl_list *list, MetaWaylandSurface *surface)
+{
+ struct wl_client *client;
+
+ if (!surface)
+ return NULL;
+
+ if (!surface->resource)
+ return NULL;
+
+ client = wl_resource_get_client (surface->resource);
+
+ return wl_resource_find_for_client (list, client);
+}
+
+static void
+default_grab_modifiers (MetaWaylandKeyboardGrab *grab, uint32_t serial,
+ uint32_t mods_depressed, uint32_t mods_latched,
+ uint32_t mods_locked, uint32_t group)
+{
+ MetaWaylandKeyboard *keyboard = grab->keyboard;
+ MetaWaylandSeat *seat = meta_wayland_keyboard_get_seat (keyboard);
+ MetaWaylandPointer *pointer = &seat->pointer;
+ struct wl_resource *resource, *pr;
+
+ resource = keyboard->focus_resource;
+ if (!resource)
+ return;
+
+ wl_keyboard_send_modifiers (resource, serial, mods_depressed,
+ mods_latched, mods_locked, group);
+
+ if (pointer && pointer->focus && pointer->focus != keyboard->focus)
+ {
+ pr = find_resource_for_surface (&keyboard->resource_list,
+ pointer->focus);
+ if (pr)
+ {
+ wl_keyboard_send_modifiers (pr,
+ serial,
+ keyboard->modifiers.mods_depressed,
+ keyboard->modifiers.mods_latched,
+ keyboard->modifiers.mods_locked,
+ keyboard->modifiers.group);
+ }
+ }
+}
+
+static const MetaWaylandKeyboardGrabInterface
+ default_keyboard_grab_interface = {
+ default_grab_key,
+ default_grab_modifiers,
+};
+
+gboolean
+meta_wayland_keyboard_init (MetaWaylandKeyboard *keyboard,
+ struct wl_display *display)
+{
+ memset (keyboard, 0, sizeof *keyboard);
+
+ wl_list_init (&keyboard->resource_list);
+ wl_array_init (&keyboard->keys);
+ keyboard->focus_listener.notify = lose_keyboard_focus;
+ keyboard->default_grab.interface = &default_keyboard_grab_interface;
+ keyboard->default_grab.keyboard = keyboard;
+ keyboard->grab = &keyboard->default_grab;
+ wl_signal_init (&keyboard->focus_signal);
+
+ keyboard->display = display;
+
+ keyboard->xkb_context = xkb_context_new (0 /* flags */);
+
+ meta_wayland_keyboard_build_global_keymap (keyboard->xkb_context,
+ &keyboard->xkb_names,
+ &keyboard->xkb_info);
+
+ return TRUE;
+}
+
+static void
+meta_wayland_xkb_info_destroy (MetaWaylandXkbInfo *xkb_info)
+{
+ if (xkb_info->keymap)
+ xkb_map_unref (xkb_info->keymap);
+
+ if (xkb_info->keymap_area)
+ munmap (xkb_info->keymap_area, xkb_info->keymap_size);
+ if (xkb_info->keymap_fd >= 0)
+ close (xkb_info->keymap_fd);
+}
+
+static void
+set_modifiers (MetaWaylandKeyboard *keyboard,
+ guint32 serial,
+ ClutterModifierType modifier_state)
+{
+ MetaWaylandKeyboardGrab *grab = keyboard->grab;
+ uint32_t depressed_mods = 0;
+ uint32_t locked_mods = 0;
+
+ if (keyboard->last_modifier_state == modifier_state)
+ return;
+
+ if ((modifier_state & CLUTTER_SHIFT_MASK) &&
+ keyboard->xkb_info.shift_mod != XKB_MOD_INVALID)
+ depressed_mods |= (1 << keyboard->xkb_info.shift_mod);
+
+ if ((modifier_state & CLUTTER_LOCK_MASK) &&
+ keyboard->xkb_info.caps_mod != XKB_MOD_INVALID)
+ locked_mods |= (1 << keyboard->xkb_info.caps_mod);
+
+ if ((modifier_state & CLUTTER_CONTROL_MASK) &&
+ keyboard->xkb_info.ctrl_mod != XKB_MOD_INVALID)
+ depressed_mods |= (1 << keyboard->xkb_info.ctrl_mod);
+
+ if ((modifier_state & CLUTTER_MOD1_MASK) &&
+ keyboard->xkb_info.alt_mod != XKB_MOD_INVALID)
+ depressed_mods |= (1 << keyboard->xkb_info.alt_mod);
+
+ if ((modifier_state & CLUTTER_MOD2_MASK) &&
+ keyboard->xkb_info.mod2_mod != XKB_MOD_INVALID)
+ depressed_mods |= (1 << keyboard->xkb_info.mod2_mod);
+
+ if ((modifier_state & CLUTTER_MOD3_MASK) &&
+ keyboard->xkb_info.mod3_mod != XKB_MOD_INVALID)
+ depressed_mods |= (1 << keyboard->xkb_info.mod3_mod);
+
+ if ((modifier_state & CLUTTER_SUPER_MASK) &&
+ keyboard->xkb_info.super_mod != XKB_MOD_INVALID)
+ depressed_mods |= (1 << keyboard->xkb_info.super_mod);
+
+ if ((modifier_state & CLUTTER_MOD5_MASK) &&
+ keyboard->xkb_info.mod5_mod != XKB_MOD_INVALID)
+ depressed_mods |= (1 << keyboard->xkb_info.mod5_mod);
+
+ keyboard->last_modifier_state = modifier_state;
+
+ grab->interface->modifiers (grab,
+ serial,
+ depressed_mods,
+ 0, /* latched_modes */
+ locked_mods,
+ 0 /* group */);
+}
+
+void
+meta_wayland_keyboard_handle_event (MetaWaylandKeyboard *keyboard,
+ const ClutterKeyEvent *event)
+{
+ gboolean state = event->type == CLUTTER_KEY_PRESS;
+ guint evdev_code;
+ uint32_t serial;
+
+ /* We can't do anything with the event if we can't get an evdev
+ keycode for it */
+ if (event->device == NULL ||
+ !clutter_input_device_keycode_to_evdev (event->device,
+ event->hardware_keycode,
+ &evdev_code))
+ return;
+
+ /* We want to ignore events that are sent because of auto-repeat. In
+ the Clutter event stream these appear as a single key press
+ event. We can detect that because the key will already have been
+ pressed */
+ if (state)
+ {
+ uint32_t *end = (void *) ((char *) keyboard->keys.data +
+ keyboard->keys.size);
+ uint32_t *k;
+
+ /* Ignore the event if the key is already down */
+ for (k = keyboard->keys.data; k < end; k++)
+ if (*k == evdev_code)
+ return;
+
+ /* Otherwise add the key to the list of pressed keys */
+ k = wl_array_add (&keyboard->keys, sizeof (*k));
+ *k = evdev_code;
+ }
+ else
+ {
+ uint32_t *end = (void *) ((char *) keyboard->keys.data +
+ keyboard->keys.size);
+ uint32_t *k;
+
+ /* Remove the key from the array */
+ for (k = keyboard->keys.data; k < end; k++)
+ if (*k == evdev_code)
+ {
+ *k = *(end - 1);
+ keyboard->keys.size -= sizeof (*k);
+
+ goto found;
+ }
+
+ g_warning ("unexpected key release event for key 0x%x", evdev_code);
+
+ found:
+ (void) 0;
+ }
+
+ serial = wl_display_next_serial (keyboard->display);
+
+ set_modifiers (keyboard, serial, event->modifier_state);
+
+ keyboard->grab->interface->key (keyboard->grab,
+ event->time,
+ evdev_code,
+ state);
+}
+
+void
+meta_wayland_keyboard_set_focus (MetaWaylandKeyboard *keyboard,
+ MetaWaylandSurface *surface)
+{
+ struct wl_resource *resource;
+ uint32_t serial;
+
+ if (keyboard->focus_resource && keyboard->focus != surface)
+ {
+ struct wl_display *display;
+ struct wl_client *client;
+
+ resource = keyboard->focus_resource;
+ client = wl_resource_get_client (resource);
+ display = wl_client_get_display (client);
+ serial = wl_display_next_serial (display);
+ wl_keyboard_send_leave (resource, serial, keyboard->focus->resource);
+ wl_list_remove (&keyboard->focus_listener.link);
+ }
+
+ resource = find_resource_for_surface (&keyboard->resource_list, surface);
+ if (resource &&
+ (keyboard->focus != surface || keyboard->focus_resource != resource))
+ {
+ struct wl_client *client = wl_resource_get_client (resource);
+ struct wl_display *display;
+
+ display = wl_client_get_display (client);
+ serial = wl_display_next_serial (display);
+ wl_keyboard_send_modifiers (resource, serial,
+ keyboard->modifiers.mods_depressed,
+ keyboard->modifiers.mods_latched,
+ keyboard->modifiers.mods_locked,
+ keyboard->modifiers.group);
+ wl_keyboard_send_enter (resource, serial, surface->resource,
+ &keyboard->keys);
+ wl_resource_add_destroy_listener (resource, &keyboard->focus_listener);
+ keyboard->focus_serial = serial;
+ }
+
+ keyboard->focus_resource = resource;
+ keyboard->focus = surface;
+ wl_signal_emit (&keyboard->focus_signal, keyboard);
+}
+
+void
+meta_wayland_keyboard_start_grab (MetaWaylandKeyboard *keyboard,
+ MetaWaylandKeyboardGrab *grab)
+{
+ keyboard->grab = grab;
+ grab->keyboard = keyboard;
+
+ /* XXX focus? */
+}
+
+void
+meta_wayland_keyboard_end_grab (MetaWaylandKeyboard *keyboard)
+{
+ keyboard->grab = &keyboard->default_grab;
+}
+
+void
+meta_wayland_keyboard_release (MetaWaylandKeyboard *keyboard)
+{
+ g_free ((char *) keyboard->xkb_names.rules);
+ g_free ((char *) keyboard->xkb_names.model);
+ g_free ((char *) keyboard->xkb_names.layout);
+ g_free ((char *) keyboard->xkb_names.variant);
+ g_free ((char *) keyboard->xkb_names.options);
+
+ meta_wayland_xkb_info_destroy (&keyboard->xkb_info);
+ xkb_context_unref (keyboard->xkb_context);
+
+ /* XXX: What about keyboard->resource_list? */
+ if (keyboard->focus_resource)
+ wl_list_remove (&keyboard->focus_listener.link);
+ wl_array_release (&keyboard->keys);
+}
diff --git a/src/wayland/meta-wayland-keyboard.h b/src/wayland/meta-wayland-keyboard.h
new file mode 100644
index 000000000..fd0d0b3c0
--- /dev/null
+++ b/src/wayland/meta-wayland-keyboard.h
@@ -0,0 +1,75 @@
+/*
+ * Wayland Support
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * 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.
+ */
+
+/*
+ * Copyright © 2008-2011 Kristian Høgsberg
+ * Copyright © 2012 Collabora, Ltd.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __META_WAYLAND_KEYBOARD_H__
+#define __META_WAYLAND_KEYBOARD_H__
+
+#include <clutter/clutter.h>
+#include <wayland-server.h>
+
+#include "meta-wayland-seat.h"
+
+gboolean
+meta_wayland_keyboard_init (MetaWaylandKeyboard *keyboard,
+ struct wl_display *display);
+
+void
+meta_wayland_keyboard_handle_event (MetaWaylandKeyboard *keyboard,
+ const ClutterKeyEvent *event);
+
+void
+meta_wayland_keyboard_set_focus (MetaWaylandKeyboard *keyboard,
+ MetaWaylandSurface *surface);
+
+void
+meta_wayland_keyboard_start_grab (MetaWaylandKeyboard *device,
+ MetaWaylandKeyboardGrab *grab);
+
+void
+meta_wayland_keyboard_end_grab (MetaWaylandKeyboard *keyboard);
+
+void
+meta_wayland_keyboard_release (MetaWaylandKeyboard *keyboard);
+
+#endif /* __META_WAYLAND_KEYBOARD_H__ */
diff --git a/src/wayland/meta-wayland-pointer.c b/src/wayland/meta-wayland-pointer.c
new file mode 100644
index 000000000..52a81ad7e
--- /dev/null
+++ b/src/wayland/meta-wayland-pointer.c
@@ -0,0 +1,260 @@
+/*
+ * Wayland Support
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Copyright © 2008 Kristian Høgsberg
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no representations
+ * about the suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+/* The file is based on src/input.c from Weston */
+
+#include "config.h"
+
+#include "meta-wayland-pointer.h"
+
+static MetaWaylandSeat *
+meta_wayland_pointer_get_seat (MetaWaylandPointer *pointer)
+{
+ MetaWaylandSeat *seat = wl_container_of (pointer, seat, pointer);
+
+ return seat;
+}
+
+static void
+lose_pointer_focus (struct wl_listener *listener, void *data)
+{
+ MetaWaylandPointer *pointer =
+ wl_container_of (listener, pointer, focus_listener);
+
+ pointer->focus_resource = NULL;
+}
+
+static void
+default_grab_focus (MetaWaylandPointerGrab *grab,
+ MetaWaylandSurface *surface,
+ wl_fixed_t x,
+ wl_fixed_t y)
+{
+ MetaWaylandPointer *pointer = grab->pointer;
+
+ if (pointer->button_count > 0)
+ return;
+
+ meta_wayland_pointer_set_focus (pointer, surface, x, y);
+}
+
+static void
+default_grab_motion (MetaWaylandPointerGrab *grab,
+ uint32_t time, wl_fixed_t x, wl_fixed_t y)
+{
+ struct wl_resource *resource;
+
+ resource = grab->pointer->focus_resource;
+ if (resource)
+ wl_pointer_send_motion (resource, time, x, y);
+}
+
+static void
+default_grab_button (MetaWaylandPointerGrab *grab,
+ uint32_t time, uint32_t button, uint32_t state_w)
+{
+ MetaWaylandPointer *pointer = grab->pointer;
+ struct wl_resource *resource;
+ uint32_t serial;
+ enum wl_pointer_button_state state = state_w;
+
+ resource = pointer->focus_resource;
+ if (resource)
+ {
+ struct wl_client *client = wl_resource_get_client (resource);
+ struct wl_display *display = wl_client_get_display (client);
+ serial = wl_display_next_serial (display);
+ wl_pointer_send_button (resource, serial, time, button, state_w);
+ }
+
+ if (pointer->button_count == 0 && state == WL_POINTER_BUTTON_STATE_RELEASED)
+ meta_wayland_pointer_set_focus (pointer, pointer->current,
+ pointer->current_x, pointer->current_y);
+}
+
+static const MetaWaylandPointerGrabInterface default_pointer_grab_interface = {
+ default_grab_focus,
+ default_grab_motion,
+ default_grab_button
+};
+
+void
+meta_wayland_pointer_init (MetaWaylandPointer *pointer)
+{
+ memset (pointer, 0, sizeof *pointer);
+ wl_list_init (&pointer->resource_list);
+ pointer->focus_listener.notify = lose_pointer_focus;
+ pointer->default_grab.interface = &default_pointer_grab_interface;
+ pointer->default_grab.pointer = pointer;
+ pointer->grab = &pointer->default_grab;
+ wl_signal_init (&pointer->focus_signal);
+
+ /* FIXME: Pick better co-ords. */
+ pointer->x = wl_fixed_from_int (100);
+ pointer->y = wl_fixed_from_int (100);
+}
+
+void
+meta_wayland_pointer_release (MetaWaylandPointer *pointer)
+{
+ /* XXX: What about pointer->resource_list? */
+ if (pointer->focus_resource)
+ wl_list_remove (&pointer->focus_listener.link);
+}
+
+static struct wl_resource *
+find_resource_for_surface (struct wl_list *list, MetaWaylandSurface *surface)
+{
+ struct wl_client *client;
+
+ if (!surface)
+ return NULL;
+
+ if (!surface->resource)
+ return NULL;
+
+ client = wl_resource_get_client (surface->resource);
+
+ return wl_resource_find_for_client (list, client);
+}
+
+void
+meta_wayland_pointer_set_focus (MetaWaylandPointer *pointer,
+ MetaWaylandSurface *surface,
+ wl_fixed_t sx, wl_fixed_t sy)
+{
+ MetaWaylandSeat *seat = meta_wayland_pointer_get_seat (pointer);
+ MetaWaylandKeyboard *kbd = &seat->keyboard;
+ struct wl_resource *resource, *kr;
+ uint32_t serial;
+
+ resource = pointer->focus_resource;
+ if (resource && pointer->focus != surface)
+ {
+ struct wl_client *client = wl_resource_get_client (resource);
+ struct wl_display *display = wl_client_get_display (client);
+ serial = wl_display_next_serial (display);
+ wl_pointer_send_leave (resource, serial, pointer->focus->resource);
+ wl_list_remove (&pointer->focus_listener.link);
+ }
+
+ resource = find_resource_for_surface (&pointer->resource_list, surface);
+ if (resource &&
+ (pointer->focus != surface || pointer->focus_resource != resource))
+ {
+ struct wl_client *client = wl_resource_get_client (resource);
+ struct wl_display *display = wl_client_get_display (client);
+ serial = wl_display_next_serial (display);
+ if (kbd)
+ {
+ kr = find_resource_for_surface (&kbd->resource_list, surface);
+ if (kr)
+ {
+ wl_keyboard_send_modifiers (kr,
+ serial,
+ kbd->modifiers.mods_depressed,
+ kbd->modifiers.mods_latched,
+ kbd->modifiers.mods_locked,
+ kbd->modifiers.group);
+ }
+ }
+ wl_pointer_send_enter (resource, serial, surface->resource, sx, sy);
+ wl_resource_add_destroy_listener (resource, &pointer->focus_listener);
+ pointer->focus_serial = serial;
+ }
+
+ pointer->focus_resource = resource;
+ pointer->focus = surface;
+ pointer->default_grab.focus = surface;
+ wl_signal_emit (&pointer->focus_signal, pointer);
+}
+
+void
+meta_wayland_pointer_start_grab (MetaWaylandPointer *pointer,
+ MetaWaylandPointerGrab *grab)
+{
+ const MetaWaylandPointerGrabInterface *interface;
+
+ pointer->grab = grab;
+ interface = pointer->grab->interface;
+ grab->pointer = pointer;
+
+ if (pointer->current)
+ interface->focus (pointer->grab, pointer->current,
+ pointer->current_x, pointer->current_y);
+}
+
+void
+meta_wayland_pointer_end_grab (MetaWaylandPointer *pointer)
+{
+ const MetaWaylandPointerGrabInterface *interface;
+
+ pointer->grab = &pointer->default_grab;
+ interface = pointer->grab->interface;
+ interface->focus (pointer->grab, pointer->current,
+ pointer->current_x, pointer->current_y);
+}
+
+static void
+current_surface_destroy (struct wl_listener *listener, void *data)
+{
+ MetaWaylandPointer *pointer =
+ wl_container_of (listener, pointer, current_listener);
+
+ pointer->current = NULL;
+}
+
+void
+meta_wayland_pointer_set_current (MetaWaylandPointer *pointer,
+ MetaWaylandSurface *surface)
+{
+ if (pointer->current)
+ wl_list_remove (&pointer->current_listener.link);
+
+ pointer->current = surface;
+
+ if (!surface)
+ return;
+
+ wl_resource_add_destroy_listener (surface->resource,
+ &pointer->current_listener);
+ pointer->current_listener.notify = current_surface_destroy;
+}
diff --git a/src/wayland/meta-wayland-pointer.h b/src/wayland/meta-wayland-pointer.h
new file mode 100644
index 000000000..a1e5f3854
--- /dev/null
+++ b/src/wayland/meta-wayland-pointer.h
@@ -0,0 +1,49 @@
+/*
+ * Wayland Support
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __META_WAYLAND_POINTER_H__
+#define __META_WAYLAND_POINTER_H__
+
+#include <wayland-server.h>
+
+#include "meta-wayland-seat.h"
+
+void
+meta_wayland_pointer_init (MetaWaylandPointer *pointer);
+
+void
+meta_wayland_pointer_release (MetaWaylandPointer *pointer);
+
+void
+meta_wayland_pointer_set_focus (MetaWaylandPointer *pointer,
+ MetaWaylandSurface *surface,
+ wl_fixed_t sx,
+ wl_fixed_t sy);
+void
+meta_wayland_pointer_start_grab (MetaWaylandPointer *pointer,
+ MetaWaylandPointerGrab *grab);
+
+void
+meta_wayland_pointer_end_grab (MetaWaylandPointer *pointer);
+
+void
+meta_wayland_pointer_set_current (MetaWaylandPointer *pointer,
+ MetaWaylandSurface *surface);
+
+#endif /* __META_WAYLAND_POINTER_H__ */
diff --git a/src/wayland/meta-wayland-private.h b/src/wayland/meta-wayland-private.h
index 59cc6d678..c33f7f943 100644
--- a/src/wayland/meta-wayland-private.h
+++ b/src/wayland/meta-wayland-private.h
@@ -21,7 +21,7 @@
#define META_WAYLAND_PRIVATE_H
#include <wayland-server.h>
-
+#include <xkbcommon/xkbcommon.h>
#include <clutter/clutter.h>
#include <glib.h>
@@ -31,6 +31,16 @@
typedef struct _MetaWaylandCompositor MetaWaylandCompositor;
+typedef struct _MetaWaylandSeat MetaWaylandSeat;
+typedef struct _MetaWaylandPointer MetaWaylandPointer;
+typedef struct _MetaWaylandPointerGrab MetaWaylandPointerGrab;
+typedef struct _MetaWaylandPointerGrabInterface MetaWaylandPointerGrabInterface;
+typedef struct _MetaWaylandKeyboard MetaWaylandKeyboard;
+typedef struct _MetaWaylandKeyboardGrab MetaWaylandKeyboardGrab;
+typedef struct _MetaWaylandKeyboardGrabInterface MetaWaylandKeyboardGrabInterface;
+typedef struct _MetaWaylandDataOffer MetaWaylandDataOffer;
+typedef struct _MetaWaylandDataSource MetaWaylandDataSource;
+
typedef struct
{
struct wl_resource *resource;
@@ -155,17 +165,198 @@ struct _MetaWaylandCompositor
struct wl_client *xwayland_client;
struct wl_resource *xserver_resource;
GHashTable *window_surfaces;
+
+ MetaWaylandSeat *seat;
+
+ /* This surface is only used to keep drag of the implicit grab when
+ synthesizing XEvents for Mutter */
+ MetaWaylandSurface *implicit_grab_surface;
+ /* Button that was pressed to initiate an implicit grab. The
+ implicit grab will only be released when this button is
+ released */
+ guint32 implicit_grab_button;
+};
+
+struct _MetaWaylandPointerGrabInterface
+{
+ void (*focus) (MetaWaylandPointerGrab * grab,
+ MetaWaylandSurface * surface, wl_fixed_t x, wl_fixed_t y);
+ void (*motion) (MetaWaylandPointerGrab * grab,
+ uint32_t time, wl_fixed_t x, wl_fixed_t y);
+ void (*button) (MetaWaylandPointerGrab * grab,
+ uint32_t time, uint32_t button, uint32_t state);
+};
+
+struct _MetaWaylandPointerGrab
+{
+ const MetaWaylandPointerGrabInterface *interface;
+ MetaWaylandPointer *pointer;
+ MetaWaylandSurface *focus;
+ wl_fixed_t x, y;
+};
+
+struct _MetaWaylandPointer
+{
+ struct wl_list resource_list;
+ MetaWaylandSurface *focus;
+ struct wl_resource *focus_resource;
+ struct wl_listener focus_listener;
+ guint32 focus_serial;
+ struct wl_signal focus_signal;
+
+ MetaWaylandPointerGrab *grab;
+ MetaWaylandPointerGrab default_grab;
+ wl_fixed_t grab_x, grab_y;
+ guint32 grab_button;
+ guint32 grab_serial;
+ guint32 grab_time;
+
+ wl_fixed_t x, y;
+ MetaWaylandSurface *current;
+ struct wl_listener current_listener;
+ wl_fixed_t current_x, current_y;
+
+ guint32 button_count;
};
-void meta_wayland_init (void);
-void meta_wayland_finalize (void);
+struct _MetaWaylandKeyboardGrabInterface
+{
+ void (*key) (MetaWaylandKeyboardGrab * grab, uint32_t time,
+ uint32_t key, uint32_t state);
+ void (*modifiers) (MetaWaylandKeyboardGrab * grab, uint32_t serial,
+ uint32_t mods_depressed, uint32_t mods_latched,
+ uint32_t mods_locked, uint32_t group);
+};
+
+struct _MetaWaylandKeyboardGrab
+{
+ const MetaWaylandKeyboardGrabInterface *interface;
+ MetaWaylandKeyboard *keyboard;
+ MetaWaylandSurface *focus;
+ uint32_t key;
+};
+
+typedef struct
+{
+ struct xkb_keymap *keymap;
+ int keymap_fd;
+ size_t keymap_size;
+ char *keymap_area;
+ xkb_mod_index_t shift_mod;
+ xkb_mod_index_t caps_mod;
+ xkb_mod_index_t ctrl_mod;
+ xkb_mod_index_t alt_mod;
+ xkb_mod_index_t mod2_mod;
+ xkb_mod_index_t mod3_mod;
+ xkb_mod_index_t super_mod;
+ xkb_mod_index_t mod5_mod;
+} MetaWaylandXkbInfo;
+
+struct _MetaWaylandKeyboard
+{
+ struct wl_list resource_list;
+ MetaWaylandSurface *focus;
+ struct wl_resource *focus_resource;
+ struct wl_listener focus_listener;
+ uint32_t focus_serial;
+ struct wl_signal focus_signal;
+
+ MetaWaylandKeyboardGrab *grab;
+ MetaWaylandKeyboardGrab default_grab;
+ uint32_t grab_key;
+ uint32_t grab_serial;
+ uint32_t grab_time;
+
+ struct wl_array keys;
+
+ struct
+ {
+ uint32_t mods_depressed;
+ uint32_t mods_latched;
+ uint32_t mods_locked;
+ uint32_t group;
+ } modifiers;
+
+ struct wl_display *display;
+
+ struct xkb_context *xkb_context;
+
+ MetaWaylandXkbInfo xkb_info;
+ struct xkb_rule_names xkb_names;
+
+ MetaWaylandKeyboardGrab input_method_grab;
+ struct wl_resource *input_method_resource;
+
+ ClutterModifierType last_modifier_state;
+};
+
+struct _MetaWaylandDataOffer
+{
+ struct wl_resource *resource;
+ MetaWaylandDataSource *source;
+ struct wl_listener source_destroy_listener;
+};
+
+struct _MetaWaylandDataSource
+{
+ struct wl_resource *resource;
+ struct wl_array mime_types;
+
+ void (*accept) (MetaWaylandDataSource * source,
+ uint32_t serial, const char *mime_type);
+ void (*send) (MetaWaylandDataSource * source,
+ const char *mime_type, int32_t fd);
+ void (*cancel) (MetaWaylandDataSource * source);
+};
+
+struct _MetaWaylandSeat
+{
+ struct wl_list base_resource_list;
+ struct wl_signal destroy_signal;
+
+ uint32_t selection_serial;
+ MetaWaylandDataSource *selection_data_source;
+ struct wl_listener selection_data_source_listener;
+ struct wl_signal selection_signal;
+
+ struct wl_list drag_resource_list;
+ struct wl_client *drag_client;
+ MetaWaylandDataSource *drag_data_source;
+ struct wl_listener drag_data_source_listener;
+ MetaWaylandSurface *drag_focus;
+ struct wl_resource *drag_focus_resource;
+ struct wl_listener drag_focus_listener;
+ MetaWaylandPointerGrab drag_grab;
+ MetaWaylandSurface *drag_surface;
+ struct wl_listener drag_icon_listener;
+ struct wl_signal drag_icon_signal;
+
+ MetaWaylandPointer pointer;
+ MetaWaylandKeyboard keyboard;
+
+ struct wl_display *display;
+
+ MetaWaylandSurface *sprite;
+ int hotspot_x, hotspot_y;
+ struct wl_listener sprite_destroy_listener;
+
+ ClutterActor *current_stage;
+};
+
+void meta_wayland_init (void);
+void meta_wayland_finalize (void);
/* We maintain a singleton MetaWaylandCompositor which can be got at via this
* API after meta_wayland_init() has been called. */
-MetaWaylandCompositor *meta_wayland_compositor_get_default (void);
+MetaWaylandCompositor *meta_wayland_compositor_get_default (void);
+
+void meta_wayland_handle_sig_child (void);
+
+MetaWaylandSurface *meta_wayland_lookup_surface_for_xid (guint32 xid);
-void meta_wayland_handle_sig_child (void);
+void meta_wayland_compositor_repick (MetaWaylandCompositor *compositor);
-MetaWaylandSurface *meta_wayland_lookup_surface_for_xid (guint32 xid);
+void meta_wayland_compositor_set_input_focus (MetaWaylandCompositor *compositor,
+ MetaWindow *window);
#endif /* META_WAYLAND_PRIVATE_H */
diff --git a/src/wayland/meta-wayland-seat.c b/src/wayland/meta-wayland-seat.c
new file mode 100644
index 000000000..fb78d0920
--- /dev/null
+++ b/src/wayland/meta-wayland-seat.c
@@ -0,0 +1,503 @@
+/*
+ * Wayland Support
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include <clutter/clutter.h>
+#include <clutter/wayland/clutter-wayland-compositor.h>
+#include <clutter/wayland/clutter-wayland-surface.h>
+#include <linux/input.h>
+#include <stdlib.h>
+#include <string.h>
+#include "meta-wayland-seat.h"
+#include "meta-wayland-private.h"
+#include "meta-wayland-keyboard.h"
+#include "meta-wayland-pointer.h"
+#include "meta-wayland-data-device.h"
+#include "meta-window-actor-private.h"
+#include "meta/meta-shaped-texture.h"
+
+#define DEFAULT_AXIS_STEP_DISTANCE wl_fixed_from_int (10)
+
+static void
+unbind_resource (struct wl_resource *resource)
+{
+ wl_list_remove (wl_resource_get_link (resource));
+}
+
+static void
+transform_stage_point_fixed (MetaWaylandSurface *surface,
+ wl_fixed_t x,
+ wl_fixed_t y,
+ wl_fixed_t *sx,
+ wl_fixed_t *sy)
+{
+ float xf = 0.0f, yf = 0.0f;
+
+ if (surface->window)
+ {
+ ClutterActor *actor =
+ CLUTTER_ACTOR (meta_window_get_compositor_private (surface->window));
+
+ if (actor)
+ clutter_actor_transform_stage_point (actor,
+ wl_fixed_to_double (x),
+ wl_fixed_to_double (y),
+ &xf, &yf);
+ }
+
+ *sx = wl_fixed_from_double (xf);
+ *sy = wl_fixed_from_double (yf);
+}
+
+static void
+pointer_unmap_sprite (MetaWaylandSeat *seat)
+{
+ if (seat->sprite)
+ {
+ if (seat->sprite->window)
+ {
+ GObject *window_actor_object =
+ meta_window_get_compositor_private (seat->sprite->window);
+ ClutterActor *window_actor = CLUTTER_ACTOR (window_actor_object);
+
+ if (window_actor)
+ clutter_actor_hide (window_actor);
+ }
+
+ wl_list_remove (&seat->sprite_destroy_listener.link);
+ seat->sprite = NULL;
+ }
+}
+
+static void
+pointer_set_cursor (struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t serial,
+ struct wl_resource *surface_resource,
+ int32_t x, int32_t y)
+{
+ MetaWaylandSeat *seat = wl_resource_get_user_data (resource);
+ MetaWaylandSurface *surface;
+
+ surface = (surface_resource ?
+ wl_resource_get_user_data (surface_resource) :
+ NULL);
+
+ if (seat->pointer.focus == NULL)
+ return;
+ if (wl_resource_get_client (seat->pointer.focus->resource) != client)
+ return;
+ if (seat->pointer.focus_serial - serial > G_MAXUINT32 / 2)
+ return;
+
+ pointer_unmap_sprite (seat);
+
+ if (!surface)
+ return;
+
+ wl_resource_add_destroy_listener (surface->resource,
+ &seat->sprite_destroy_listener);
+
+ seat->sprite = surface;
+ seat->hotspot_x = x;
+ seat->hotspot_y = y;
+}
+
+static const struct wl_pointer_interface
+pointer_interface =
+ {
+ pointer_set_cursor
+ };
+
+static void
+seat_get_pointer (struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t id)
+{
+ MetaWaylandSeat *seat = wl_resource_get_user_data (resource);
+ struct wl_resource *cr;
+
+ cr = wl_client_add_object (client, &wl_pointer_interface,
+ &pointer_interface, id, seat);
+ wl_list_insert (&seat->pointer.resource_list, wl_resource_get_link (cr));
+ wl_resource_set_destructor (cr, unbind_resource);
+
+ if (seat->pointer.focus &&
+ wl_resource_get_client (seat->pointer.focus->resource) == client)
+ {
+ MetaWaylandSurface *surface;
+ wl_fixed_t sx, sy;
+
+ surface = (MetaWaylandSurface *) seat->pointer.focus;
+ transform_stage_point_fixed (surface,
+ seat->pointer.x,
+ seat->pointer.y,
+ &sx, &sy);
+ meta_wayland_pointer_set_focus (&seat->pointer,
+ seat->pointer.focus,
+ sx, sy);
+ }
+}
+
+static void
+seat_get_keyboard (struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t id)
+{
+ MetaWaylandSeat *seat = wl_resource_get_user_data (resource);
+ struct wl_resource *cr;
+
+ cr = wl_client_add_object (client, &wl_keyboard_interface, NULL, id, seat);
+ wl_list_insert (&seat->keyboard.resource_list, wl_resource_get_link (cr));
+ wl_resource_set_destructor (cr, unbind_resource);
+
+ wl_keyboard_send_keymap (cr,
+ WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
+ seat->keyboard.xkb_info.keymap_fd,
+ seat->keyboard.xkb_info.keymap_size);
+
+ if (seat->keyboard.focus &&
+ wl_resource_get_client (seat->keyboard.focus->resource) == client)
+ {
+ meta_wayland_keyboard_set_focus (&seat->keyboard,
+ seat->keyboard.focus);
+ meta_wayland_data_device_set_keyboard_focus (seat);
+ }
+}
+
+static void
+seat_get_touch (struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t id)
+{
+ /* Touch not supported */
+}
+
+static const struct wl_seat_interface
+seat_interface =
+ {
+ seat_get_pointer,
+ seat_get_keyboard,
+ seat_get_touch
+ };
+
+static void
+bind_seat (struct wl_client *client,
+ void *data,
+ guint32 version,
+ guint32 id)
+{
+ MetaWaylandSeat *seat = data;
+ struct wl_resource *resource;
+
+ resource = wl_client_add_object (client,
+ &wl_seat_interface,
+ &seat_interface,
+ id,
+ data);
+ wl_list_insert (&seat->base_resource_list, wl_resource_get_link (resource));
+ wl_resource_set_destructor (resource, unbind_resource);
+
+ wl_seat_send_capabilities (resource,
+ WL_SEAT_CAPABILITY_POINTER |
+ WL_SEAT_CAPABILITY_KEYBOARD);
+}
+
+static void
+pointer_handle_sprite_destroy (struct wl_listener *listener, void *data)
+{
+ MetaWaylandSeat *seat =
+ wl_container_of (listener, seat, sprite_destroy_listener);
+
+ seat->sprite = NULL;
+}
+
+MetaWaylandSeat *
+meta_wayland_seat_new (struct wl_display *display)
+{
+ MetaWaylandSeat *seat = g_new0 (MetaWaylandSeat, 1);
+
+ wl_signal_init (&seat->destroy_signal);
+
+ seat->selection_data_source = NULL;
+ wl_list_init (&seat->base_resource_list);
+ wl_signal_init (&seat->selection_signal);
+ wl_list_init (&seat->drag_resource_list);
+ wl_signal_init (&seat->drag_icon_signal);
+
+ meta_wayland_pointer_init (&seat->pointer);
+
+ meta_wayland_keyboard_init (&seat->keyboard, display);
+
+ seat->display = display;
+
+ seat->current_stage = 0;
+
+ seat->sprite = NULL;
+ seat->sprite_destroy_listener.notify = pointer_handle_sprite_destroy;
+ seat->hotspot_x = 16;
+ seat->hotspot_y = 16;
+
+ wl_display_add_global (display, &wl_seat_interface, seat, bind_seat);
+
+ return seat;
+}
+
+static void
+notify_motion (MetaWaylandSeat *seat,
+ const ClutterEvent *event)
+{
+ MetaWaylandPointer *pointer = &seat->pointer;
+ float x, y;
+
+ clutter_event_get_coords (event, &x, &y);
+ pointer->x = wl_fixed_from_double (x);
+ pointer->y = wl_fixed_from_double (y);
+
+ meta_wayland_seat_repick (seat,
+ clutter_event_get_time (event),
+ clutter_event_get_source (event));
+
+ pointer->grab->interface->motion (pointer->grab,
+ clutter_event_get_time (event),
+ pointer->grab->x,
+ pointer->grab->y);
+}
+
+static void
+handle_motion_event (MetaWaylandSeat *seat,
+ const ClutterMotionEvent *event)
+{
+ notify_motion (seat, (const ClutterEvent *) event);
+}
+
+static void
+handle_button_event (MetaWaylandSeat *seat,
+ const ClutterButtonEvent *event)
+{
+ MetaWaylandPointer *pointer = &seat->pointer;
+ gboolean state = event->type == CLUTTER_BUTTON_PRESS;
+ uint32_t button;
+
+ notify_motion (seat, (const ClutterEvent *) event);
+
+ switch (event->button)
+ {
+ /* The evdev input right and middle button numbers are swapped
+ relative to how Clutter numbers them */
+ case 2:
+ button = BTN_MIDDLE;
+ break;
+
+ case 3:
+ button = BTN_RIGHT;
+ break;
+
+ default:
+ button = event->button + BTN_LEFT - 1;
+ break;
+ }
+
+ if (state)
+ {
+ if (pointer->button_count == 0)
+ {
+ pointer->grab_button = button;
+ pointer->grab_time = event->time;
+ pointer->grab_x = pointer->x;
+ pointer->grab_y = pointer->y;
+ }
+
+ pointer->button_count++;
+ }
+ else
+ pointer->button_count--;
+
+ pointer->grab->interface->button (pointer->grab, event->time, button, state);
+
+ if (pointer->button_count == 1)
+ pointer->grab_serial = wl_display_get_serial (seat->display);
+}
+
+static void
+handle_scroll_event (MetaWaylandSeat *seat,
+ const ClutterScrollEvent *event)
+{
+ enum wl_pointer_axis axis;
+ wl_fixed_t value;
+
+ notify_motion (seat, (const ClutterEvent *) event);
+
+ switch (event->direction)
+ {
+ case CLUTTER_SCROLL_UP:
+ axis = WL_POINTER_AXIS_VERTICAL_SCROLL;
+ value = -DEFAULT_AXIS_STEP_DISTANCE;
+ break;
+
+ case CLUTTER_SCROLL_DOWN:
+ axis = WL_POINTER_AXIS_VERTICAL_SCROLL;
+ value = DEFAULT_AXIS_STEP_DISTANCE;
+ break;
+
+ case CLUTTER_SCROLL_LEFT:
+ axis = WL_POINTER_AXIS_HORIZONTAL_SCROLL;
+ value = -DEFAULT_AXIS_STEP_DISTANCE;
+ break;
+
+ case CLUTTER_SCROLL_RIGHT:
+ axis = WL_POINTER_AXIS_HORIZONTAL_SCROLL;
+ value = DEFAULT_AXIS_STEP_DISTANCE;
+ break;
+
+ default:
+ return;
+ }
+
+ if (seat->pointer.focus_resource)
+ wl_pointer_send_axis (seat->pointer.focus_resource,
+ event->time,
+ axis,
+ value);
+}
+
+void
+meta_wayland_seat_handle_event (MetaWaylandSeat *seat,
+ const ClutterEvent *event)
+{
+ switch (event->type)
+ {
+ case CLUTTER_MOTION:
+ handle_motion_event (seat,
+ (const ClutterMotionEvent *) event);
+ break;
+
+ case CLUTTER_BUTTON_PRESS:
+ case CLUTTER_BUTTON_RELEASE:
+ handle_button_event (seat,
+ (const ClutterButtonEvent *) event);
+ break;
+
+ case CLUTTER_KEY_PRESS:
+ case CLUTTER_KEY_RELEASE:
+ meta_wayland_keyboard_handle_event (&seat->keyboard,
+ (const ClutterKeyEvent *) event);
+ break;
+
+ case CLUTTER_SCROLL:
+ handle_scroll_event (seat, (const ClutterScrollEvent *) event);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void
+update_pointer_position_for_actor (MetaWaylandPointer *pointer,
+ ClutterActor *actor)
+{
+ float ax, ay;
+
+ clutter_actor_transform_stage_point (actor,
+ wl_fixed_to_double (pointer->x),
+ wl_fixed_to_double (pointer->y),
+ &ax, &ay);
+ pointer->current_x = wl_fixed_from_double (ax);
+ pointer->current_y = wl_fixed_from_double (ay);
+}
+
+/* The actor argument can be NULL in which case a Clutter pick will be
+ performed to determine the right actor. An actor should only be
+ passed if the repick is being performed due to an event in which
+ case Clutter will have already performed a pick so we can avoid
+ redundantly doing another one */
+void
+meta_wayland_seat_repick (MetaWaylandSeat *seat,
+ uint32_t time,
+ ClutterActor *actor)
+{
+ MetaWaylandPointer *pointer = &seat->pointer;
+ MetaWaylandSurface *surface = NULL;
+
+ if (actor == NULL && seat->current_stage)
+ {
+ ClutterStage *stage = CLUTTER_STAGE (seat->current_stage);
+ actor = clutter_stage_get_actor_at_pos (stage,
+ CLUTTER_PICK_REACTIVE,
+ wl_fixed_to_double (pointer->x),
+ wl_fixed_to_double (pointer->y));
+ }
+
+ if (actor)
+ seat->current_stage = clutter_actor_get_stage (actor);
+ else
+ seat->current_stage = NULL;
+
+ if (META_IS_WINDOW_ACTOR (actor))
+ {
+ MetaWindow *window =
+ meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor));
+
+ update_pointer_position_for_actor (pointer, actor);
+
+ surface = window->surface;
+ }
+ else if (META_IS_SHAPED_TEXTURE (actor))
+ {
+ MetaShapedTexture *shaped_texture = META_SHAPED_TEXTURE (actor);
+
+ update_pointer_position_for_actor (pointer, actor);
+
+ surface = meta_shaped_texture_get_wayland_surface (shaped_texture);
+ }
+
+ if (surface != pointer->current)
+ {
+ const MetaWaylandPointerGrabInterface *interface =
+ pointer->grab->interface;
+ interface->focus (pointer->grab,
+ surface,
+ pointer->current_x, pointer->current_y);
+ pointer->current = surface;
+ }
+
+ if (pointer->grab->focus)
+ transform_stage_point_fixed (pointer->grab->focus,
+ pointer->x,
+ pointer->y,
+ &pointer->grab->x,
+ &pointer->grab->y);
+}
+
+void
+meta_wayland_seat_free (MetaWaylandSeat *seat)
+{
+ pointer_unmap_sprite (seat);
+
+ meta_wayland_pointer_release (&seat->pointer);
+ meta_wayland_keyboard_release (&seat->keyboard);
+
+ wl_signal_emit (&seat->destroy_signal, seat);
+
+ g_slice_free (MetaWaylandSeat, seat);
+}
diff --git a/src/wayland/meta-wayland-seat.h b/src/wayland/meta-wayland-seat.h
new file mode 100644
index 000000000..ee567785e
--- /dev/null
+++ b/src/wayland/meta-wayland-seat.h
@@ -0,0 +1,47 @@
+/*
+ * Wayland Support
+ *
+ * Copyright (C) 2012 Intel Corporation
+ *
+ * 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.
+ */
+
+#ifndef __META_WAYLAND_SEAT_H__
+#define __META_WAYLAND_SEAT_H__
+
+#include <wayland-server.h>
+#include <xkbcommon/xkbcommon.h>
+#include <clutter/clutter.h>
+#include <glib.h>
+
+#include "meta-wayland-private.h"
+
+MetaWaylandSeat *
+meta_wayland_seat_new (struct wl_display *display);
+
+void
+meta_wayland_seat_handle_event (MetaWaylandSeat *seat,
+ const ClutterEvent *event);
+
+void
+meta_wayland_seat_repick (MetaWaylandSeat *seat,
+ uint32_t time,
+ ClutterActor *actor);
+
+void
+meta_wayland_seat_free (MetaWaylandSeat *seat);
+
+#endif /* __META_WAYLAND_SEAT_H__ */
diff --git a/src/wayland/meta-wayland.c b/src/wayland/meta-wayland.c
index 437406818..c364de0e4 100644
--- a/src/wayland/meta-wayland.c
+++ b/src/wayland/meta-wayland.c
@@ -40,6 +40,9 @@
#include "meta-wayland-private.h"
#include "meta-xwayland-private.h"
#include "meta-window-actor-private.h"
+#include "meta-wayland-seat.h"
+#include "meta-wayland-keyboard.h"
+#include "meta-wayland-data-device.h"
#include "display-private.h"
#include "window-private.h"
#include <meta/types.h>
@@ -414,6 +417,17 @@ const struct wl_surface_interface meta_wayland_surface_interface = {
meta_wayland_surface_set_buffer_scale
};
+void
+meta_wayland_compositor_set_input_focus (MetaWaylandCompositor *compositor,
+ MetaWindow *window)
+{
+ MetaWaylandSurface *surface = window ? window->surface : NULL;
+
+ meta_wayland_keyboard_set_focus (&compositor->seat->keyboard,
+ surface);
+ meta_wayland_data_device_set_keyboard_focus (compositor->seat);
+}
+
static void
window_destroyed_cb (void *user_data, GObject *old_object)
{
@@ -422,6 +436,14 @@ window_destroyed_cb (void *user_data, GObject *old_object)
surface->window = NULL;
}
+void
+meta_wayland_compositor_repick (MetaWaylandCompositor *compositor)
+{
+ meta_wayland_seat_repick (compositor->seat,
+ get_time (),
+ NULL);
+}
+
static void
meta_wayland_surface_free (MetaWaylandSurface *surface)
{
@@ -457,6 +479,11 @@ meta_wayland_surface_free (MetaWaylandSurface *surface)
wl_resource_destroy (cb->resource);
g_slice_free (MetaWaylandSurface, surface);
+
+ meta_wayland_compositor_repick (compositor);
+
+ if (compositor->implicit_grab_surface == surface)
+ compositor->implicit_grab_surface = compositor->seat->pointer.current;
}
static void
@@ -962,6 +989,14 @@ xserver_set_window_id (struct wl_client *client,
g_object_weak_ref (G_OBJECT (surface->window),
window_destroyed_cb,
surface);
+
+ /* If the window is already meant to have focus then the
+ * original attempt to call this in response to the FocusIn
+ * event will have been lost because there was no surface
+ * yet. */
+ if (window->has_focus)
+ meta_wayland_compositor_set_input_focus (compositor, window);
+
}
#warning "FIXME: Handle surface destroy and remove window_surfaces mapping"
}
@@ -1022,10 +1057,236 @@ stage_destroy_cb (void)
meta_quit (META_EXIT_SUCCESS);
}
+#define N_BUTTONS 5
+
+static void
+synthesize_motion_event (MetaWaylandCompositor *compositor,
+ const ClutterEvent *event)
+{
+ /* We want to synthesize X events for mouse motion events so that we
+ don't have to rely on the X server's window position being
+ synched with the surface position. See the comment in
+ event_callback() in display.c */
+ MetaWaylandSeat *seat = compositor->seat;
+ MetaWaylandPointer *pointer = &seat->pointer;
+ MetaWaylandSurface *surface;
+ XGenericEventCookie generic_event;
+ XIDeviceEvent device_event;
+ unsigned char button_mask[(N_BUTTONS + 7) / 8] = { 0 };
+ MetaDisplay *display = meta_get_display ();
+ ClutterModifierType state;
+ int i;
+
+ generic_event.type = GenericEvent;
+ generic_event.serial = 0;
+ generic_event.send_event = False;
+ generic_event.display = display->xdisplay;
+ generic_event.extension = display->xinput_opcode;
+ generic_event.evtype = XI_Motion;
+ /* Mutter assumes the data for the event is already retrieved by GDK
+ * so we don't need the cookie */
+ generic_event.cookie = 0;
+ generic_event.data = &device_event;
+
+ memcpy (&device_event, &generic_event, sizeof (XGenericEvent));
+
+ device_event.time = clutter_event_get_time (event);
+ device_event.deviceid = clutter_event_get_device_id (event);
+ device_event.sourceid = 0; /* not used, not sure what this should be */
+ device_event.detail = 0;
+ device_event.root = DefaultRootWindow (display->xdisplay);
+ device_event.flags = 0 /* not used for motion events */;
+
+ if (compositor->implicit_grab_surface)
+ surface = compositor->implicit_grab_surface;
+ else
+ surface = pointer->current;
+
+ if (surface == pointer->current)
+ {
+ device_event.event_x = wl_fixed_to_int (pointer->current_x);
+ device_event.event_y = wl_fixed_to_int (pointer->current_y);
+ }
+ else if (surface && surface->window)
+ {
+ ClutterActor *window_actor =
+ CLUTTER_ACTOR (meta_window_get_compositor_private (surface->window));
+
+ if (window_actor)
+ {
+ float ax, ay;
+
+ clutter_actor_transform_stage_point (window_actor,
+ wl_fixed_to_double (pointer->x),
+ wl_fixed_to_double (pointer->y),
+ &ax, &ay);
+
+ device_event.event_x = ax;
+ device_event.event_y = ay;
+ }
+ else
+ {
+ device_event.event_x = wl_fixed_to_double (pointer->x);
+ device_event.event_y = wl_fixed_to_double (pointer->y);
+ }
+ }
+ else
+ {
+ device_event.event_x = wl_fixed_to_double (pointer->x);
+ device_event.event_y = wl_fixed_to_double (pointer->y);
+ }
+
+ if (surface && surface->xid != None)
+ device_event.event = surface->xid;
+ else
+ device_event.event = device_event.root;
+
+ /* Mutter doesn't really know about the sub-windows. This assumes it
+ doesn't care either */
+ device_event.child = device_event.event;
+ device_event.root_x = wl_fixed_to_double (pointer->x);
+ device_event.root_y = wl_fixed_to_double (pointer->y);
+
+ state = clutter_event_get_state (event);
+
+ for (i = 0; i < N_BUTTONS; i++)
+ if ((state & (CLUTTER_BUTTON1_MASK << i)))
+ XISetMask (button_mask, i + 1);
+ device_event.buttons.mask_len = N_BUTTONS + 1;
+ device_event.buttons.mask = button_mask;
+
+ device_event.valuators.mask_len = 0;
+ device_event.valuators.mask = NULL;
+ device_event.valuators.values = NULL;
+
+ memset (&device_event.mods, 0, sizeof (device_event.mods));
+ device_event.mods.effective =
+ state & (CLUTTER_MODIFIER_MASK &
+ ~(((CLUTTER_BUTTON1_MASK << N_BUTTONS) - 1) ^
+ (CLUTTER_BUTTON1_MASK - 1)));
+
+ memset (&device_event.group, 0, sizeof (device_event.group));
+
+ meta_display_handle_event (display, (XEvent *) &generic_event);
+}
+
+static gboolean
+event_cb (ClutterActor *stage,
+ const ClutterEvent *event,
+ MetaWaylandCompositor *compositor)
+{
+ MetaWaylandSeat *seat = compositor->seat;
+ MetaWaylandPointer *pointer = &seat->pointer;
+ MetaWaylandSurface *surface;
+ MetaDisplay *display;
+
+ meta_wayland_seat_handle_event (compositor->seat, event);
+
+ /* HACK: for now, the surfaces from Wayland clients aren't
+ integrated into Mutter's stacking and Mutter won't give them
+ focus on mouse clicks. As a hack to work around this we can just
+ give them input focus on mouse clicks so we can at least test the
+ keyboard support */
+ if (event->type == CLUTTER_BUTTON_PRESS)
+ {
+ surface = pointer->current;
+
+ /* Only focus surfaces that wouldn't be handled by the
+ corresponding X events */
+ if (surface && surface->xid == 0)
+ {
+ meta_wayland_keyboard_set_focus (&seat->keyboard, surface);
+ meta_wayland_data_device_set_keyboard_focus (seat);
+ }
+ }
+
+ display = meta_get_display ();
+ if (!display)
+ return FALSE;
+
+ switch (event->type)
+ {
+ case CLUTTER_BUTTON_PRESS:
+ if (compositor->implicit_grab_surface == NULL)
+ {
+ compositor->implicit_grab_button = event->button.button;
+ compositor->implicit_grab_surface = pointer->current;
+ }
+ return FALSE;
+
+ case CLUTTER_BUTTON_RELEASE:
+ if (event->type == CLUTTER_BUTTON_RELEASE &&
+ compositor->implicit_grab_surface &&
+ event->button.button == compositor->implicit_grab_button)
+ compositor->implicit_grab_surface = NULL;
+ return FALSE;
+
+ case CLUTTER_MOTION:
+ synthesize_motion_event (compositor, event);
+ return FALSE;
+
+ default:
+ return FALSE;
+ }
+}
+
+static gboolean
+event_emission_hook_cb (GSignalInvocationHint *ihint,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer data)
+{
+ MetaWaylandCompositor *compositor = data;
+ ClutterActor *actor;
+ ClutterEvent *event;
+
+ g_return_val_if_fail (n_param_values == 2, FALSE);
+
+ actor = g_value_get_object (param_values + 0);
+ event = g_value_get_boxed (param_values + 1);
+
+ if (actor == NULL)
+ return TRUE /* stay connected */;
+
+ /* If this event belongs to the corresponding grab for this event
+ * type then the captured-event signal won't be emitted so we have
+ * to manually forward it on */
+
+ switch (event->type)
+ {
+ /* Pointer events */
+ case CLUTTER_MOTION:
+ case CLUTTER_ENTER:
+ case CLUTTER_LEAVE:
+ case CLUTTER_BUTTON_PRESS:
+ case CLUTTER_BUTTON_RELEASE:
+ case CLUTTER_SCROLL:
+ if (actor == clutter_get_pointer_grab ())
+ event_cb (clutter_actor_get_stage (actor),
+ event,
+ compositor);
+ break;
+
+ /* Keyboard events */
+ case CLUTTER_KEY_PRESS:
+ case CLUTTER_KEY_RELEASE:
+ if (actor == clutter_get_keyboard_grab ())
+ event_cb (clutter_actor_get_stage (actor),
+ event,
+ compositor);
+
+ default:
+ break;
+ }
+
+ return TRUE /* stay connected */;
+}
+
void
meta_wayland_init (void)
{
MetaWaylandCompositor *compositor = &_meta_wayland_compositor;
+ guint event_signal;
memset (compositor, 0, sizeof (MetaWaylandCompositor));
@@ -1072,6 +1333,25 @@ meta_wayland_init (void)
g_signal_connect (compositor->stage, "destroy",
G_CALLBACK (stage_destroy_cb), NULL);
+ meta_wayland_data_device_manager_init (compositor->wayland_display);
+
+ compositor->seat = meta_wayland_seat_new (compositor->wayland_display);
+
+ g_signal_connect (compositor->stage,
+ "captured-event",
+ G_CALLBACK (event_cb),
+ compositor);
+ /* If something sets a grab on an actor then the captured event
+ * signal won't get emitted but we still want to see these events so
+ * we can update the cursor position. To make sure we see all events
+ * we also install an emission hook on the event signal */
+ event_signal = g_signal_lookup ("event", CLUTTER_TYPE_STAGE);
+ g_signal_add_emission_hook (event_signal,
+ 0 /* detail */,
+ event_emission_hook_cb,
+ compositor, /* hook_data */
+ NULL /* data_destroy */);
+
meta_wayland_compositor_create_output (compositor, 0, 0, 1024, 600, 222, 125);
if (wl_global_create (compositor->wayland_display,