/* * 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 #include #include "backends/meta-backend-private.h" #include "backends/meta-cursor-renderer.h" #include "backends/meta-cursor-tracker-private.h" #include "backends/meta-cursor.h" #include "clutter/clutter.h" #include "cogl/cogl-wayland-server.h" #include "cogl/cogl.h" #include "compositor/meta-surface-actor-wayland.h" #include "meta/meta-cursor-tracker.h" #include "wayland/meta-wayland-buffer.h" #include "wayland/meta-wayland-cursor-surface.h" #include "wayland/meta-wayland-pointer.h" #include "wayland/meta-wayland-popup.h" #include "wayland/meta-wayland-private.h" #include "wayland/meta-wayland-seat.h" #include "wayland/meta-wayland-surface.h" #include "wayland/meta-xwayland.h" #ifdef HAVE_NATIVE_BACKEND #include "backends/native/meta-backend-native.h" #endif #include "relative-pointer-unstable-v1-server-protocol.h" #define DEFAULT_AXIS_STEP_DISTANCE wl_fixed_from_int (10) enum { FOCUS_SURFACE_CHANGED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL]; G_DEFINE_TYPE (MetaWaylandPointer, meta_wayland_pointer, META_TYPE_WAYLAND_INPUT_DEVICE) static void meta_wayland_pointer_set_current (MetaWaylandPointer *pointer, MetaWaylandSurface *surface); static void meta_wayland_pointer_reset_grab (MetaWaylandPointer *pointer); static void meta_wayland_pointer_cancel_grab (MetaWaylandPointer *pointer); static MetaWaylandPointerClient * meta_wayland_pointer_client_new (void) { MetaWaylandPointerClient *pointer_client; pointer_client = g_new0 (MetaWaylandPointerClient, 1); wl_list_init (&pointer_client->pointer_resources); wl_list_init (&pointer_client->swipe_gesture_resources); wl_list_init (&pointer_client->pinch_gesture_resources); wl_list_init (&pointer_client->relative_pointer_resources); return pointer_client; } static void meta_wayland_pointer_client_free (MetaWaylandPointerClient *pointer_client) { struct wl_resource *resource, *next; /* Since we make every wl_pointer resource defunct when we stop advertising * the pointer capability on the wl_seat, we need to make sure all the * resources in the pointer client instance gets removed. */ wl_resource_for_each_safe (resource, next, &pointer_client->pointer_resources) { wl_list_remove (wl_resource_get_link (resource)); wl_list_init (wl_resource_get_link (resource)); wl_resource_set_user_data (resource, NULL); } wl_resource_for_each_safe (resource, next, &pointer_client->swipe_gesture_resources) { wl_list_remove (wl_resource_get_link (resource)); wl_list_init (wl_resource_get_link (resource)); wl_resource_set_user_data (resource, NULL); } wl_resource_for_each_safe (resource, next, &pointer_client->pinch_gesture_resources) { wl_list_remove (wl_resource_get_link (resource)); wl_list_init (wl_resource_get_link (resource)); wl_resource_set_user_data (resource, NULL); } wl_resource_for_each_safe (resource, next, &pointer_client->relative_pointer_resources) { wl_list_remove (wl_resource_get_link (resource)); wl_list_init (wl_resource_get_link (resource)); wl_resource_set_user_data (resource, NULL); } g_free (pointer_client); } static gboolean meta_wayland_pointer_client_is_empty (MetaWaylandPointerClient *pointer_client) { return (wl_list_empty (&pointer_client->pointer_resources) && wl_list_empty (&pointer_client->swipe_gesture_resources) && wl_list_empty (&pointer_client->pinch_gesture_resources) && wl_list_empty (&pointer_client->relative_pointer_resources)); } MetaWaylandPointerClient * meta_wayland_pointer_get_pointer_client (MetaWaylandPointer *pointer, struct wl_client *client) { if (!pointer->pointer_clients) return NULL; return g_hash_table_lookup (pointer->pointer_clients, client); } static MetaWaylandPointerClient * meta_wayland_pointer_ensure_pointer_client (MetaWaylandPointer *pointer, struct wl_client *client) { MetaWaylandPointerClient *pointer_client; pointer_client = meta_wayland_pointer_get_pointer_client (pointer, client); if (pointer_client) return pointer_client; pointer_client = meta_wayland_pointer_client_new (); g_hash_table_insert (pointer->pointer_clients, client, pointer_client); if (!pointer->focus_client && pointer->focus_surface && wl_resource_get_client (pointer->focus_surface->resource) == client) pointer->focus_client = pointer_client; return pointer_client; } static void meta_wayland_pointer_cleanup_pointer_client (MetaWaylandPointer *pointer, MetaWaylandPointerClient *pointer_client, struct wl_client *client) { if (meta_wayland_pointer_client_is_empty (pointer_client)) { if (pointer->focus_client == pointer_client) pointer->focus_client = NULL; g_hash_table_remove (pointer->pointer_clients, client); } } void meta_wayland_pointer_unbind_pointer_client_resource (struct wl_resource *resource) { MetaWaylandPointer *pointer = wl_resource_get_user_data (resource); MetaWaylandPointerClient *pointer_client; struct wl_client *client = wl_resource_get_client (resource); pointer = wl_resource_get_user_data (resource); if (!pointer) return; wl_list_remove (wl_resource_get_link (resource)); pointer_client = meta_wayland_pointer_get_pointer_client (pointer, client); if (!pointer_client) { /* This happens if all pointer devices were unplugged and no new resources * were created by the client. * * If this is a resource that was previously made defunct, pointer_client * be non-NULL but it is harmless since the below cleanup call will be * prevented from removing the pointer client because of valid resources. */ return; } meta_wayland_pointer_cleanup_pointer_client (pointer, pointer_client, client); } static void sync_focus_surface (MetaWaylandPointer *pointer) { MetaDisplay *display = meta_get_display (); MetaBackend *backend = meta_get_backend (); MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); ClutterBackend *clutter_backend = clutter_get_default_backend (); ClutterSeat *clutter_seat = clutter_backend_get_default_seat (clutter_backend); if (!meta_cursor_tracker_get_pointer_visible (cursor_tracker) && !clutter_seat_is_unfocus_inhibited (clutter_seat)) { meta_wayland_pointer_set_focus (pointer, NULL); return; } switch (display->event_route) { case META_EVENT_ROUTE_WINDOW_OP: case META_EVENT_ROUTE_COMPOSITOR_GRAB: case META_EVENT_ROUTE_FRAME_BUTTON: /* The compositor has a grab, so remove our focus... */ meta_wayland_pointer_set_focus (pointer, NULL); break; case META_EVENT_ROUTE_NORMAL: case META_EVENT_ROUTE_WAYLAND_POPUP: { const MetaWaylandPointerGrabInterface *interface = pointer->grab->interface; interface->focus (pointer->grab, pointer->current); } break; default: g_assert_not_reached (); } } static void meta_wayland_pointer_send_frame (MetaWaylandPointer *pointer, struct wl_resource *resource) { if (wl_resource_get_version (resource) >= WL_POINTER_AXIS_SOURCE_SINCE_VERSION) wl_pointer_send_frame (resource); } void meta_wayland_pointer_broadcast_frame (MetaWaylandPointer *pointer) { struct wl_resource *resource; if (!pointer->focus_client) return; wl_resource_for_each (resource, &pointer->focus_client->pointer_resources) { meta_wayland_pointer_send_frame (pointer, resource); } } void meta_wayland_pointer_send_relative_motion (MetaWaylandPointer *pointer, const ClutterEvent *event) { struct wl_resource *resource; double dx, dy; double dx_unaccel, dy_unaccel; uint64_t time_us; uint32_t time_us_hi; uint32_t time_us_lo; wl_fixed_t dxf, dyf; wl_fixed_t dx_unaccelf, dy_unaccelf; if (!pointer->focus_client) return; if (!clutter_event_get_relative_motion (event, &dx, &dy, &dx_unaccel, &dy_unaccel)) return; time_us = clutter_event_get_time_us (event); if (time_us == 0) time_us = clutter_event_get_time (event) * 1000ULL; time_us_hi = (uint32_t) (time_us >> 32); time_us_lo = (uint32_t) time_us; dxf = wl_fixed_from_double (dx); dyf = wl_fixed_from_double (dy); dx_unaccelf = wl_fixed_from_double (dx_unaccel); dy_unaccelf = wl_fixed_from_double (dy_unaccel); wl_resource_for_each (resource, &pointer->focus_client->relative_pointer_resources) { zwp_relative_pointer_v1_send_relative_motion (resource, time_us_hi, time_us_lo, dxf, dyf, dx_unaccelf, dy_unaccelf); } } void meta_wayland_pointer_send_motion (MetaWaylandPointer *pointer, const ClutterEvent *event) { struct wl_resource *resource; uint32_t time; float sx, sy; if (!pointer->focus_client) return; time = clutter_event_get_time (event); meta_wayland_surface_get_relative_coordinates (pointer->focus_surface, event->motion.x, event->motion.y, &sx, &sy); wl_resource_for_each (resource, &pointer->focus_client->pointer_resources) { wl_pointer_send_motion (resource, time, wl_fixed_from_double (sx), wl_fixed_from_double (sy)); } meta_wayland_pointer_send_relative_motion (pointer, event); meta_wayland_pointer_broadcast_frame (pointer); } void meta_wayland_pointer_send_button (MetaWaylandPointer *pointer, const ClutterEvent *event) { struct wl_resource *resource; ClutterEventType event_type; event_type = clutter_event_type (event); if (pointer->focus_client && !wl_list_empty (&pointer->focus_client->pointer_resources)) { MetaWaylandInputDevice *input_device = META_WAYLAND_INPUT_DEVICE (pointer); uint32_t time; uint32_t button; uint32_t serial; button = clutter_event_get_event_code (event); time = clutter_event_get_time (event); serial = meta_wayland_input_device_next_serial (input_device); wl_resource_for_each (resource, &pointer->focus_client->pointer_resources) { wl_pointer_send_button (resource, serial, time, button, event_type == CLUTTER_BUTTON_PRESS ? 1 : 0); } meta_wayland_pointer_broadcast_frame (pointer); } if (pointer->button_count == 0 && event_type == CLUTTER_BUTTON_RELEASE) sync_focus_surface (pointer); } static void default_grab_focus (MetaWaylandPointerGrab *grab, MetaWaylandSurface *surface) { MetaWaylandPointer *pointer = grab->pointer; MetaWaylandSeat *seat = meta_wayland_pointer_get_seat (pointer); MetaDisplay *display = meta_get_display (); MetaBackend *backend = meta_get_backend (); MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); ClutterBackend *clutter_backend = clutter_get_default_backend (); ClutterSeat *clutter_seat = clutter_backend_get_default_seat (clutter_backend); if (!meta_wayland_seat_has_pointer (seat)) return; if (!meta_cursor_tracker_get_pointer_visible (cursor_tracker) && !clutter_seat_is_unfocus_inhibited (clutter_seat)) return; if (pointer->button_count > 0) return; switch (display->event_route) { case META_EVENT_ROUTE_WINDOW_OP: case META_EVENT_ROUTE_COMPOSITOR_GRAB: case META_EVENT_ROUTE_FRAME_BUTTON: return; break; case META_EVENT_ROUTE_NORMAL: case META_EVENT_ROUTE_WAYLAND_POPUP: break; } meta_wayland_pointer_set_focus (pointer, surface); } static void default_grab_motion (MetaWaylandPointerGrab *grab, const ClutterEvent *event) { MetaWaylandPointer *pointer = grab->pointer; meta_wayland_pointer_send_motion (pointer, event); } static void default_grab_button (MetaWaylandPointerGrab *grab, const ClutterEvent *event) { MetaWaylandPointer *pointer = grab->pointer; meta_wayland_pointer_send_button (pointer, event); } static const MetaWaylandPointerGrabInterface default_pointer_grab_interface = { default_grab_focus, default_grab_motion, default_grab_button }; static void meta_wayland_pointer_on_cursor_changed (MetaCursorTracker *cursor_tracker, MetaWaylandPointer *pointer) { if (pointer->cursor_surface) meta_wayland_surface_update_outputs (pointer->cursor_surface); } void meta_wayland_pointer_enable (MetaWaylandPointer *pointer) { MetaBackend *backend = meta_get_backend (); MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); ClutterSeat *clutter_seat; pointer->pointer_clients = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) meta_wayland_pointer_client_free); pointer->cursor_surface = NULL; clutter_seat = clutter_backend_get_default_seat (clutter_get_default_backend ()); pointer->device = clutter_seat_get_pointer (clutter_seat); g_signal_connect (cursor_tracker, "cursor-changed", G_CALLBACK (meta_wayland_pointer_on_cursor_changed), pointer); g_signal_connect_swapped (cursor_tracker, "visibility-changed", G_CALLBACK (sync_focus_surface), pointer); g_signal_connect_swapped (clutter_seat, "is-unfocus-inhibited-changed", G_CALLBACK (sync_focus_surface), pointer); } void meta_wayland_pointer_disable (MetaWaylandPointer *pointer) { MetaBackend *backend = meta_get_backend (); MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); ClutterBackend *clutter_backend = clutter_get_default_backend (); ClutterSeat *clutter_seat = clutter_backend_get_default_seat (clutter_backend); g_signal_handlers_disconnect_by_func (cursor_tracker, (gpointer) meta_wayland_pointer_on_cursor_changed, pointer); g_signal_handlers_disconnect_by_func (cursor_tracker, sync_focus_surface, pointer); g_signal_handlers_disconnect_by_func (clutter_seat, sync_focus_surface, pointer); if (pointer->cursor_surface) { g_clear_signal_handler (&pointer->cursor_surface_destroy_id, pointer->cursor_surface); } meta_wayland_pointer_cancel_grab (pointer); meta_wayland_pointer_reset_grab (pointer); meta_wayland_pointer_set_focus (pointer, NULL); meta_wayland_pointer_set_current (pointer, NULL); g_clear_pointer (&pointer->pointer_clients, g_hash_table_unref); pointer->cursor_surface = NULL; } static int count_buttons (const ClutterEvent *event) { static gint maskmap[5] = { CLUTTER_BUTTON1_MASK, CLUTTER_BUTTON2_MASK, CLUTTER_BUTTON3_MASK, CLUTTER_BUTTON4_MASK, CLUTTER_BUTTON5_MASK }; ClutterModifierType mod_mask; int i, count; mod_mask = clutter_event_get_state (event); count = 0; for (i = 0; i < 5; i++) { if (mod_mask & maskmap[i]) count++; } return count; } static void current_surface_destroyed (MetaWaylandSurface *surface, MetaWaylandPointer *pointer) { meta_wayland_pointer_set_current (pointer, NULL); } static void meta_wayland_pointer_set_current (MetaWaylandPointer *pointer, MetaWaylandSurface *surface) { if (pointer->current) { g_clear_signal_handler (&pointer->current_surface_destroyed_handler_id, pointer->current); pointer->current = NULL; } if (surface) { pointer->current = surface; pointer->current_surface_destroyed_handler_id = g_signal_connect (surface, "destroy", G_CALLBACK (current_surface_destroyed), pointer); } } static void repick_for_event (MetaWaylandPointer *pointer, const ClutterEvent *for_event) { ClutterActor *actor; MetaWaylandSurface *surface; if (clutter_event_type (for_event) == CLUTTER_LEAVE) actor = clutter_event_get_related (for_event); else actor = clutter_event_get_source (for_event); if (META_IS_SURFACE_ACTOR_WAYLAND (actor)) { MetaSurfaceActorWayland *actor_wayland = META_SURFACE_ACTOR_WAYLAND (actor); surface = meta_surface_actor_wayland_get_surface (actor_wayland); } else { surface = NULL; } meta_wayland_pointer_set_current (pointer, surface); sync_focus_surface (pointer); meta_wayland_pointer_update_cursor_surface (pointer); } void meta_wayland_pointer_update (MetaWaylandPointer *pointer, const ClutterEvent *event) { if ((event->type == CLUTTER_MOTION || event->type == CLUTTER_ENTER || event->type == CLUTTER_LEAVE) && !clutter_event_get_event_sequence (event)) repick_for_event (pointer, event); if (event->type == CLUTTER_MOTION || event->type == CLUTTER_BUTTON_PRESS || event->type == CLUTTER_BUTTON_RELEASE) { pointer->button_count = count_buttons (event); } } static void notify_motion (MetaWaylandPointer *pointer, const ClutterEvent *event) { pointer->grab->interface->motion (pointer->grab, event); } static void handle_motion_event (MetaWaylandPointer *pointer, const ClutterEvent *event) { notify_motion (pointer, event); } static void handle_button_event (MetaWaylandPointer *pointer, const ClutterEvent *event) { gboolean implicit_grab; implicit_grab = (event->type == CLUTTER_BUTTON_PRESS) && (pointer->button_count == 1); if (implicit_grab) { pointer->grab_button = clutter_event_get_button (event); pointer->grab_time = clutter_event_get_time (event); clutter_event_get_coords (event, &pointer->grab_x, &pointer->grab_y); } pointer->grab->interface->button (pointer->grab, event); if (implicit_grab) { MetaWaylandSeat *seat = meta_wayland_pointer_get_seat (pointer); pointer->grab_serial = wl_display_get_serial (seat->wl_display); } } static void handle_scroll_event (MetaWaylandPointer *pointer, const ClutterEvent *event) { struct wl_resource *resource; wl_fixed_t x_value = 0, y_value = 0; int x_discrete = 0, y_discrete = 0; enum wl_pointer_axis_source source = -1; if (clutter_event_is_pointer_emulated (event)) return; switch (event->scroll.scroll_source) { case CLUTTER_SCROLL_SOURCE_WHEEL: source = WL_POINTER_AXIS_SOURCE_WHEEL; break; case CLUTTER_SCROLL_SOURCE_FINGER: source = WL_POINTER_AXIS_SOURCE_FINGER; break; case CLUTTER_SCROLL_SOURCE_CONTINUOUS: source = WL_POINTER_AXIS_SOURCE_CONTINUOUS; break; default: source = WL_POINTER_AXIS_SOURCE_WHEEL; break; } switch (clutter_event_get_scroll_direction (event)) { case CLUTTER_SCROLL_UP: y_value = -DEFAULT_AXIS_STEP_DISTANCE; y_discrete = -1; break; case CLUTTER_SCROLL_DOWN: y_value = DEFAULT_AXIS_STEP_DISTANCE; y_discrete = 1; break; case CLUTTER_SCROLL_LEFT: x_value = -DEFAULT_AXIS_STEP_DISTANCE; x_discrete = -1; break; case CLUTTER_SCROLL_RIGHT: x_value = DEFAULT_AXIS_STEP_DISTANCE; x_discrete = 1; break; case CLUTTER_SCROLL_SMOOTH: { double dx, dy; /* Clutter smooth scroll events are in discrete steps (1 step = 1.0 long * vector along one axis). To convert to smooth scroll events that are * in pointer motion event space, multiply the vector with the 10. */ const double factor = 10.0; clutter_event_get_scroll_delta (event, &dx, &dy); x_value = wl_fixed_from_double (dx) * factor; y_value = wl_fixed_from_double (dy) * factor; } break; default: return; } if (pointer->focus_client) { wl_resource_for_each (resource, &pointer->focus_client->pointer_resources) { if (wl_resource_get_version (resource) >= WL_POINTER_AXIS_SOURCE_SINCE_VERSION) wl_pointer_send_axis_source (resource, source); /* X axis */ if (x_discrete != 0 && wl_resource_get_version (resource) >= WL_POINTER_AXIS_DISCRETE_SINCE_VERSION) wl_pointer_send_axis_discrete (resource, WL_POINTER_AXIS_HORIZONTAL_SCROLL, x_discrete); if (x_value) wl_pointer_send_axis (resource, clutter_event_get_time (event), WL_POINTER_AXIS_HORIZONTAL_SCROLL, x_value); if ((event->scroll.finish_flags & CLUTTER_SCROLL_FINISHED_HORIZONTAL) && wl_resource_get_version (resource) >= WL_POINTER_AXIS_STOP_SINCE_VERSION) wl_pointer_send_axis_stop (resource, clutter_event_get_time (event), WL_POINTER_AXIS_HORIZONTAL_SCROLL); /* Y axis */ if (y_discrete != 0 && wl_resource_get_version (resource) >= WL_POINTER_AXIS_DISCRETE_SINCE_VERSION) wl_pointer_send_axis_discrete (resource, WL_POINTER_AXIS_VERTICAL_SCROLL, y_discrete); if (y_value) wl_pointer_send_axis (resource, clutter_event_get_time (event), WL_POINTER_AXIS_VERTICAL_SCROLL, y_value); if ((event->scroll.finish_flags & CLUTTER_SCROLL_FINISHED_VERTICAL) && wl_resource_get_version (resource) >= WL_POINTER_AXIS_STOP_SINCE_VERSION) wl_pointer_send_axis_stop (resource, clutter_event_get_time (event), WL_POINTER_AXIS_VERTICAL_SCROLL); } meta_wayland_pointer_broadcast_frame (pointer); } } gboolean meta_wayland_pointer_handle_event (MetaWaylandPointer *pointer, const ClutterEvent *event) { switch (event->type) { case CLUTTER_MOTION: handle_motion_event (pointer, event); break; case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_RELEASE: handle_button_event (pointer, event); break; case CLUTTER_SCROLL: handle_scroll_event (pointer, event); break; case CLUTTER_TOUCHPAD_SWIPE: meta_wayland_pointer_gesture_swipe_handle_event (pointer, event); break; case CLUTTER_TOUCHPAD_PINCH: meta_wayland_pointer_gesture_pinch_handle_event (pointer, event); break; default: break; } return FALSE; } static void meta_wayland_pointer_send_enter (MetaWaylandPointer *pointer, struct wl_resource *pointer_resource, uint32_t serial, MetaWaylandSurface *surface) { wl_fixed_t sx, sy; meta_wayland_pointer_get_relative_coordinates (pointer, surface, &sx, &sy); wl_pointer_send_enter (pointer_resource, serial, surface->resource, sx, sy); } static void meta_wayland_pointer_send_leave (MetaWaylandPointer *pointer, struct wl_resource *pointer_resource, uint32_t serial, MetaWaylandSurface *surface) { wl_pointer_send_leave (pointer_resource, serial, surface->resource); } static void meta_wayland_pointer_broadcast_enter (MetaWaylandPointer *pointer, uint32_t serial, MetaWaylandSurface *surface) { struct wl_resource *pointer_resource; wl_resource_for_each (pointer_resource, &pointer->focus_client->pointer_resources) meta_wayland_pointer_send_enter (pointer, pointer_resource, serial, surface); meta_wayland_pointer_broadcast_frame (pointer); } static void meta_wayland_pointer_broadcast_leave (MetaWaylandPointer *pointer, uint32_t serial, MetaWaylandSurface *surface) { struct wl_resource *pointer_resource; wl_resource_for_each (pointer_resource, &pointer->focus_client->pointer_resources) meta_wayland_pointer_send_leave (pointer, pointer_resource, serial, surface); meta_wayland_pointer_broadcast_frame (pointer); } static void focus_surface_destroyed (MetaWaylandSurface *surface, MetaWaylandPointer *pointer) { meta_wayland_pointer_set_focus (pointer, NULL); } void meta_wayland_pointer_set_focus (MetaWaylandPointer *pointer, MetaWaylandSurface *surface) { MetaWaylandInputDevice *input_device = META_WAYLAND_INPUT_DEVICE (pointer); MetaBackend *backend = meta_get_backend (); MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); ClutterBackend *clutter_backend = clutter_get_default_backend (); ClutterSeat *clutter_seat = clutter_backend_get_default_seat (clutter_backend); g_return_if_fail (meta_cursor_tracker_get_pointer_visible (cursor_tracker) || clutter_seat_is_unfocus_inhibited (clutter_seat) || surface == NULL); if (pointer->focus_surface == surface) return; if (pointer->focus_surface != NULL) { uint32_t serial; serial = meta_wayland_input_device_next_serial (input_device); if (pointer->focus_client) { meta_wayland_pointer_broadcast_leave (pointer, serial, pointer->focus_surface); pointer->focus_client = NULL; } g_clear_signal_handler (&pointer->focus_surface_destroyed_handler_id, pointer->focus_surface); pointer->focus_surface = NULL; } if (surface != NULL) { ClutterStage *stage = CLUTTER_STAGE (meta_backend_get_stage (backend)); struct wl_client *client = wl_resource_get_client (surface->resource); graphene_point_t pos; MetaWindow *focus_window; pointer->focus_surface = surface; pointer->focus_surface_destroyed_handler_id = g_signal_connect_after (pointer->focus_surface, "destroy", G_CALLBACK (focus_surface_destroyed), pointer); clutter_stage_get_device_coords (stage, pointer->device, NULL, &pos); focus_window = meta_wayland_surface_get_window (pointer->focus_surface); if (focus_window) meta_window_handle_enter (focus_window, /* XXX -- can we reliably get a timestamp for setting focus? */ clutter_get_current_event_time (), pos.x, pos.y); pointer->focus_client = meta_wayland_pointer_get_pointer_client (pointer, client); if (pointer->focus_client) { pointer->focus_serial = meta_wayland_input_device_next_serial (input_device); meta_wayland_pointer_broadcast_enter (pointer, pointer->focus_serial, pointer->focus_surface); } } meta_wayland_pointer_update_cursor_surface (pointer); g_signal_emit (pointer, signals[FOCUS_SURFACE_CHANGED], 0); } void meta_wayland_pointer_start_grab (MetaWaylandPointer *pointer, MetaWaylandPointerGrab *grab) { const MetaWaylandPointerGrabInterface *interface; meta_wayland_pointer_cancel_grab (pointer); pointer->grab = grab; interface = pointer->grab->interface; grab->pointer = pointer; interface->focus (pointer->grab, pointer->current); } static void meta_wayland_pointer_reset_grab (MetaWaylandPointer *pointer) { pointer->grab = &pointer->default_grab; } 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); meta_wayland_pointer_update_cursor_surface (pointer); } static void meta_wayland_pointer_cancel_grab (MetaWaylandPointer *pointer) { if (pointer->grab->interface->cancel) pointer->grab->interface->cancel (pointer->grab); } void meta_wayland_pointer_end_popup_grab (MetaWaylandPointer *pointer) { MetaWaylandPopupGrab *popup_grab = (MetaWaylandPopupGrab*)pointer->grab; meta_wayland_popup_grab_destroy (popup_grab); } MetaWaylandPopup * meta_wayland_pointer_start_popup_grab (MetaWaylandPointer *pointer, MetaWaylandPopupSurface *popup_surface) { MetaWaylandPopupGrab *grab; if (pointer->grab != &pointer->default_grab && !meta_wayland_pointer_grab_is_popup_grab (pointer->grab)) return NULL; if (pointer->grab == &pointer->default_grab) grab = meta_wayland_popup_grab_create (pointer, popup_surface); else grab = (MetaWaylandPopupGrab*)pointer->grab; return meta_wayland_popup_create (popup_surface, grab); } void meta_wayland_pointer_get_relative_coordinates (MetaWaylandPointer *pointer, MetaWaylandSurface *surface, wl_fixed_t *sx, wl_fixed_t *sy) { MetaBackend *backend = meta_get_backend (); ClutterStage *stage = CLUTTER_STAGE (meta_backend_get_stage (backend)); float xf = 0.0f, yf = 0.0f; graphene_point_t pos; clutter_stage_get_device_coords (stage, pointer->device, NULL, &pos); meta_wayland_surface_get_relative_coordinates (surface, pos.x, pos.y, &xf, &yf); *sx = wl_fixed_from_double (xf); *sy = wl_fixed_from_double (yf); } void meta_wayland_pointer_update_cursor_surface (MetaWaylandPointer *pointer) { MetaBackend *backend = meta_get_backend (); MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); if (pointer->current) { MetaCursorSprite *cursor_sprite = NULL; if (pointer->cursor_surface) { MetaWaylandCursorSurface *cursor_surface = META_WAYLAND_CURSOR_SURFACE (pointer->cursor_surface->role); cursor_sprite = meta_wayland_cursor_surface_get_sprite (cursor_surface); } meta_cursor_tracker_set_window_cursor (cursor_tracker, cursor_sprite); } else { meta_cursor_tracker_unset_window_cursor (cursor_tracker); } } static void ensure_update_cursor_surface (MetaWaylandPointer *pointer, MetaWaylandSurface *surface) { if (pointer->cursor_surface != surface) return; pointer->cursor_surface = NULL; meta_wayland_pointer_update_cursor_surface (pointer); } static void meta_wayland_pointer_set_cursor_surface (MetaWaylandPointer *pointer, MetaWaylandSurface *cursor_surface) { MetaWaylandSurface *prev_cursor_surface; prev_cursor_surface = pointer->cursor_surface; if (prev_cursor_surface == cursor_surface) return; pointer->cursor_surface = cursor_surface; if (prev_cursor_surface) { meta_wayland_surface_update_outputs (prev_cursor_surface); g_clear_signal_handler (&pointer->cursor_surface_destroy_id, prev_cursor_surface); } if (cursor_surface) { pointer->cursor_surface_destroy_id = g_signal_connect_swapped (cursor_surface, "destroy", G_CALLBACK (ensure_update_cursor_surface), pointer); } meta_wayland_pointer_update_cursor_surface (pointer); } static void pointer_set_cursor (struct wl_client *client, struct wl_resource *resource, uint32_t serial, struct wl_resource *surface_resource, int32_t hot_x, int32_t hot_y) { MetaWaylandPointer *pointer; MetaWaylandSurface *surface; pointer = wl_resource_get_user_data (resource); if (!pointer) return; surface = (surface_resource ? wl_resource_get_user_data (surface_resource) : NULL); if (pointer->focus_surface == NULL) return; if (wl_resource_get_client (pointer->focus_surface->resource) != client) return; if (pointer->focus_serial - serial > G_MAXUINT32 / 2) return; if (surface && !meta_wayland_surface_assign_role (surface, META_TYPE_WAYLAND_CURSOR_SURFACE, NULL)) { wl_resource_post_error (resource, WL_POINTER_ERROR_ROLE, "wl_surface@%d already has a different role", wl_resource_get_id (surface_resource)); return; } if (surface) { ClutterBackend *clutter_backend = clutter_get_default_backend (); ClutterSeat *clutter_seat = clutter_backend_get_default_seat (clutter_backend); ClutterInputDevice *device = clutter_seat_get_pointer (clutter_seat); MetaCursorRenderer *cursor_renderer = meta_backend_get_cursor_renderer_for_device (meta_get_backend (), device); MetaWaylandCursorSurface *cursor_surface; cursor_surface = META_WAYLAND_CURSOR_SURFACE (surface->role); meta_wayland_cursor_surface_set_renderer (cursor_surface, cursor_renderer); meta_wayland_cursor_surface_set_hotspot (cursor_surface, hot_x, hot_y); } meta_wayland_pointer_set_cursor_surface (pointer, surface); } static void pointer_release (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static const struct wl_pointer_interface pointer_interface = { pointer_set_cursor, pointer_release, }; void meta_wayland_pointer_create_new_resource (MetaWaylandPointer *pointer, struct wl_client *client, struct wl_resource *seat_resource, uint32_t id) { struct wl_resource *resource; MetaWaylandPointerClient *pointer_client; resource = wl_resource_create (client, &wl_pointer_interface, wl_resource_get_version (seat_resource), id); wl_resource_set_implementation (resource, &pointer_interface, pointer, meta_wayland_pointer_unbind_pointer_client_resource); pointer_client = meta_wayland_pointer_ensure_pointer_client (pointer, client); wl_list_insert (&pointer_client->pointer_resources, wl_resource_get_link (resource)); if (pointer->focus_client == pointer_client) { meta_wayland_pointer_send_enter (pointer, resource, pointer->focus_serial, pointer->focus_surface); meta_wayland_pointer_send_frame (pointer, resource); } } static gboolean pointer_can_grab_surface (MetaWaylandPointer *pointer, MetaWaylandSurface *surface) { MetaWaylandSurface *subsurface; if (pointer->focus_surface == surface) return TRUE; META_WAYLAND_SURFACE_FOREACH_SUBSURFACE (surface, subsurface) { if (pointer_can_grab_surface (pointer, subsurface)) return TRUE; } return FALSE; } gboolean meta_wayland_pointer_can_grab_surface (MetaWaylandPointer *pointer, MetaWaylandSurface *surface, uint32_t serial) { return (pointer->grab_serial == serial && pointer_can_grab_surface (pointer, surface)); } gboolean meta_wayland_pointer_can_popup (MetaWaylandPointer *pointer, uint32_t serial) { return pointer->grab_serial == serial; } MetaWaylandSurface * meta_wayland_pointer_get_top_popup (MetaWaylandPointer *pointer) { MetaWaylandPopupGrab *grab; if (!meta_wayland_pointer_grab_is_popup_grab (pointer->grab)) return NULL; grab = (MetaWaylandPopupGrab*)pointer->grab; return meta_wayland_popup_grab_get_top_popup(grab); } static void relative_pointer_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static const struct zwp_relative_pointer_v1_interface relative_pointer_interface = { relative_pointer_destroy }; static void relative_pointer_manager_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void relative_pointer_manager_get_relative_pointer (struct wl_client *client, struct wl_resource *manager_resource, uint32_t id, struct wl_resource *pointer_resource) { MetaWaylandPointer *pointer = wl_resource_get_user_data (pointer_resource); struct wl_resource *resource; MetaWaylandPointerClient *pointer_client; resource = wl_resource_create (client, &zwp_relative_pointer_v1_interface, wl_resource_get_version (manager_resource), id); if (!resource) { wl_client_post_no_memory (client); return; } wl_resource_set_implementation (resource, &relative_pointer_interface, pointer, meta_wayland_pointer_unbind_pointer_client_resource); pointer_client = meta_wayland_pointer_ensure_pointer_client (pointer, client); wl_list_insert (&pointer_client->relative_pointer_resources, wl_resource_get_link (resource)); } static const struct zwp_relative_pointer_manager_v1_interface relative_pointer_manager = { relative_pointer_manager_destroy, relative_pointer_manager_get_relative_pointer, }; static void bind_relative_pointer_manager (struct wl_client *client, void *data, uint32_t version, uint32_t id) { MetaWaylandCompositor *compositor = data; struct wl_resource *resource; resource = wl_resource_create (client, &zwp_relative_pointer_manager_v1_interface, 1, id); if (version != 1) wl_resource_post_error (resource, WL_DISPLAY_ERROR_INVALID_OBJECT, "bound invalid version %u of " "wp_relative_pointer_manager", version); wl_resource_set_implementation (resource, &relative_pointer_manager, compositor, NULL); } void meta_wayland_relative_pointer_init (MetaWaylandCompositor *compositor) { /* Relative pointer events are currently only supported by the native backend * so lets just advertise the extension when the native backend is used. */ #ifdef HAVE_NATIVE_BACKEND if (!META_IS_BACKEND_NATIVE (meta_get_backend ())) return; #else return; #endif if (!wl_global_create (compositor->wayland_display, &zwp_relative_pointer_manager_v1_interface, 1, compositor, bind_relative_pointer_manager)) g_error ("Could not create relative pointer manager global"); } MetaWaylandSeat * meta_wayland_pointer_get_seat (MetaWaylandPointer *pointer) { MetaWaylandInputDevice *input_device = META_WAYLAND_INPUT_DEVICE (pointer); return meta_wayland_input_device_get_seat (input_device); } static void meta_wayland_pointer_init (MetaWaylandPointer *pointer) { pointer->default_grab.interface = &default_pointer_grab_interface; pointer->default_grab.pointer = pointer; pointer->grab = &pointer->default_grab; } static void meta_wayland_pointer_class_init (MetaWaylandPointerClass *klass) { signals[FOCUS_SURFACE_CHANGED] = g_signal_new ("focus-surface-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); }