diff --git a/src/wayland/meta-wayland-pointer.c b/src/wayland/meta-wayland-pointer.c index 06e3cf713..c15e39fcc 100644 --- a/src/wayland/meta-wayland-pointer.c +++ b/src/wayland/meta-wayland-pointer.c @@ -1139,32 +1139,6 @@ meta_wayland_pointer_cancel_grab (MetaWaylandPointer *pointer) 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, @@ -1384,18 +1358,6 @@ 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) diff --git a/src/wayland/meta-wayland-pointer.h b/src/wayland/meta-wayland-pointer.h index 5e64fe623..b25fccbca 100644 --- a/src/wayland/meta-wayland-pointer.h +++ b/src/wayland/meta-wayland-pointer.h @@ -122,11 +122,6 @@ void meta_wayland_pointer_start_grab (MetaWaylandPointer *pointer, void meta_wayland_pointer_end_grab (MetaWaylandPointer *pointer); -MetaWaylandPopup *meta_wayland_pointer_start_popup_grab (MetaWaylandPointer *pointer, - MetaWaylandPopupSurface *popup_surface); - -void meta_wayland_pointer_end_popup_grab (MetaWaylandPointer *pointer); - void meta_wayland_pointer_get_relative_coordinates (MetaWaylandPointer *pointer, MetaWaylandSurface *surface, wl_fixed_t *x, @@ -144,8 +139,6 @@ gboolean meta_wayland_pointer_can_grab_surface (MetaWaylandPointer *pointer, gboolean meta_wayland_pointer_can_popup (MetaWaylandPointer *pointer, uint32_t serial); -MetaWaylandSurface *meta_wayland_pointer_get_top_popup (MetaWaylandPointer *pointer); - MetaWaylandPointerClient * meta_wayland_pointer_get_pointer_client (MetaWaylandPointer *pointer, struct wl_client *client); void meta_wayland_pointer_unbind_pointer_client_resource (struct wl_resource *resource); diff --git a/src/wayland/meta-wayland-popup.c b/src/wayland/meta-wayland-popup.c index 3777e9c4d..2af5cb853 100644 --- a/src/wayland/meta-wayland-popup.c +++ b/src/wayland/meta-wayland-popup.c @@ -53,7 +53,10 @@ G_DEFINE_INTERFACE (MetaWaylandPopupSurface, meta_wayland_popup_surface, struct _MetaWaylandPopupGrab { - MetaWaylandPointerGrab generic; + MetaWaylandSeat *seat; + MetaWaylandEventHandler *handler; + + int press_count; struct wl_client *grab_client; struct wl_list all_popups; @@ -66,12 +69,7 @@ struct _MetaWaylandPopup struct wl_list link; }; -static void -meta_wayland_popup_grab_begin (MetaWaylandPopupGrab *grab, - MetaWaylandSurface *surface); - -static void -meta_wayland_popup_grab_end (MetaWaylandPopupGrab *grab); +static void meta_wayland_popup_grab_finish (MetaWaylandPopupGrab *grab); static void meta_wayland_popup_surface_default_init (MetaWaylandPopupSurfaceInterface *iface) @@ -90,121 +88,165 @@ meta_wayland_popup_surface_dismiss (MetaWaylandPopupSurface *popup_surface) META_WAYLAND_POPUP_SURFACE_GET_IFACE (popup_surface)->dismiss (popup_surface); } +static void +meta_wayland_popup_surface_finish (MetaWaylandPopupSurface *popup_surface) +{ + META_WAYLAND_POPUP_SURFACE_GET_IFACE (popup_surface)->finish (popup_surface); +} + static MetaWaylandSurface * meta_wayland_popup_surface_get_surface (MetaWaylandPopupSurface *popup_surface) { return META_WAYLAND_POPUP_SURFACE_GET_IFACE (popup_surface)->get_surface (popup_surface); } -static void -popup_grab_focus (MetaWaylandPointerGrab *grab, - MetaWaylandSurface *surface) +static MetaWaylandSurface * +popup_grab_get_focus_surface (MetaWaylandEventHandler *handler, + ClutterInputDevice *device, + ClutterEventSequence *sequence, + gpointer user_data) { - MetaWaylandPopupGrab *popup_grab = (MetaWaylandPopupGrab*)grab; - MetaWaylandSeat *seat = meta_wayland_pointer_get_seat (grab->pointer); - MetaWaylandPointer *pointer = grab->pointer; + MetaWaylandPopupGrab *popup_grab = user_data; + ClutterSeat *clutter_seat = clutter_input_device_get_seat (device); + MetaWaylandSurface *surface; - /* - * We rely on having a pointer grab even when the seat doesn't have - * the pointer capability. In this case, we shouldn't update any pointer focus - * since there is no such thing when the seat doesn't have the pointer - * capability. - */ - if (!meta_wayland_seat_has_pointer (seat)) - return; + if (device == clutter_seat_get_keyboard (clutter_seat) && + !wl_list_empty (&popup_grab->all_popups)) + { + /* Keyboard focus must always go to the topmost surface */ + return meta_wayland_popup_grab_get_top_popup (popup_grab); + } + else + { + surface = meta_wayland_event_handler_chain_up_get_focus_surface (handler, + device, + sequence); - /* Popup grabs are in owner-events mode (ie, events for the same client - are reported as normal) */ - if (surface && - wl_resource_get_client (surface->resource) == popup_grab->grab_client) - meta_wayland_pointer_set_focus (grab->pointer, surface); - else if (pointer->button_count == 0) - meta_wayland_pointer_set_focus (grab->pointer, NULL); + if (surface && + wl_resource_get_client (surface->resource) == popup_grab->grab_client) + return surface; + } + + return NULL; } static void -popup_grab_motion (MetaWaylandPointerGrab *grab, - const ClutterEvent *event) +popup_grab_focus (MetaWaylandEventHandler *handler, + ClutterInputDevice *device, + ClutterEventSequence *sequence, + MetaWaylandSurface *surface, + gpointer user_data) { - meta_wayland_pointer_send_motion (grab->pointer, event); + meta_wayland_event_handler_chain_up_focus (handler, device, sequence, surface); } -static void -popup_grab_button (MetaWaylandPointerGrab *grab, - const ClutterEvent *event) +static gboolean +popup_grab_press (MetaWaylandEventHandler *handler, + const ClutterEvent *event, + gpointer user_data) { - MetaWaylandPointer *pointer = grab->pointer; + MetaWaylandPopupGrab *popup_grab = user_data; - if (pointer->focus_surface) - meta_wayland_pointer_send_button (grab->pointer, event); - else if (clutter_event_type (event) == CLUTTER_BUTTON_RELEASE && - pointer->button_count == 0) - meta_wayland_pointer_end_popup_grab (grab->pointer); + popup_grab->press_count++; + + return CLUTTER_EVENT_PROPAGATE; } -static void -popup_grab_cancel (MetaWaylandPointerGrab *grab) +static gboolean +popup_grab_release (MetaWaylandEventHandler *handler, + const ClutterEvent *event, + gpointer user_data) { - meta_wayland_pointer_end_popup_grab (grab->pointer); + MetaWaylandPopupGrab *popup_grab = user_data; + ClutterInputDevice *device = clutter_event_get_source_device (event); + ClutterEventSequence *sequence = clutter_event_get_event_sequence (event); + + popup_grab->press_count = MAX (0, popup_grab->press_count - 1); + + if (popup_grab->press_count == 0) + { + MetaWaylandSurface *surface; + + surface = meta_wayland_event_handler_chain_up_get_focus_surface (popup_grab->handler, + device, + sequence); + if (!surface || + wl_resource_get_client (surface->resource) != popup_grab->grab_client) + { + meta_wayland_popup_grab_finish (popup_grab); + return CLUTTER_EVENT_STOP; + } + } + + return CLUTTER_EVENT_PROPAGATE; } -static MetaWaylandPointerGrabInterface popup_grab_interface = { +static MetaWaylandEventInterface popup_event_interface = { + popup_grab_get_focus_surface, popup_grab_focus, - popup_grab_motion, - popup_grab_button, - popup_grab_cancel + NULL, /* motion */ + popup_grab_press, + popup_grab_release, }; MetaWaylandPopupGrab * -meta_wayland_popup_grab_create (MetaWaylandPointer *pointer, +meta_wayland_popup_grab_create (MetaWaylandSeat *seat, MetaWaylandPopupSurface *popup_surface) { MetaWaylandSurface *surface = meta_wayland_popup_surface_get_surface (popup_surface); struct wl_client *client = wl_resource_get_client (surface->resource); + MetaWaylandInput *input = meta_wayland_seat_get_input (seat); MetaWaylandPopupGrab *grab; grab = g_new0 (MetaWaylandPopupGrab, 1); - grab->generic.interface = &popup_grab_interface; - grab->generic.pointer = pointer; + grab->seat = seat; grab->grab_client = client; wl_list_init (&grab->all_popups); - meta_wayland_popup_grab_begin (grab, surface); + grab->handler = + meta_wayland_input_attach_event_handler (input, + &popup_event_interface, + grab); return grab; } void -meta_wayland_popup_grab_destroy (MetaWaylandPopupGrab *grab) -{ - meta_wayland_popup_grab_end (grab); - g_free (grab); -} - -static void -meta_wayland_popup_grab_begin (MetaWaylandPopupGrab *grab, - MetaWaylandSurface *surface) -{ - MetaWaylandPointer *pointer = grab->generic.pointer; - - meta_wayland_pointer_start_grab (pointer, (MetaWaylandPointerGrab*)grab); -} - -void -meta_wayland_popup_grab_end (MetaWaylandPopupGrab *grab) +meta_wayland_popup_grab_finish (MetaWaylandPopupGrab *grab) { MetaWaylandPopup *popup, *tmp; - g_assert (grab->generic.interface == &popup_grab_interface); - wl_list_for_each_safe (popup, tmp, &grab->all_popups, link) { - meta_wayland_popup_surface_done (popup->popup_surface); + MetaWaylandPopupSurface *popup_surface = popup->popup_surface; + + meta_wayland_popup_surface_done (popup_surface); meta_wayland_popup_destroy (popup); + meta_wayland_popup_surface_finish (popup_surface); + } +} + +void +meta_wayland_popup_grab_destroy (MetaWaylandPopupGrab *grab) +{ + g_assert (wl_list_empty (&grab->all_popups)); + + if (grab->handler) + { + MetaWaylandInput *input = meta_wayland_seat_get_input (grab->seat); + + meta_wayland_input_detach_event_handler (input, grab->handler); + grab->handler = NULL; } - meta_wayland_pointer_end_grab (grab->generic.pointer); + g_free (grab); +} + +gboolean +meta_wayland_popup_grab_has_popups (MetaWaylandPopupGrab *grab) +{ + return !wl_list_empty (&grab->all_popups); } MetaWaylandSurface * @@ -218,41 +260,45 @@ meta_wayland_popup_grab_get_top_popup (MetaWaylandPopupGrab *grab) return meta_wayland_popup_surface_get_surface (popup->popup_surface); } -gboolean -meta_wayland_pointer_grab_is_popup_grab (MetaWaylandPointerGrab *grab) -{ - return grab->interface == &popup_grab_interface; -} - void meta_wayland_popup_destroy (MetaWaylandPopup *popup) { meta_wayland_popup_surface_dismiss (popup->popup_surface); - wl_list_remove (&popup->link); g_free (popup); } +static void +meta_wayland_popup_grab_repick_keyboard_focus (MetaWaylandPopupGrab *popup_grab) +{ + MetaWaylandSeat *seat = popup_grab->seat; + MetaContext *context = + meta_wayland_compositor_get_context (seat->compositor); + MetaBackend *backend = meta_context_get_backend (context); + ClutterBackend *clutter_backend = + meta_backend_get_clutter_backend (backend); + ClutterSeat *clutter_seat = + clutter_backend_get_default_seat (clutter_backend); + MetaWaylandInput *input; + + input = meta_wayland_seat_get_input (seat); + meta_wayland_input_invalidate_focus (input, + clutter_seat_get_keyboard (clutter_seat), + NULL); +} + void meta_wayland_popup_dismiss (MetaWaylandPopup *popup) { + MetaWaylandPopupSurface *popup_surface = popup->popup_surface; MetaWaylandPopupGrab *popup_grab = popup->grab; meta_wayland_popup_destroy (popup); if (wl_list_empty (&popup_grab->all_popups)) - { - meta_wayland_pointer_end_popup_grab (popup_grab->generic.pointer); - } + meta_wayland_popup_surface_finish (popup_surface); else - { - MetaWaylandSurface *top_popup_surface; - MetaWaylandSeat *seat; - - top_popup_surface = meta_wayland_popup_grab_get_top_popup (popup_grab); - seat = meta_wayland_pointer_get_seat (popup_grab->generic.pointer); - meta_wayland_seat_set_input_focus (seat, top_popup_surface); - } + meta_wayland_popup_grab_repick_keyboard_focus (popup_grab); } MetaWaylandSurface * @@ -268,7 +314,6 @@ meta_wayland_popup_create (MetaWaylandPopupSurface *popup_surface, MetaWaylandSurface *surface = meta_wayland_popup_surface_get_surface (popup_surface); MetaWaylandPopup *popup; - MetaWaylandSeat *seat; /* Don't allow creating popups if the grab has a different client. */ if (grab->grab_client != wl_resource_get_client (surface->resource)) @@ -280,8 +325,7 @@ meta_wayland_popup_create (MetaWaylandPopupSurface *popup_surface, wl_list_insert (&grab->all_popups, &popup->link); - seat = meta_wayland_pointer_get_seat (grab->generic.pointer); - meta_wayland_seat_set_input_focus (seat, surface); + meta_wayland_popup_grab_repick_keyboard_focus (grab); return popup; } diff --git a/src/wayland/meta-wayland-popup.h b/src/wayland/meta-wayland-popup.h index 19a3c0094..cf15767b2 100644 --- a/src/wayland/meta-wayland-popup.h +++ b/src/wayland/meta-wayland-popup.h @@ -37,17 +37,18 @@ struct _MetaWaylandPopupSurfaceInterface void (*done) (MetaWaylandPopupSurface *popup_surface); void (*dismiss) (MetaWaylandPopupSurface *popup_surface); + void (*finish) (MetaWaylandPopupSurface *popup_surface); MetaWaylandSurface *(*get_surface) (MetaWaylandPopupSurface *popup_surface); }; -MetaWaylandPopupGrab *meta_wayland_popup_grab_create (MetaWaylandPointer *pointer, +MetaWaylandPopupGrab *meta_wayland_popup_grab_create (MetaWaylandSeat *seat, MetaWaylandPopupSurface *popup_surface); void meta_wayland_popup_grab_destroy (MetaWaylandPopupGrab *grab); -MetaWaylandSurface *meta_wayland_popup_grab_get_top_popup (MetaWaylandPopupGrab *grab); +gboolean meta_wayland_popup_grab_has_popups (MetaWaylandPopupGrab *grab); -gboolean meta_wayland_pointer_grab_is_popup_grab (MetaWaylandPointerGrab *grab); +MetaWaylandSurface *meta_wayland_popup_grab_get_top_popup (MetaWaylandPopupGrab *grab); MetaWaylandPopup *meta_wayland_popup_create (MetaWaylandPopupSurface *surface, MetaWaylandPopupGrab *grab); diff --git a/src/wayland/meta-wayland-xdg-shell.c b/src/wayland/meta-wayland-xdg-shell.c index 91df6ec0f..28b39577b 100644 --- a/src/wayland/meta-wayland-xdg-shell.c +++ b/src/wayland/meta-wayland-xdg-shell.c @@ -55,6 +55,7 @@ typedef struct _MetaWaylandXdgShellClient struct wl_resource *resource; GList *surfaces; GList *surface_constructors; + MetaWaylandPopupGrab *popup_grab; } MetaWaylandXdgShellClient; struct _MetaWaylandXdgPositioner @@ -1125,6 +1126,9 @@ finish_popup_setup (MetaWaylandXdgPopup *xdg_popup) MetaWaylandShellSurface *shell_surface = META_WAYLAND_SHELL_SURFACE (xdg_surface); MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (xdg_popup); + MetaWaylandXdgSurfacePrivate *xdg_surface_priv = + meta_wayland_xdg_surface_get_instance_private (xdg_surface); + MetaWaylandXdgShellClient *xdg_shell_client = xdg_surface_priv->shell_client; struct wl_resource *xdg_wm_base_resource = meta_wayland_xdg_surface_get_wm_base_resource (xdg_surface); MetaWaylandSurface *surface = @@ -1153,7 +1157,7 @@ finish_popup_setup (MetaWaylandXdgPopup *xdg_popup) if (seat) { - MetaWaylandSurface *top_popup; + MetaWaylandSurface *top_popup = NULL; if (!meta_wayland_seat_can_popup (seat, serial)) { @@ -1161,7 +1165,9 @@ finish_popup_setup (MetaWaylandXdgPopup *xdg_popup) return; } - top_popup = meta_wayland_pointer_get_top_popup (seat->pointer); + if (xdg_shell_client->popup_grab) + top_popup = meta_wayland_popup_grab_get_top_popup (xdg_shell_client->popup_grab); + if (top_popup && parent_surface != top_popup) { wl_resource_post_error (xdg_wm_base_resource, @@ -1193,8 +1199,16 @@ finish_popup_setup (MetaWaylandXdgPopup *xdg_popup) meta_window_focus (window, meta_display_get_current_time (display)); popup_surface = META_WAYLAND_POPUP_SURFACE (surface->role); - popup = meta_wayland_pointer_start_popup_grab (seat->pointer, - popup_surface); + + if (!xdg_shell_client->popup_grab) + { + xdg_shell_client->popup_grab = + meta_wayland_popup_grab_create (seat, popup_surface); + } + + popup = meta_wayland_popup_create (popup_surface, + xdg_shell_client->popup_grab); + if (popup == NULL) { xdg_popup_send_popup_done (xdg_popup->resource); @@ -1506,6 +1520,23 @@ meta_wayland_xdg_popup_dismiss (MetaWaylandPopupSurface *popup_surface) meta_wayland_xdg_popup_unmap (xdg_popup); } +static void +meta_wayland_xdg_popup_finish (MetaWaylandPopupSurface *popup_surface) +{ + MetaWaylandXdgPopup *xdg_popup = META_WAYLAND_XDG_POPUP (popup_surface); + MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (xdg_popup); + MetaWaylandXdgSurfacePrivate *xdg_surface_priv = + meta_wayland_xdg_surface_get_instance_private (xdg_surface); + MetaWaylandXdgShellClient *xdg_shell_client = xdg_surface_priv->shell_client; + + if (xdg_shell_client->popup_grab && + !meta_wayland_popup_grab_has_popups (xdg_shell_client->popup_grab)) + { + meta_wayland_popup_grab_destroy (xdg_shell_client->popup_grab); + xdg_shell_client->popup_grab = NULL; + } +} + static MetaWaylandSurface * meta_wayland_xdg_popup_get_surface (MetaWaylandPopupSurface *popup_surface) { @@ -1520,6 +1551,7 @@ popup_surface_iface_init (MetaWaylandPopupSurfaceInterface *iface) { iface->done = meta_wayland_xdg_popup_done; iface->dismiss = meta_wayland_xdg_popup_dismiss; + iface->finish = meta_wayland_xdg_popup_finish; iface->get_surface = meta_wayland_xdg_popup_get_surface; }