[mutter/wip/wayland] wayland: Synthesize X events for mouse input from Clutter events



commit ada4281f7f707116cb839e778792a08fa56302fa
Author: Neil Roberts <neil linux intel com>
Date:   Thu Jan 12 12:22:53 2012 +0000

    wayland: Synthesize X events for mouse input from Clutter events
    
    Receiving mouse events via the X server for moving a window is awkward
    because they are first converted to surface relative events and sent
    to the X server by the Wayland protocol and then converted back to
    screen-relative coordinates by the X server. If Mutter moves the
    window in response to these events then the lag between the compositor
    and the X server when updating the window position may cause the
    translation to get different results into and out of surface-relative
    coordinates. This causes the window to jump around erratically.
    
    To fix this the mouse events delivered to the X event filter in Mutter
    are now synthesized directly from the Clutter events so that they
    don't have to pass through the X server. The event callback has been
    split into two functions so that under Wayland the mouse events can be
    filtered out. The Wayland compositor directly calls the unfiltered
    version of the function with its synthesized events.

 src/core/display-private.h         |    3 +
 src/core/display.c                 |   43 ++++++++++++---
 src/wayland/meta-wayland-private.h |    8 +++
 src/wayland/meta-wayland.c         |   98 ++++++++++++++++++++++++++++++++++++
 4 files changed, 143 insertions(+), 9 deletions(-)
---
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 b73c053..1776bb7 100644
--- a/src/core/display.c
+++ b/src/core/display.c
@@ -1591,26 +1591,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);
@@ -2664,6 +2660,35 @@ 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 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
+  switch (event->type)
+    {
+    case ButtonPress:
+    case ButtonRelease:
+    case 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-private.h b/src/wayland/meta-wayland-private.h
index cd10bd9..30f895d 100644
--- a/src/wayland/meta-wayland-private.h
+++ b/src/wayland/meta-wayland-private.h
@@ -117,6 +117,14 @@ struct _MetaWaylandCompositor
   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);
diff --git a/src/wayland/meta-wayland.c b/src/wayland/meta-wayland.c
index de4f92c..9f28602 100644
--- a/src/wayland/meta-wayland.c
+++ b/src/wayland/meta-wayland.c
@@ -376,6 +376,10 @@ 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
@@ -1023,8 +1027,102 @@ 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;
+  XEvent xevent;
+
   meta_wayland_input_device_handle_event (compositor->input_device, event);
 
+  /* We want to synthesize X events for mouse related 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:
+      xevent.type = ButtonPress;
+      xevent.xbutton.button = event->button.button;
+      xevent.xbutton.same_screen = TRUE;
+
+      if (compositor->implicit_grab_surface == NULL)
+        {
+          compositor->implicit_grab_button = event->button.button;
+          compositor->implicit_grab_surface = device->current;
+        }
+      break;
+
+    case CLUTTER_BUTTON_RELEASE:
+      xevent.type = ButtonRelease;
+      xevent.xbutton.button = event->button.button;
+      xevent.xbutton.same_screen = TRUE;
+
+      if (compositor->implicit_grab_surface &&
+          event->button.button == compositor->implicit_grab_button)
+        compositor->implicit_grab_surface = NULL;
+      break;
+
+    case CLUTTER_MOTION:
+      xevent.type = MotionNotify;
+      xevent.xmotion.is_hint = NotifyNormal;
+      xevent.xbutton.same_screen = TRUE;
+      break;
+
+    default:
+      return FALSE;
+    }
+
+  display = meta_get_display ();
+
+  xevent.xany.serial = 0;
+  xevent.xany.send_event = False;
+  xevent.xany.display = display->xdisplay;
+  xevent.xbutton.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.xbutton.x = device->current_x;
+      xevent.xbutton.y = device->current_y;
+    }
+  else if (surface)
+    {
+      float ax, ay;
+
+      clutter_actor_transform_stage_point (surface->actor,
+                                           device->x, device->y,
+                                           &ax, &ay);
+      xevent.xbutton.x = ax;
+      xevent.xbutton.y = ay;
+    }
+  else
+    {
+      xevent.xbutton.x = device->x;
+      xevent.xbutton.y = device->y;
+    }
+
+  if (surface && surface->xid != None)
+    xevent.xbutton.window = surface->xid;
+  else
+    xevent.xbutton.window = xevent.xbutton.root;
+
+  /* Mutter doesn't really know about the sub-windows. This assumes it
+     doesn't care either */
+  xevent.xbutton.subwindow = xevent.xbutton.window;
+  xevent.xbutton.time = event->any.time;
+  xevent.xbutton.x_root = device->x;
+  xevent.xbutton.y_root = device->y;
+  /* The Clutter state flags exactly match the X values */
+  xevent.xbutton.state = clutter_event_get_state (event);
+
+  meta_display_handle_event (display, &xevent);
+
   return FALSE;
 }
 



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