diff --git a/src/wayland/meta-wayland-tablet-tool.c b/src/wayland/meta-wayland-tablet-tool.c index 55544055d..0d031566a 100644 --- a/src/wayland/meta-wayland-tablet-tool.c +++ b/src/wayland/meta-wayland-tablet-tool.c @@ -30,6 +30,9 @@ #include #include "tablet-unstable-v1-server-protocol.h" #include "meta-wayland-private.h" +#include "meta-surface-actor-wayland.h" +#include "meta-wayland-tablet.h" +#include "meta-wayland-tablet-seat.h" #include "meta-wayland-tablet-tool.h" static void @@ -38,6 +41,135 @@ unbind_resource (struct wl_resource *resource) wl_list_remove (wl_resource_get_link (resource)); } +static void +move_resources (struct wl_list *destination, + struct wl_list *source) +{ + wl_list_insert_list (destination, source); + wl_list_init (source); +} + +static void +move_resources_for_client (struct wl_list *destination, + struct wl_list *source, + struct wl_client *client) +{ + struct wl_resource *resource, *tmp; + + wl_resource_for_each_safe (resource, tmp, source) + { + if (wl_resource_get_client (resource) == client) + { + wl_list_remove (wl_resource_get_link (resource)); + wl_list_insert (destination, wl_resource_get_link (resource)); + } + } +} + +static void +broadcast_proximity_in (MetaWaylandTabletTool *tool) +{ + struct wl_resource *resource, *tablet_resource; + struct wl_client *client; + + client = wl_resource_get_client (tool->focus_surface->resource); + tablet_resource = meta_wayland_tablet_lookup_resource (tool->current_tablet, + client); + + wl_resource_for_each (resource, &tool->focus_resource_list) + { + zwp_tablet_tool_v1_send_proximity_in (resource, tool->proximity_serial, + tablet_resource, + tool->focus_surface->resource); + } +} + +static void +broadcast_proximity_out (MetaWaylandTabletTool *tool) +{ + struct wl_resource *resource; + + wl_resource_for_each (resource, &tool->focus_resource_list) + { + zwp_tablet_tool_v1_send_proximity_out (resource); + } +} + +static void +broadcast_frame (MetaWaylandTabletTool *tool, + const ClutterEvent *event) +{ + struct wl_resource *resource; + guint32 _time = event ? clutter_event_get_time (event) : CLUTTER_CURRENT_TIME; + + wl_resource_for_each (resource, &tool->focus_resource_list) + { + zwp_tablet_tool_v1_send_frame (resource, _time); + } +} + +static void +meta_wayland_tablet_tool_set_focus (MetaWaylandTabletTool *tool, + MetaWaylandSurface *surface, + const ClutterEvent *event) +{ + if (tool->focus_surface == surface) + return; + + if (tool->focus_surface != NULL) + { + struct wl_list *l; + + l = &tool->focus_resource_list; + if (!wl_list_empty (l)) + { + broadcast_proximity_out (tool); + broadcast_frame (tool, event); + move_resources (&tool->resource_list, &tool->focus_resource_list); + } + + wl_list_remove (&tool->focus_surface_destroy_listener.link); + tool->focus_surface = NULL; + } + + if (surface != NULL) + { + struct wl_client *client; + struct wl_list *l; + + tool->focus_surface = surface; + client = wl_resource_get_client (tool->focus_surface->resource); + wl_resource_add_destroy_listener (tool->focus_surface->resource, + &tool->focus_surface_destroy_listener); + + move_resources_for_client (&tool->focus_resource_list, + &tool->resource_list, client); + + l = &tool->focus_resource_list; + + if (!wl_list_empty (l)) + { + struct wl_client *client = wl_resource_get_client (tool->focus_surface->resource); + struct wl_display *display = wl_client_get_display (client); + + tool->proximity_serial = wl_display_next_serial (display); + + broadcast_proximity_in (tool); + broadcast_frame (tool, event); + } + } +} + +static void +tablet_tool_handle_focus_surface_destroy (struct wl_listener *listener, + void *data) +{ + MetaWaylandTabletTool *tool; + + tool = wl_container_of (listener, tool, focus_surface_destroy_listener); + meta_wayland_tablet_tool_set_focus (tool, NULL, NULL); +} + MetaWaylandTabletTool * meta_wayland_tablet_tool_new (MetaWaylandTabletSeat *seat, ClutterInputDevice *device, @@ -50,6 +182,9 @@ meta_wayland_tablet_tool_new (MetaWaylandTabletSeat *seat, tool->device = device; tool->device_tool = device_tool; wl_list_init (&tool->resource_list); + wl_list_init (&tool->focus_resource_list); + + tool->focus_surface_destroy_listener.notify = tablet_tool_handle_focus_surface_destroy; return tool; } @@ -59,6 +194,8 @@ meta_wayland_tablet_tool_free (MetaWaylandTabletTool *tool) { struct wl_resource *resource, *next; + meta_wayland_tablet_tool_set_focus (tool, NULL, NULL); + wl_resource_for_each_safe (resource, next, &tool->resource_list) { zwp_tablet_tool_v1_send_removed (resource); @@ -89,6 +226,24 @@ static const struct zwp_tablet_tool_v1_interface tool_interface = { tool_destroy }; +static void +emit_proximity_in (MetaWaylandTabletTool *tool, + struct wl_resource *resource) +{ + struct wl_resource *tablet_resource; + struct wl_client *client; + + if (!tool->focus_surface) + return; + + client = wl_resource_get_client (resource); + tablet_resource = meta_wayland_tablet_lookup_resource (tool->current_tablet, + client); + + zwp_tablet_tool_v1_send_proximity_in (resource, tool->proximity_serial, + tablet_resource, tool->focus_surface->resource); +} + struct wl_resource * meta_wayland_tablet_tool_create_new_resource (MetaWaylandTabletTool *tool, struct wl_client *client, @@ -102,7 +257,17 @@ meta_wayland_tablet_tool_create_new_resource (MetaWaylandTabletTool *tool, wl_resource_set_implementation (resource, &tool_interface, tool, unbind_resource); wl_resource_set_user_data (resource, tool); - wl_list_insert (&tool->resource_list, wl_resource_get_link (resource)); + + if (tool->focus_surface && + wl_resource_get_client (tool->focus_surface->resource) == client) + { + wl_list_insert (&tool->focus_resource_list, wl_resource_get_link (resource)); + emit_proximity_in (tool, resource); + } + else + { + wl_list_insert (&tool->resource_list, wl_resource_get_link (resource)); + } return resource; } @@ -111,21 +276,110 @@ struct wl_resource * meta_wayland_tablet_tool_lookup_resource (MetaWaylandTabletTool *tool, struct wl_client *client) { - if (wl_list_empty (&tool->resource_list)) - return NULL; + struct wl_resource *resource = NULL; - return wl_resource_find_for_client (&tool->resource_list, client); + if (!wl_list_empty (&tool->resource_list)) + resource = wl_resource_find_for_client (&tool->resource_list, client); + + if (!wl_list_empty (&tool->focus_resource_list)) + resource = wl_resource_find_for_client (&tool->focus_resource_list, client); + + return resource; +} + +static void +meta_wayland_tablet_tool_account_button (MetaWaylandTabletTool *tool, + const ClutterEvent *event) +{ + if (event->type == CLUTTER_BUTTON_PRESS) + tool->pressed_buttons |= 1 << (event->button.button - 1); + else if (event->type == CLUTTER_BUTTON_RELEASE) + tool->pressed_buttons &= ~(1 << (event->button.button - 1)); +} + +static void +sync_focus_surface (MetaWaylandTabletTool *tool, + const ClutterEvent *event) +{ + MetaDisplay *display = meta_get_display (); + + 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_tablet_tool_set_focus (tool, NULL, event); + break; + + case META_EVENT_ROUTE_NORMAL: + case META_EVENT_ROUTE_WAYLAND_POPUP: + meta_wayland_tablet_tool_set_focus (tool, tool->current, event); + break; + + default: + g_assert_not_reached (); + } +} + +static void +repick_for_event (MetaWaylandTabletTool *tool, + const ClutterEvent *for_event) +{ + ClutterActor *actor = NULL; + + actor = clutter_event_get_source (for_event); + + if (META_IS_SURFACE_ACTOR_WAYLAND (actor)) + tool->current = meta_surface_actor_wayland_get_surface (META_SURFACE_ACTOR_WAYLAND (actor)); + else + tool->current = NULL; + + sync_focus_surface (tool, for_event); } void meta_wayland_tablet_tool_update (MetaWaylandTabletTool *tool, const ClutterEvent *event) { + switch (event->type) + { + case CLUTTER_BUTTON_PRESS: + case CLUTTER_BUTTON_RELEASE: + meta_wayland_tablet_tool_account_button (tool, event); + break; + case CLUTTER_MOTION: + if (!tool->pressed_buttons) + repick_for_event (tool, event); + break; + case CLUTTER_PROXIMITY_IN: + tool->current_tablet = + meta_wayland_tablet_seat_lookup_tablet (tool->seat, + clutter_event_get_source_device (event)); + break; + default: + break; + } } gboolean meta_wayland_tablet_tool_handle_event (MetaWaylandTabletTool *tool, const ClutterEvent *event) { + switch (event->type) + { + case CLUTTER_PROXIMITY_IN: + /* We don't have much info here to make anything useful out of it, + * wait until the first motion event so we have both coordinates + * and tool. + */ + break; + case CLUTTER_PROXIMITY_OUT: + meta_wayland_tablet_tool_set_focus (tool, NULL, event); + break; + default: + return CLUTTER_EVENT_PROPAGATE; + } + return CLUTTER_EVENT_STOP; } diff --git a/src/wayland/meta-wayland-tablet-tool.h b/src/wayland/meta-wayland-tablet-tool.h index 04f7db8d6..12510c4dc 100644 --- a/src/wayland/meta-wayland-tablet-tool.h +++ b/src/wayland/meta-wayland-tablet-tool.h @@ -35,6 +35,17 @@ struct _MetaWaylandTabletTool ClutterInputDevice *device; ClutterInputDeviceTool *device_tool; struct wl_list resource_list; + struct wl_list focus_resource_list; + + MetaWaylandSurface *focus_surface; + struct wl_listener focus_surface_destroy_listener; + + MetaWaylandSurface *current; + guint32 pressed_buttons; + + guint32 proximity_serial; + + MetaWaylandTablet *current_tablet; }; MetaWaylandTabletTool * meta_wayland_tablet_tool_new (MetaWaylandTabletSeat *seat,