diff --git a/src/meson.build b/src/meson.build index 8f8a6571d..47ee8f570 100644 --- a/src/meson.build +++ b/src/meson.build @@ -625,6 +625,8 @@ if have_wayland 'wayland/meta-wayland-inhibit-shortcuts-dialog.c', 'wayland/meta-wayland-inhibit-shortcuts-dialog.h', 'wayland/meta-wayland-inhibit-shortcuts.h', + 'wayland/meta-wayland-input.c', + 'wayland/meta-wayland-input.h', 'wayland/meta-wayland-input-device.c', 'wayland/meta-wayland-input-device.h', 'wayland/meta-wayland-keyboard.c', diff --git a/src/wayland/meta-wayland-input.c b/src/wayland/meta-wayland-input.c new file mode 100644 index 000000000..8115c489d --- /dev/null +++ b/src/wayland/meta-wayland-input.c @@ -0,0 +1,326 @@ +/* + * Interface for Wayland events + * + * Copyright (C) 2023 Red Hat Inc. + * + * 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, see . + * + * Author: Carlos Garnacho + */ + +#include "config.h" + +#include + +#include "wayland/meta-wayland-input.h" + +#include "wayland/meta-wayland-seat.h" +#include "wayland/meta-wayland-tablet-seat.h" + +struct _MetaWaylandEventHandler +{ + const MetaWaylandEventInterface *iface; + MetaWaylandInput *input; + gpointer user_data; + struct wl_list link; +}; + +struct _MetaWaylandInput +{ + GObject parent_instance; + + MetaWaylandSeat *seat; + struct wl_list event_handler_list; +}; + +G_DEFINE_FINAL_TYPE (MetaWaylandInput, meta_wayland_input, G_TYPE_OBJECT) + +static void +meta_wayland_input_init (MetaWaylandInput *input) +{ + wl_list_init (&input->event_handler_list); +} + +static void +meta_wayland_input_finalize (GObject *object) +{ + MetaWaylandInput *input = META_WAYLAND_INPUT (object); + MetaWaylandEventHandler *handler, *next; + + wl_list_for_each_safe (handler, next, &input->event_handler_list, link) + meta_wayland_input_detach_event_handler (input, handler); + + G_OBJECT_CLASS (meta_wayland_input_parent_class)->finalize (object); +} + +static void +meta_wayland_input_class_init (MetaWaylandInputClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = meta_wayland_input_finalize; +} + +MetaWaylandInput * +meta_wayland_input_new (MetaWaylandSeat *seat) +{ + MetaWaylandInput *input; + + input = g_object_new (META_TYPE_WAYLAND_INPUT, NULL); + input->seat = seat; + + return input; +} + +static void +meta_wayland_event_handler_invalidate_focus (MetaWaylandEventHandler *handler, + ClutterInputDevice *device, + ClutterEventSequence *sequence) +{ + MetaWaylandSurface *surface = NULL; + + if (!handler->iface->focus) + return; + + /* Only the first handler can focus other than a NULL surface */ + if (meta_wayland_input_is_current_handler (handler->input, handler) && + handler->iface->get_focus_surface) + { + surface = handler->iface->get_focus_surface (handler, + device, sequence, + handler->user_data); + } + + handler->iface->focus (handler, + device, sequence, surface, + handler->user_data); +} + +static void +meta_wayland_event_handler_invalidate_all_focus (MetaWaylandEventHandler *handler) +{ + MetaWaylandSeat *seat = handler->input->seat; + ClutterSeat *clutter_seat = + clutter_backend_get_default_seat (clutter_get_default_backend ()); + ClutterInputDevice *device; + GHashTableIter iter; + + /* Trigger sync of all known devices */ + if (meta_wayland_seat_has_pointer (seat)) + { + device = clutter_seat_get_pointer (clutter_seat); + meta_wayland_event_handler_invalidate_focus (handler, device, NULL); + } + + if (meta_wayland_seat_has_keyboard (seat)) + { + device = clutter_seat_get_keyboard (clutter_seat); + meta_wayland_event_handler_invalidate_focus (handler, device, NULL); + } + + if (meta_wayland_seat_has_touch (seat)) + meta_wayland_touch_cancel (seat->touch); + + g_hash_table_iter_init (&iter, seat->tablet_seat->tablets); + while (g_hash_table_iter_next (&iter, (gpointer*) &device, NULL)) + meta_wayland_event_handler_invalidate_focus (handler, device, NULL); + + g_hash_table_iter_init (&iter, seat->tablet_seat->pads); + while (g_hash_table_iter_next (&iter, (gpointer*) &device, NULL)) + meta_wayland_event_handler_invalidate_focus (handler, device, NULL); +} + +static gboolean +meta_wayland_event_handler_handle_event (MetaWaylandEventHandler *handler, + const ClutterEvent *event) +{ + ClutterEventType event_type = clutter_event_type (event); + + switch (event_type) + { + case CLUTTER_ENTER: + case CLUTTER_LEAVE: + { + ClutterInputDevice *device; + ClutterEventSequence *sequence; + + device = clutter_event_get_device (event); + sequence = clutter_event_get_event_sequence (event); + meta_wayland_event_handler_invalidate_focus (handler, + device, sequence); + } + + return CLUTTER_EVENT_PROPAGATE; + case CLUTTER_TOUCH_BEGIN: + case CLUTTER_BUTTON_PRESS: + if (!handler->iface->press) + return CLUTTER_EVENT_PROPAGATE; + return handler->iface->press (handler, event, handler->user_data); + + case CLUTTER_TOUCH_UPDATE: + case CLUTTER_MOTION: + if (!handler->iface->motion) + return CLUTTER_EVENT_PROPAGATE; + return handler->iface->motion (handler, event, handler->user_data); + + case CLUTTER_TOUCH_END: + case CLUTTER_BUTTON_RELEASE: + if (!handler->iface->release) + return CLUTTER_EVENT_PROPAGATE; + return handler->iface->release (handler, event, handler->user_data); + + case CLUTTER_KEY_PRESS: + case CLUTTER_KEY_RELEASE: + if (!handler->iface->key) + return CLUTTER_EVENT_PROPAGATE; + return handler->iface->key (handler, event, handler->user_data); + + case CLUTTER_TOUCH_CANCEL: + case CLUTTER_SCROLL: + case CLUTTER_PROXIMITY_IN: + case CLUTTER_PROXIMITY_OUT: + case CLUTTER_TOUCHPAD_PINCH: + case CLUTTER_TOUCHPAD_SWIPE: + case CLUTTER_TOUCHPAD_HOLD: + case CLUTTER_PAD_BUTTON_PRESS: + case CLUTTER_PAD_BUTTON_RELEASE: + case CLUTTER_PAD_STRIP: + case CLUTTER_PAD_RING: + case CLUTTER_IM_COMMIT: + case CLUTTER_IM_DELETE: + case CLUTTER_IM_PREEDIT: + case CLUTTER_DEVICE_ADDED: + case CLUTTER_DEVICE_REMOVED: + case CLUTTER_NOTHING: + case CLUTTER_EVENT_LAST: + if (!handler->iface->other) + return CLUTTER_EVENT_PROPAGATE; + return handler->iface->other (handler, event, handler->user_data); + } + + g_assert_not_reached (); +} + +MetaWaylandEventHandler * +meta_wayland_input_attach_event_handler (MetaWaylandInput *input, + const MetaWaylandEventInterface *iface, + gpointer user_data) +{ + MetaWaylandEventHandler *handler; + + handler = g_new0 (MetaWaylandEventHandler, 1); + handler->iface = iface; + handler->input = input; + handler->user_data = user_data; + wl_list_init (&handler->link); + wl_list_insert (&input->event_handler_list, &handler->link); + + meta_wayland_event_handler_invalidate_all_focus (handler); + + return handler; +} + +void +meta_wayland_input_detach_event_handler (MetaWaylandInput *input, + MetaWaylandEventHandler *handler) +{ + gboolean handler_change = FALSE; + + handler_change = meta_wayland_input_is_current_handler (input, handler); + wl_list_remove (&handler->link); + + if (handler_change && !wl_list_empty (&input->event_handler_list)) + { + MetaWaylandEventHandler *head = + wl_container_of (input->event_handler_list.next, + head, link); + + meta_wayland_event_handler_invalidate_all_focus (head); + } + + g_free (handler); +} + +gboolean +meta_wayland_input_is_current_handler (MetaWaylandInput *input, + MetaWaylandEventHandler *handler) +{ + return input->event_handler_list.next == &handler->link; +} + +gboolean +meta_wayland_input_handle_event (MetaWaylandInput *input, + const ClutterEvent *event) +{ + MetaWaylandEventHandler *handler, *next; + gboolean retval = CLUTTER_EVENT_PROPAGATE; + ClutterEventType event_type = clutter_event_type (event); + + wl_list_for_each_safe (handler, next, &input->event_handler_list, link) + { + retval = meta_wayland_event_handler_handle_event (handler, event); + if (retval == CLUTTER_EVENT_STOP) + break; + /* Event handlers propagate focus themselves */ + if (event_type == CLUTTER_ENTER || + event_type == CLUTTER_LEAVE) + break; + } + + return retval; +} + +void +meta_wayland_input_invalidate_focus (MetaWaylandInput *input, + ClutterInputDevice *device, + ClutterEventSequence *sequence) +{ + if (!wl_list_empty (&input->event_handler_list)) + { + MetaWaylandEventHandler *head = + wl_container_of (input->event_handler_list.next, + head, link); + + meta_wayland_event_handler_invalidate_focus (head, device, sequence); + } +} + +MetaWaylandSurface * +meta_wayland_event_handler_chain_up_get_focus_surface (MetaWaylandEventHandler *handler, + ClutterInputDevice *device, + ClutterEventSequence *sequence) +{ + MetaWaylandEventHandler *next; + + g_assert (!wl_list_empty (&handler->link)); + + next = wl_container_of (handler->link.next, next, link); + + return next->iface->get_focus_surface (next, device, sequence, next->user_data); +} + +void +meta_wayland_event_handler_chain_up_focus (MetaWaylandEventHandler *handler, + ClutterInputDevice *device, + ClutterEventSequence *sequence, + MetaWaylandSurface *surface) +{ + MetaWaylandEventHandler *next; + + g_assert (!wl_list_empty (&handler->link)); + + next = wl_container_of (handler->link.next, next, link); + + return next->iface->focus (next, device, sequence, surface, next->user_data); +} diff --git a/src/wayland/meta-wayland-input.h b/src/wayland/meta-wayland-input.h new file mode 100644 index 000000000..5eff00435 --- /dev/null +++ b/src/wayland/meta-wayland-input.h @@ -0,0 +1,102 @@ +/* + * Interface for Wayland events + * + * Copyright (C) 2023 Red Hat Inc. + * + * 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, see . + * + * Author: Carlos Garnacho + */ + +#pragma once + +#include + +#include "clutter/clutter.h" +#include "wayland/meta-wayland-types.h" + +#define META_TYPE_WAYLAND_INPUT (meta_wayland_input_get_type ()) +G_DECLARE_FINAL_TYPE (MetaWaylandInput, + meta_wayland_input, + META, WAYLAND_INPUT, + GObject); + +typedef struct _MetaWaylandEventHandler MetaWaylandEventHandler; + +typedef struct _MetaWaylandEventInterface MetaWaylandEventInterface; + +struct _MetaWaylandEventInterface +{ + MetaWaylandSurface * (*get_focus_surface) (MetaWaylandEventHandler *handler, + ClutterInputDevice *device, + ClutterEventSequence *sequence, + gpointer user_data); + + /* Pointer/stylus/touch */ + void (*focus) (MetaWaylandEventHandler *handler, + ClutterInputDevice *device, + ClutterEventSequence *sequence, + MetaWaylandSurface *surface, + gpointer user_data); + + gboolean (*motion) (MetaWaylandEventHandler *handler, + const ClutterEvent *event, + gpointer user_data); + + gboolean (*press) (MetaWaylandEventHandler *handler, + const ClutterEvent *event, + gpointer user_data); + + gboolean (*release) (MetaWaylandEventHandler *handler, + const ClutterEvent *event, + gpointer user_data); + + /* Key */ + gboolean (*key) (MetaWaylandEventHandler *handler, + const ClutterEvent *event, + gpointer user_data); + + /* Other (Pads/IM/...) */ + gboolean (*other) (MetaWaylandEventHandler *handler, + const ClutterEvent *event, + gpointer user_data); +}; + +MetaWaylandInput * meta_wayland_input_new (MetaWaylandSeat *seat); + +MetaWaylandEventHandler * meta_wayland_input_attach_event_handler (MetaWaylandInput *input, + const MetaWaylandEventInterface *iface, + gpointer user_data); + +gboolean meta_wayland_input_is_current_handler (MetaWaylandInput *input, + MetaWaylandEventHandler *handler); + +void meta_wayland_input_detach_event_handler (MetaWaylandInput *input, + MetaWaylandEventHandler *handler); + +gboolean meta_wayland_input_handle_event (MetaWaylandInput *input, + const ClutterEvent *event); + +void meta_wayland_input_invalidate_focus (MetaWaylandInput *input, + ClutterInputDevice *device, + ClutterEventSequence *sequence); + +MetaWaylandSurface * meta_wayland_event_handler_chain_up_get_focus_surface (MetaWaylandEventHandler *handler, + ClutterInputDevice *device, + ClutterEventSequence *sequence); + +void meta_wayland_event_handler_chain_up_focus (MetaWaylandEventHandler *handler, + ClutterInputDevice *device, + ClutterEventSequence *sequence, + MetaWaylandSurface *surface);