[mutter/wip/wayland-input: 2/8] wayland: Add basic mouse input support



commit 7e14e7986fbc429e391c64b5e68d2d7e9ff5b615
Author: Neil Roberts <neil linux intel com>
Date:   Wed Jan 11 18:33:13 2012 +0000

    wayland: Add basic mouse input support
    
    This copies the basic mouse support from the test-wayland-surface demo
    in the Clutter source. It adds a basic wl_input_device implementation
    which can convert Clutter mouse events to Wayland events. For this to
    work all of the wayland surface actors need to be made reactive.

 src/Makefile.am                         |    4 +-
 src/core/display-private.h              |    3 +
 src/core/display.c                      |   38 ++++-
 src/wayland/meta-wayland-input-device.c |  242 +++++++++++++++++++++++++++++++
 src/wayland/meta-wayland-input-device.h |   37 +++++
 src/wayland/meta-wayland-private.h      |   13 ++
 src/wayland/meta-wayland.c              |  122 ++++++++++++++++
 7 files changed, 449 insertions(+), 10 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index b8881e2..47209b3 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -168,7 +168,9 @@ libmutter_la_SOURCES =				\
 if HAVE_WAYLAND
 libmutter_la_SOURCES +=				\
 	wayland/meta-wayland.c			\
-	wayland/meta-wayland-private.h
+	wayland/meta-wayland-private.h		\
+	wayland/meta-wayland-input-device.h	\
+	wayland/meta-wayland-input-device.c
 endif
 
 libmutter_la_LDFLAGS = -no-undefined
diff --git a/src/core/display-private.h b/src/core/display-private.h
index 6db1b64..bbe561b 100644
--- a/src/core/display-private.h
+++ b/src/core/display-private.h
@@ -444,4 +444,7 @@ void meta_display_overlay_key_activate (MetaDisplay *display);
 /* In above-tab-keycode.c */
 guint meta_display_get_above_tab_keycode (MetaDisplay *display);
 
+gboolean meta_display_handle_event (MetaDisplay *display,
+                                    XEvent      *event);
+
 #endif
diff --git a/src/core/display.c b/src/core/display.c
index 1336b18..e3dac39 100644
--- a/src/core/display.c
+++ b/src/core/display.c
@@ -1630,26 +1630,22 @@ handle_net_restack_window (MetaDisplay* display,
  * busy around here. Most of this function is a ginormous switch statement
  * dealing with all the kinds of events that might turn up.
  *
- * \param event The event that just happened
- * \param data  The MetaDisplay that events are coming from, cast to a gpointer
- *              so that it can be sent to a callback
+ * \param display The MetaDisplay that events are coming from
+ * \param event   The event that just happened
  *
  * \ingroup main
  */
-static gboolean
-event_callback (XEvent   *event,
-                gpointer  data)
+gboolean
+meta_display_handle_event (MetaDisplay *display,
+                           XEvent      *event)
 {
   MetaWindow *window;
   MetaWindow *property_for_window;
-  MetaDisplay *display;
   Window modified;
   gboolean frame_was_receiver;
   gboolean bypass_compositor;
   gboolean filter_out_event;
 
-  display = data;
-  
 #ifdef WITH_VERBOSE_MODE
   if (dump_events)
     meta_spew_event (display, event);
@@ -2730,6 +2726,30 @@ event_callback (XEvent   *event,
   return filter_out_event;
 }
 
+static gboolean
+event_callback (XEvent  *event,
+                gpointer data)
+{
+  MetaDisplay *display = data;
+
+  /* Under Wayland we want to filter out mouse motion events so we can
+     synthesize them from the Clutter events instead. This is
+     necessary because the position in the mouse events is passed to
+     the X server relative to the position of the surface. The X
+     server then translates these back to screen coordinates based on
+     the window position. If we rely on this translatation when
+     dragging a window around then the window will jump around
+     erratically because of the lag between updating the window
+     position from the surface position. Instead we bypass the
+     translation altogether by directly using the Clutter events */
+#ifdef HAVE_WAYLAND
+  if (event->type == MotionNotify)
+    return FALSE;
+#endif
+
+  return meta_display_handle_event (display, event);
+}
+
 /* Return the window this has to do with, if any, rather
  * than the frame or root window that was selecting
  * for substructure
diff --git a/src/wayland/meta-wayland-input-device.c b/src/wayland/meta-wayland-input-device.c
new file mode 100644
index 0000000..89dcd55
--- /dev/null
+++ b/src/wayland/meta-wayland-input-device.c
@@ -0,0 +1,242 @@
+/*
+ * Wayland Support
+ *
+ * Copyright (C) 2012 Intel Corporation
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <clutter/clutter.h>
+#include <clutter/wayland/clutter-wayland-compositor.h>
+#include <clutter/wayland/clutter-wayland-surface.h>
+#include <stdlib.h>
+#include <linux/input.h>
+#include "meta-wayland-input-device.h"
+#include "meta-wayland-private.h"
+
+struct _MetaWaylandInputDevice
+{
+  struct wl_input_device parent;
+
+  ClutterActor *stage;
+};
+
+static void
+input_device_attach (struct wl_client *client,
+                     struct wl_resource *resource,
+                     uint32_t time,
+                     struct wl_resource *buffer_resource,
+                     int32_t hotspot_x,
+                     int32_t hotspot_y)
+{
+}
+
+const static struct wl_input_device_interface
+input_device_interface =
+  {
+    input_device_attach
+  };
+
+static void
+unbind_input_device (struct wl_resource *resource)
+{
+  wl_list_remove (&resource->link);
+  free (resource);
+}
+
+static void
+bind_input_device (struct wl_client *client,
+                   void *data,
+                   uint32_t version,
+                   uint32_t id)
+{
+  struct wl_input_device *device = data;
+  struct wl_resource *resource;
+
+  resource = wl_client_add_object (client,
+                                   &wl_input_device_interface,
+                                   &input_device_interface,
+                                   id,
+                                   data);
+
+  wl_list_insert (&device->resource_list, &resource->link);
+
+  resource->destroy = unbind_input_device;
+}
+
+MetaWaylandInputDevice *
+meta_wayland_input_device_new (struct wl_display *display,
+                               ClutterActor *stage)
+{
+  MetaWaylandInputDevice *device = g_new (MetaWaylandInputDevice, 1);
+
+  wl_input_device_init (&device->parent);
+  device->stage = stage;
+
+  wl_display_add_global (display,
+                         &wl_input_device_interface,
+                         device,
+                         bind_input_device);
+
+  return device;
+}
+
+static void
+handle_motion_event (MetaWaylandInputDevice *input_device,
+                     const ClutterMotionEvent *event)
+{
+  struct wl_input_device *device =
+    (struct wl_input_device *) input_device;
+
+  device->x = event->x;
+  device->y = event->y;
+
+  meta_wayland_input_device_repick (input_device,
+                                    event->time,
+                                    event->source);
+
+  device->grab->interface->motion (device->grab,
+                                   event->time,
+                                   device->grab->x,
+                                   device->grab->y);
+}
+
+static void
+handle_button_event (MetaWaylandInputDevice *input_device,
+                     const ClutterButtonEvent *event)
+{
+  struct wl_input_device *device =
+    (struct wl_input_device *) input_device;
+  gboolean state = event->type == CLUTTER_BUTTON_PRESS;
+  uint32_t button;
+
+  switch (event->button)
+    {
+      /* The evdev input right and middle button numbers are swapped
+         relative to how Clutter numbers them */
+    case 2:
+      button = BTN_MIDDLE;
+      break;
+
+    case 3:
+      button = BTN_RIGHT;
+      break;
+
+    default:
+      button = event->button + BTN_LEFT - 1;
+      break;
+    }
+
+  if (state)
+    {
+      if (device->button_count == 0)
+        {
+          device->grab_button = button;
+          device->grab_time = event->time;
+          device->grab_x = device->x;
+          device->grab_y = device->y;
+        }
+
+      device->button_count++;
+    }
+  else
+    device->button_count--;
+
+  device->grab->interface->button (device->grab, event->time, button, state);
+}
+
+void
+meta_wayland_input_device_handle_event (MetaWaylandInputDevice *input_device,
+                                        const ClutterEvent *event)
+{
+  switch (event->type)
+    {
+    case CLUTTER_MOTION:
+      handle_motion_event (input_device,
+                           (const ClutterMotionEvent *) event);
+      break;
+
+    case CLUTTER_BUTTON_PRESS:
+    case CLUTTER_BUTTON_RELEASE:
+      handle_button_event (input_device,
+                           (const ClutterButtonEvent *) event);
+      break;
+
+    default:
+      break;
+    }
+}
+
+/* The actor argument can be NULL in which case a Clutter pick will be
+   performed to determine the right actor. An actor should only be
+   passed if the repick is being performed due to an event in which
+   case Clutter will have already performed a pick so we can avoid
+   redundantly doing another one */
+void
+meta_wayland_input_device_repick (MetaWaylandInputDevice *device,
+                                  uint32_t                time,
+                                  ClutterActor           *actor)
+{
+  struct wl_input_device *input_device = (struct wl_input_device *) device;
+  struct wl_surface *surface;
+  MetaWaylandSurface *focus;
+
+  if (actor == NULL)
+    {
+      ClutterStage *stage = CLUTTER_STAGE (device->stage);
+      actor = clutter_stage_get_actor_at_pos (stage,
+                                              CLUTTER_PICK_REACTIVE,
+                                              input_device->x, input_device->y);
+    }
+
+  if (CLUTTER_WAYLAND_IS_SURFACE (actor))
+    {
+      ClutterWaylandSurface *wl_surface = CLUTTER_WAYLAND_SURFACE (actor);
+      float ax, ay;
+
+      clutter_actor_transform_stage_point (actor,
+                                           input_device->x, input_device->y,
+                                           &ax, &ay);
+      input_device->current_x = ax;
+      input_device->current_y = ay;
+
+      surface = clutter_wayland_surface_get_surface (wl_surface);
+    }
+  else
+    surface = NULL;
+
+  if (surface != input_device->current)
+    {
+      const struct wl_grab_interface *interface = input_device->grab->interface;
+      interface->focus (input_device->grab, time, surface,
+                        input_device->current_x, input_device->current_y);
+      input_device->current = surface;
+    }
+
+  focus = (MetaWaylandSurface *) input_device->grab->focus;
+  if (focus)
+    {
+      float ax, ay;
+
+      clutter_actor_transform_stage_point (focus->actor,
+                                           input_device->x, input_device->y,
+                                           &ax, &ay);
+      input_device->grab->x = ax;
+      input_device->grab->y = ay;
+    }
+}
diff --git a/src/wayland/meta-wayland-input-device.h b/src/wayland/meta-wayland-input-device.h
new file mode 100644
index 0000000..91315cc
--- /dev/null
+++ b/src/wayland/meta-wayland-input-device.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2012 Intel Corporation
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef META_WAYLAND_INPUT_H
+#define META_WAYLAND_INPUT_H
+
+#include <wayland-server.h>
+
+typedef struct _MetaWaylandInputDevice MetaWaylandInputDevice;
+
+MetaWaylandInputDevice *meta_wayland_input_device_new          (struct wl_display      *display,
+                                                                ClutterActor           *stage);
+
+void                    meta_wayland_input_device_handle_event (MetaWaylandInputDevice *input_device,
+                                                                const ClutterEvent     *event);
+
+void                    meta_wayland_input_device_repick       (MetaWaylandInputDevice *input_device,
+                                                                uint32_t                time,
+                                                                ClutterActor           *actor);
+
+#endif /* META_WAYLAND_INPUT_H */
diff --git a/src/wayland/meta-wayland-private.h b/src/wayland/meta-wayland-private.h
index d0c5412..0e4e2ec 100644
--- a/src/wayland/meta-wayland-private.h
+++ b/src/wayland/meta-wayland-private.h
@@ -27,6 +27,7 @@
 #include <glib.h>
 
 #include "window-private.h"
+#include "meta-wayland-input-device.h"
 
 typedef struct _MetaWaylandCompositor MetaWaylandCompositor;
 
@@ -120,6 +121,16 @@ struct _MetaWaylandCompositor
   struct wl_client *xwayland_client;
   struct wl_resource *xserver_resource;
   GHashTable *window_surfaces;
+
+  MetaWaylandInputDevice *input_device;
+
+  /* This surface is only used to keep drag of the implicit grab when
+     synthesizing XEvents for Mutter */
+  struct wl_surface *implicit_grab_surface;
+  /* Button that was pressed to initiate an implicit grab. The
+     implicit grab will only be released when this button is
+     released */
+  guint32 implicit_grab_button;
 };
 
 void                    meta_wayland_init                   (void);
@@ -133,4 +144,6 @@ void                    meta_wayland_handle_sig_child       (void);
 
 MetaWaylandSurface     *meta_wayland_lookup_surface_for_xid (guint32 xid);
 
+void                    meta_wayland_compositor_repick      (MetaWaylandCompositor *compositor);
+
 #endif /* META_WAYLAND_PRIVATE_H */
diff --git a/src/wayland/meta-wayland.c b/src/wayland/meta-wayland.c
index b9e28ba..8bb7506 100644
--- a/src/wayland/meta-wayland.c
+++ b/src/wayland/meta-wayland.c
@@ -284,6 +284,7 @@ meta_wayland_surface_attach_buffer (struct wl_client *wayland_client,
   if (surface->actor)
     {
       surface_actor = CLUTTER_WAYLAND_SURFACE (surface->actor);
+      clutter_actor_set_reactive (surface->actor, TRUE);
       if (!clutter_wayland_surface_attach_buffer (surface_actor, wayland_buffer,
                                                   NULL))
         g_warning ("Failed to attach buffer to ClutterWaylandSurface");
@@ -349,6 +350,16 @@ const struct wl_surface_interface meta_wayland_surface_interface = {
   meta_wayland_surface_frame
 };
 
+/* This should be called whenever the window stacking changes to
+   update the current position on all of the input devices */
+void
+meta_wayland_compositor_repick (MetaWaylandCompositor *compositor)
+{
+  meta_wayland_input_device_repick (compositor->input_device,
+                                    get_time (),
+                                    NULL);
+}
+
 static void
 surface_actor_destroyed_cb (void *user_data,
                             GObject *old_object)
@@ -382,6 +393,12 @@ meta_wayland_surface_free (MetaWaylandSurface *surface)
     }
 
   g_slice_free (MetaWaylandSurface, surface);
+
+  meta_wayland_compositor_repick (compositor);
+
+  if (compositor->implicit_grab_surface == (struct wl_surface *) surface)
+    compositor->implicit_grab_surface =
+      ((struct wl_input_device *) compositor->input_device)->current;
 }
 
 static void
@@ -1123,6 +1140,105 @@ stage_destroy_cb (void)
   meta_quit (META_EXIT_SUCCESS);
 }
 
+static gboolean
+event_cb (ClutterActor *stage,
+          const ClutterEvent *event,
+          MetaWaylandCompositor *compositor)
+{
+  struct wl_input_device *device =
+    (struct wl_input_device *) compositor->input_device;
+  MetaWaylandSurface *surface;
+  MetaDisplay *display;
+  XMotionEvent xevent;
+
+  meta_wayland_input_device_handle_event (compositor->input_device, event);
+
+  display = meta_get_display ();
+  if (!display)
+    return FALSE;
+
+  /* We want to synthesize X events for mouse motion events so that we
+     don't have to rely on the X server's window position being
+     synched with the surface positoin. See the comment in
+     event_callback() in display.c */
+
+  switch (event->type)
+    {
+    case CLUTTER_BUTTON_PRESS:
+      if (compositor->implicit_grab_surface == NULL)
+        {
+          compositor->implicit_grab_button = event->button.button;
+          compositor->implicit_grab_surface = device->current;
+        }
+      return FALSE;
+
+    case CLUTTER_BUTTON_RELEASE:
+      if (event->type == CLUTTER_BUTTON_RELEASE &&
+          compositor->implicit_grab_surface &&
+          event->button.button == compositor->implicit_grab_button)
+        compositor->implicit_grab_surface = NULL;
+      return FALSE;
+
+    case CLUTTER_MOTION:
+      break;
+
+    default:
+      return FALSE;
+    }
+
+  xevent.type = MotionNotify;
+  xevent.is_hint = NotifyNormal;
+  xevent.same_screen = TRUE;
+  xevent.serial = 0;
+  xevent.send_event = False;
+  xevent.display = display->xdisplay;
+  xevent.root = DefaultRootWindow (display->xdisplay);
+
+  if (compositor->implicit_grab_surface)
+    surface = (MetaWaylandSurface *) compositor->implicit_grab_surface;
+  else
+    surface = (MetaWaylandSurface *) device->current;
+
+  if (surface == (MetaWaylandSurface *) device->current)
+    {
+      xevent.x = device->current_x;
+      xevent.y = device->current_y;
+    }
+  else if (surface)
+    {
+      float ax, ay;
+
+      clutter_actor_transform_stage_point (surface->actor,
+                                           device->x, device->y,
+                                           &ax, &ay);
+      xevent.x = ax;
+      xevent.y = ay;
+    }
+  else
+    {
+      xevent.x = device->x;
+      xevent.y = device->y;
+    }
+
+  if (surface && surface->xid != None)
+    xevent.window = surface->xid;
+  else
+    xevent.window = xevent.root;
+
+  /* Mutter doesn't really know about the sub-windows. This assumes it
+     doesn't care either */
+  xevent.subwindow = xevent.window;
+  xevent.time = event->any.time;
+  xevent.x_root = device->x;
+  xevent.y_root = device->y;
+  /* The Clutter state flags exactly match the X values */
+  xevent.state = clutter_event_get_state (event);
+
+  meta_display_handle_event (display, (XEvent *) &xevent);
+
+  return FALSE;
+}
+
 void
 meta_wayland_init (void)
 {
@@ -1176,6 +1292,12 @@ meta_wayland_init (void)
                           G_CALLBACK (paint_finished_cb), compositor);
   g_signal_connect (compositor->stage, "destroy",
                     G_CALLBACK (stage_destroy_cb), NULL);
+  g_signal_connect (compositor->stage, "event",
+                    G_CALLBACK (event_cb), compositor);
+
+  compositor->input_device =
+    meta_wayland_input_device_new (compositor->wayland_display,
+                                   compositor->stage);
 
   meta_wayland_compositor_create_output (compositor, 0, 0, 1024, 600, 222, 125);
 



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