diff options
Diffstat (limited to 'src/wayland')
-rw-r--r-- | src/wayland/meta-wayland-keyboard.c | 395 | ||||
-rw-r--r-- | src/wayland/meta-wayland-keyboard.h | 97 | ||||
-rw-r--r-- | src/wayland/meta-wayland-private.h | 27 | ||||
-rw-r--r-- | src/wayland/meta-wayland-seat.c | 444 | ||||
-rw-r--r-- | src/wayland/meta-wayland-seat.h | 44 | ||||
-rw-r--r-- | src/wayland/meta-wayland.c | 233 |
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, |