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 +#include +#include +#include +#include + +#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 + +#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 +#include +#include +#include +#include +#include +#include + +#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 +#include + +#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 . + */ + +/* + * 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 . + */ + +#ifndef __META_WAYLAND_POINTER_H__ +#define __META_WAYLAND_POINTER_H__ + +#include + +#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 - +#include #include #include @@ -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; }; -void meta_wayland_init (void); -void meta_wayland_finalize (void); +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; +}; + +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); +void meta_wayland_handle_sig_child (void); -MetaWaylandSurface *meta_wayland_lookup_surface_for_xid (guint32 xid); +MetaWaylandSurface *meta_wayland_lookup_surface_for_xid (guint32 xid); + +void meta_wayland_compositor_repick (MetaWaylandCompositor *compositor); + +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 +#include +#include +#include +#include +#include +#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 +#include +#include +#include + +#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 @@ -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,