[mutter/wip/tablet-protocol: 94/100] wayland: Add focus management to MetaWaylandTabletTool



commit c92cf35058e0f89b6e72b0eaabef2e73d8173c79
Author: Carlos Garnacho <carlosg gnome org>
Date:   Wed Oct 28 11:08:09 2015 +0100

    wayland: Add focus management to MetaWaylandTabletTool
    
    Tools can now switch between surfaces, which implies the emission
    of wl_tablet_tool.proximity_in/out events.

 src/wayland/meta-wayland-tablet-tool.c |  262 +++++++++++++++++++++++++++++++-
 src/wayland/meta-wayland-tablet-tool.h |   11 ++
 2 files changed, 269 insertions(+), 4 deletions(-)
---
diff --git a/src/wayland/meta-wayland-tablet-tool.c b/src/wayland/meta-wayland-tablet-tool.c
index a1d57b8..01221ed 100644
--- a/src/wayland/meta-wayland-tablet-tool.c
+++ b/src/wayland/meta-wayland-tablet-tool.c
@@ -30,6 +30,9 @@
 #include <wayland-server.h>
 #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);
@@ -90,6 +227,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,
@@ -103,7 +258,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;
 }
@@ -112,21 +277,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;
+
+  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));
+}
 
-  return wl_resource_find_for_client (&tool->resource_list, client);
+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 04f7db8..12510c4 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,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]