summaryrefslogtreecommitdiff
path: root/src/wayland
diff options
context:
space:
mode:
Diffstat (limited to 'src/wayland')
-rw-r--r--src/wayland/meta-wayland-keyboard.c395
-rw-r--r--src/wayland/meta-wayland-keyboard.h97
-rw-r--r--src/wayland/meta-wayland-private.h27
-rw-r--r--src/wayland/meta-wayland-seat.c444
-rw-r--r--src/wayland/meta-wayland-seat.h44
-rw-r--r--src/wayland/meta-wayland.c233
6 files changed, 1235 insertions, 5 deletions
diff --git a/src/wayland/meta-wayland-keyboard.c b/src/wayland/meta-wayland-keyboard.c
new file mode 100644
index 000000000..533d5ae4b
--- /dev/null
+++ b/src/wayland/meta-wayland-keyboard.c
@@ -0,0 +1,395 @@
+/*
+ * 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.
+ */
+
+#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"
+
+#ifndef HAVE_MKOSTEMP
+
+static int
+set_cloexec_or_close (int fd)
+{
+ long flags;
+
+ if (fd == -1)
+ return -1;
+
+ flags = fcntl (fd, F_GETFD);
+ if (flags == -1)
+ goto err;
+
+ if (fcntl (fd, F_SETFD, flags | FD_CLOEXEC) == -1)
+ goto err;
+
+ return fd;
+
+err:
+ close (fd);
+ return -1;
+}
+
+#endif /* HAVE_MKOSTEMP */
+
+static int
+create_tmpfile_cloexec (char *tmpname)
+{
+ int fd;
+
+#ifdef HAVE_MKOSTEMP
+ fd = mkostemp (tmpname, O_CLOEXEC);
+ if (fd >= 0)
+ unlink (tmpname);
+#else
+ fd = mkstemp (tmpname);
+ if (fd >= 0)
+ {
+ fd = set_cloexec_or_close (fd);
+ unlink (tmpname);
+ }
+#endif
+
+ return fd;
+}
+
+static int
+create_anonymous_file (off_t size,
+ GError **error)
+{
+ static const char template[] = "weston-shared-XXXXXX";
+ const char *path;
+ char *name;
+ int fd;
+
+ path = g_getenv ("XDG_RUNTIME_DIR");
+ if (!path)
+ {
+ errno = ENOENT;
+ return -1;
+ }
+
+ name = g_build_filename (path, template, NULL);
+
+ fd = create_tmpfile_cloexec (name);
+
+ free (name);
+
+ if (fd < 0)
+ return -1;
+
+ if (ftruncate (fd, size) < 0)
+ {
+ close (fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+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;
+}
+
+gboolean
+meta_wayland_keyboard_init (MetaWaylandKeyboard *keyboard,
+ struct wl_display *display)
+{
+ wl_keyboard_init (&keyboard->parent);
+
+ keyboard->display = display;
+
+ memset (&keyboard->xkb_names, 0, sizeof (keyboard->xkb_names));
+
+ 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 *meta_wayland_keyboard,
+ guint32 serial,
+ ClutterModifierType modifier_state)
+{
+ struct wl_keyboard *keyboard = &meta_wayland_keyboard->parent;
+ struct wl_keyboard_grab *grab = keyboard->grab;
+ uint32_t depressed_mods = 0;
+ uint32_t locked_mods = 0;
+
+ if (meta_wayland_keyboard->last_modifier_state == modifier_state)
+ return;
+
+ if ((modifier_state & CLUTTER_SHIFT_MASK) &&
+ meta_wayland_keyboard->xkb_info.shift_mod != XKB_MOD_INVALID)
+ depressed_mods |= (1 << meta_wayland_keyboard->xkb_info.shift_mod);
+
+ if ((modifier_state & CLUTTER_LOCK_MASK) &&
+ meta_wayland_keyboard->xkb_info.caps_mod != XKB_MOD_INVALID)
+ locked_mods |= (1 << meta_wayland_keyboard->xkb_info.caps_mod);
+
+ if ((modifier_state & CLUTTER_CONTROL_MASK) &&
+ meta_wayland_keyboard->xkb_info.ctrl_mod != XKB_MOD_INVALID)
+ depressed_mods |= (1 << meta_wayland_keyboard->xkb_info.ctrl_mod);
+
+ if ((modifier_state & CLUTTER_MOD1_MASK) &&
+ meta_wayland_keyboard->xkb_info.alt_mod != XKB_MOD_INVALID)
+ depressed_mods |= (1 << meta_wayland_keyboard->xkb_info.alt_mod);
+
+ if ((modifier_state & CLUTTER_MOD2_MASK) &&
+ meta_wayland_keyboard->xkb_info.mod2_mod != XKB_MOD_INVALID)
+ depressed_mods |= (1 << meta_wayland_keyboard->xkb_info.mod2_mod);
+
+ if ((modifier_state & CLUTTER_MOD3_MASK) &&
+ meta_wayland_keyboard->xkb_info.mod3_mod != XKB_MOD_INVALID)
+ depressed_mods |= (1 << meta_wayland_keyboard->xkb_info.mod3_mod);
+
+ if ((modifier_state & CLUTTER_SUPER_MASK) &&
+ meta_wayland_keyboard->xkb_info.super_mod != XKB_MOD_INVALID)
+ depressed_mods |= (1 << meta_wayland_keyboard->xkb_info.super_mod);
+
+ if ((modifier_state & CLUTTER_MOD5_MASK) &&
+ meta_wayland_keyboard->xkb_info.mod5_mod != XKB_MOD_INVALID)
+ depressed_mods |= (1 << meta_wayland_keyboard->xkb_info.mod5_mod);
+
+ meta_wayland_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 *meta_wayland_keyboard,
+ const ClutterKeyEvent *event)
+{
+ struct wl_keyboard *keyboard = &meta_wayland_keyboard->parent;
+ 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 (meta_wayland_keyboard->display);
+
+ set_modifiers (meta_wayland_keyboard, serial, event->modifier_state);
+
+ keyboard->grab->interface->key (keyboard->grab,
+ event->time,
+ evdev_code,
+ state);
+}
+
+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);
+
+ wl_keyboard_release (&keyboard->parent);
+}
diff --git a/src/wayland/meta-wayland-keyboard.h b/src/wayland/meta-wayland-keyboard.h
new file mode 100644
index 000000000..3bf6bccc2
--- /dev/null
+++ b/src/wayland/meta-wayland-keyboard.h
@@ -0,0 +1,97 @@
+/*
+ * 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 <xkbcommon/xkbcommon.h>
+
+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;
+
+typedef struct
+{
+ struct wl_keyboard parent;
+
+ struct wl_display *display;
+
+ struct xkb_context *xkb_context;
+
+ MetaWaylandXkbInfo xkb_info;
+ struct xkb_rule_names xkb_names;
+
+ struct wl_keyboard_grab input_method_grab;
+ struct wl_resource *input_method_resource;
+
+ ClutterModifierType last_modifier_state;
+} MetaWaylandKeyboard;
+
+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_release (MetaWaylandKeyboard *keyboard);
+
+#endif /* __META_WAYLAND_KEYBOARD_H__ */
diff --git a/src/wayland/meta-wayland-private.h b/src/wayland/meta-wayland-private.h
index 5b7e8e1c5..905f732cd 100644
--- a/src/wayland/meta-wayland-private.h
+++ b/src/wayland/meta-wayland-private.h
@@ -29,6 +29,8 @@
#include "window-private.h"
+#include "meta-wayland-seat.h"
+
typedef struct _MetaWaylandCompositor MetaWaylandCompositor;
typedef struct
@@ -135,17 +137,32 @@ 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 */
+ struct wl_surface *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;
};
-void meta_wayland_init (void);
-void meta_wayland_finalize (void);
+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..e53637904
--- /dev/null
+++ b/src/wayland/meta-wayland-seat.c
@@ -0,0 +1,444 @@
+/*
+ * 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-keyboard.h"
+#include "meta-wayland-private.h"
+
+struct _MetaWaylandSeat
+{
+ struct wl_seat parent;
+
+ struct wl_pointer pointer;
+ MetaWaylandKeyboard keyboard;
+
+ struct wl_display *display;
+
+ MetaWaylandSurface *sprite;
+ int hotspot_x, hotspot_y;
+ struct wl_listener sprite_destroy_listener;
+
+ ClutterActor *current_stage;
+};
+
+static void
+unbind_resource (struct wl_resource *resource)
+{
+ wl_list_remove (&resource->link);
+ free (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, yf;
+
+ clutter_actor_transform_stage_point (surface->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->actor)
+ clutter_actor_hide (seat->sprite->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 = resource->data;
+ MetaWaylandSurface *surface;
+
+ surface = surface_resource ? surface_resource->data : NULL;
+
+ if (seat->parent.pointer->focus == NULL)
+ return;
+ if (seat->parent.pointer->focus->resource.client != client)
+ return;
+ if (seat->parent.pointer->focus_serial - serial > G_MAXUINT32 / 2)
+ return;
+
+ pointer_unmap_sprite (seat);
+
+ if (!surface)
+ return;
+
+ wl_signal_add (&surface->wayland_surface.resource.destroy_signal,
+ &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 = resource->data;
+ struct wl_resource *cr;
+
+ if (!seat->parent.pointer)
+ return;
+
+ cr = wl_client_add_object (client, &wl_pointer_interface,
+ &pointer_interface, id, seat);
+ wl_list_insert (&seat->parent.pointer->resource_list, &cr->link);
+ cr->destroy = unbind_resource;
+
+ if (seat->parent.pointer->focus &&
+ seat->parent.pointer->focus->resource.client == client)
+ {
+ MetaWaylandSurface *surface;
+ wl_fixed_t sx, sy;
+
+ surface = (MetaWaylandSurface *) seat->parent.pointer->focus;
+ transform_stage_point_fixed (surface,
+ seat->parent.pointer->x,
+ seat->parent.pointer->y,
+ &sx, &sy);
+ wl_pointer_set_focus (seat->parent.pointer,
+ seat->parent.pointer->focus,
+ sx, sy);
+ }
+}
+
+static void
+seat_get_keyboard (struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t id)
+{
+ MetaWaylandSeat *seat = resource->data;
+ struct wl_resource *cr;
+
+ if (!seat->parent.keyboard)
+ return;
+
+ cr = wl_client_add_object (client, &wl_keyboard_interface, NULL, id, seat);
+ wl_list_insert (&seat->parent.keyboard->resource_list, &cr->link);
+ cr->destroy = 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->parent.keyboard->focus &&
+ seat->parent.keyboard->focus->resource.client == client)
+ {
+ wl_keyboard_set_focus (seat->parent.keyboard,
+ seat->parent.keyboard->focus);
+ wl_data_device_set_keyboard_focus (&seat->parent);
+ }
+}
+
+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)
+{
+ struct wl_seat *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, &resource->link);
+ resource->destroy = 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_new (MetaWaylandSeat, 1);
+
+ wl_seat_init (&seat->parent);
+
+ wl_pointer_init (&seat->pointer);
+ wl_seat_set_pointer (&seat->parent, &seat->pointer);
+
+ meta_wayland_keyboard_init (&seat->keyboard, display);
+ wl_seat_set_keyboard (&seat->parent, &seat->keyboard.parent);
+
+ 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)
+{
+ struct wl_pointer *pointer = seat->parent.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)
+{
+ struct wl_pointer *pointer = seat->parent.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);
+}
+
+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;
+
+ default:
+ break;
+ }
+}
+
+/* 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)
+{
+ struct wl_pointer *pointer = seat->parent.pointer;
+ struct wl_surface *surface;
+ MetaWaylandSurface *focus;
+
+ 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 (CLUTTER_WAYLAND_IS_SURFACE (actor))
+ {
+ ClutterWaylandSurface *wl_surface = CLUTTER_WAYLAND_SURFACE (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);
+
+ surface = clutter_wayland_surface_get_surface (wl_surface);
+ }
+ else
+ surface = NULL;
+
+ if (surface != pointer->current)
+ {
+ const struct wl_pointer_grab_interface *interface =
+ pointer->grab->interface;
+ interface->focus (pointer->grab,
+ surface,
+ pointer->current_x, pointer->current_y);
+ pointer->current = surface;
+ }
+
+ focus = (MetaWaylandSurface *) pointer->grab->focus;
+ if (focus)
+ {
+ float ax, ay;
+
+ clutter_actor_transform_stage_point (focus->actor,
+ wl_fixed_to_double (pointer->x),
+ wl_fixed_to_double (pointer->y),
+ &ax, &ay);
+ pointer->grab->x = wl_fixed_from_double (ax);
+ pointer->grab->y = wl_fixed_from_double (ay);
+ }
+}
+
+void
+meta_wayland_seat_free (MetaWaylandSeat *seat)
+{
+ pointer_unmap_sprite (seat);
+
+ wl_pointer_release (&seat->pointer);
+ meta_wayland_keyboard_release (&seat->keyboard);
+ wl_seat_release (&seat->parent);
+
+ 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..0f175f7ad
--- /dev/null
+++ b/src/wayland/meta-wayland-seat.h
@@ -0,0 +1,44 @@
+/*
+ * 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>
+
+typedef struct _MetaWaylandSeat MetaWaylandSeat;
+
+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 5f481867d..673189d20 100644
--- a/src/wayland/meta-wayland.c
+++ b/src/wayland/meta-wayland.c
@@ -44,6 +44,7 @@
#include "meta-wayland-private.h"
#include "meta-window-actor-private.h"
+#include "meta-wayland-seat.h"
#include "display-private.h"
#include "window-private.h"
#include <meta/types.h>
@@ -316,6 +317,8 @@ meta_wayland_surface_commit (struct wl_client *client,
{
surface_actor = CLUTTER_WAYLAND_SURFACE (surface->actor);
+ clutter_actor_set_reactive (surface->actor, TRUE);
+
if (!clutter_wayland_surface_attach_buffer (surface_actor,
buffer,
&error))
@@ -381,6 +384,33 @@ const struct wl_surface_interface meta_wayland_surface_interface = {
meta_wayland_surface_set_buffer_transform
};
+void
+meta_wayland_compositor_set_input_focus (MetaWaylandCompositor *compositor,
+ MetaWindow *window)
+{
+ struct wl_surface *surface = NULL;
+
+ if (window)
+ {
+ MetaWindowActor *window_actor =
+ META_WINDOW_ACTOR (meta_window_get_compositor_private (window));
+ ClutterActor *shaped_texture =
+ meta_window_actor_get_shaped_texture (window_actor);
+
+ if (CLUTTER_WAYLAND_IS_SURFACE (shaped_texture))
+ {
+ ClutterWaylandSurface *surface_actor =
+ CLUTTER_WAYLAND_SURFACE (shaped_texture);
+
+ surface = clutter_wayland_surface_get_surface (surface_actor);
+ }
+ }
+
+ wl_keyboard_set_focus (((struct wl_seat *) compositor->seat)->keyboard,
+ surface);
+ wl_data_device_set_keyboard_focus ((struct wl_seat *) compositor->seat);
+}
+
static void
surface_actor_destroyed_cb (void *user_data,
GObject *old_object)
@@ -391,6 +421,14 @@ surface_actor_destroyed_cb (void *user_data,
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)
{
@@ -425,6 +463,12 @@ 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 == (struct wl_surface *) surface)
+ compositor->implicit_grab_surface =
+ ((struct wl_seat *) compositor->seat)->pointer->current;
}
static void
@@ -1281,10 +1325,180 @@ stage_destroy_cb (void)
meta_quit (META_EXIT_SUCCESS);
}
+static gboolean
+event_cb (ClutterActor *stage,
+ const ClutterEvent *event,
+ MetaWaylandCompositor *compositor)
+{
+ struct wl_seat *seat = (struct wl_seat *) compositor->seat;
+ struct wl_pointer *pointer = seat->pointer;
+ MetaWaylandSurface *surface;
+ MetaDisplay *display;
+ XMotionEvent xevent;
+
+ 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)
+ {
+ MetaWaylandSurface *surface = (MetaWaylandSurface *) pointer->current;
+
+ /* Only focus surfaces that wouldn't be handled by the
+ corresponding X events */
+ if (surface && surface->xid == 0)
+ {
+ wl_keyboard_set_focus (seat->keyboard, &surface->wayland_surface);
+ wl_data_device_set_keyboard_focus (seat);
+ }
+ }
+
+ display = meta_get_display ();
+ if (!display)
+ return FALSE;
+
+ /* 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 positoin. See the comment in
+ event_callback() in display.c */
+
+ 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:
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ xevent.type = MotionNotify;
+ xevent.is_hint = NotifyNormal;
+ xevent.same_screen = TRUE;
+ xevent.serial = 0;
+ xevent.send_event = False;
+ xevent.display = display->xdisplay;
+ xevent.root = DefaultRootWindow (display->xdisplay);
+
+ if (compositor->implicit_grab_surface)
+ surface = (MetaWaylandSurface *) compositor->implicit_grab_surface;
+ else
+ surface = (MetaWaylandSurface *) pointer->current;
+
+ if (surface == (MetaWaylandSurface *) pointer->current)
+ {
+ xevent.x = pointer->current_x;
+ xevent.y = pointer->current_y;
+ }
+ else if (surface)
+ {
+ float ax, ay;
+
+ clutter_actor_transform_stage_point (surface->actor,
+ pointer->x, pointer->y,
+ &ax, &ay);
+ xevent.x = ax;
+ xevent.y = ay;
+ }
+ else
+ {
+ xevent.x = pointer->x;
+ xevent.y = pointer->y;
+ }
+
+ if (surface && surface->xid != None)
+ xevent.window = surface->xid;
+ else
+ xevent.window = xevent.root;
+
+ /* Mutter doesn't really know about the sub-windows. This assumes it
+ doesn't care either */
+ xevent.subwindow = xevent.window;
+ xevent.time = event->any.time;
+ xevent.x_root = pointer->x;
+ xevent.y_root = pointer->y;
+ /* The Clutter state flags exactly match the X values */
+ xevent.state = clutter_event_get_state (event);
+
+ meta_display_handle_event (display, (XEvent *) &xevent);
+
+ 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));
@@ -1332,6 +1546,25 @@ meta_wayland_init (void)
g_signal_connect (compositor->stage, "destroy",
G_CALLBACK (stage_destroy_cb), NULL);
+ wl_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_display_add_global (compositor->wayland_display, &wl_shell_interface,