[mutter/wip/wayland-display: 17/55] wayland: handle key events from clutter instead of XWayland



commit f5a07be311467b5742215254bc92bfc85dcababd
Author: Giovanni Campagna <gcampagn redhat com>
Date:   Wed Jul 17 10:44:44 2013 +0200

    wayland: handle key events from clutter instead of XWayland
    
    Keyboard grabs don't work in a rootless X, so we need to use
    our own "grabs" by translating events from Clutter.
    Ideally, we would avoid the translation and just use Clutter
    events directly, but for now it at least ensures that we
    see something.

 src/core/display.c         |   49 ++++++-
 src/wayland/meta-wayland.c |  335 ++++++++++++++++++++++++++++++--------------
 2 files changed, 274 insertions(+), 110 deletions(-)
---
diff --git a/src/core/display.c b/src/core/display.c
index 6eaca92..c651f46 100644
--- a/src/core/display.c
+++ b/src/core/display.c
@@ -3226,6 +3226,31 @@ meta_display_handle_event (MetaDisplay *display,
 }
 
 static gboolean
+event_is_filtered_in_wayland (MetaDisplay *display,
+                              XEvent      *event)
+{
+  XIDeviceEvent *input_event;
+
+  if (event->type == MotionNotify ||
+      event->type == KeyPress ||
+      event->type == KeyRelease)
+    return TRUE;
+
+  input_event = (XIDeviceEvent *) get_input_event (display, event);
+
+  /* Careful! input_event->type exists, but it's
+     always GenericEvent */
+
+  if (input_event &&
+      (input_event->evtype == XI_Motion ||
+       input_event->evtype == XI_KeyPress ||
+       input_event->evtype == XI_KeyRelease))
+    return TRUE;
+
+  return FALSE;
+}
+
+static gboolean
 event_callback (XEvent  *event,
                 gpointer data)
 {
@@ -3240,11 +3265,27 @@ event_callback (XEvent  *event,
      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 */
+     translation altogether by directly using the Clutter events.
+
+     Additionally, we want to filter out all keyboard events as well,
+     because we synthetize them from a Clutter captured-event handler.
+     This is necessary because keyboard grabs don't work with X in
+     rootless mode, and because there is no root window to listen
+     for events.
+     We filter both core and XI2 events, although we should never
+     see core events of those types.
+
+     In the end, what happens is:
+     Clutter -> captured-event -> meta_display_handle_event
+       if TRUE:
+          event is handled
+       if FALSE:
+          event is propagated to Clutter -> MetaWaylandKeyboard -> XWayland
+          maybe here again, we return FALSE -> Gdk
+  */
 #ifdef HAVE_WAYLAND
-  if (event->type == GenericEvent &&
-      event->xcookie.evtype == XI_Motion &&
-      meta_is_display_server ())
+  if (meta_is_display_server () &&
+      event_is_filtered_in_wayland (display, event))
     return FALSE;
 #endif
 
diff --git a/src/wayland/meta-wayland.c b/src/wayland/meta-wayland.c
index 9eb60c1..0002e91 100644
--- a/src/wayland/meta-wayland.c
+++ b/src/wayland/meta-wayland.c
@@ -58,6 +58,13 @@
 
 static MetaWaylandCompositor _meta_wayland_compositor;
 
+static gboolean meta_synthetize_key_event (MetaDisplay           *display,
+                                          const ClutterEvent    *event,
+                                          MetaWaylandCompositor *compositor);
+static gboolean meta_synthetize_motion_event (MetaDisplay           *display,
+                                             const ClutterEvent    *event,
+                                             MetaWaylandCompositor *compositor);
+
 MetaWaylandCompositor *
 meta_wayland_compositor_get_default (void)
 {
@@ -1550,43 +1557,110 @@ stage_destroy_cb (void)
 
 #define N_BUTTONS 5
 
-static void
-synthesize_motion_event (MetaWaylandCompositor *compositor,
-                         const ClutterEvent *event)
+static gboolean
+captured_event_cb (ClutterActor *stage,
+                  const ClutterEvent *event,
+                  MetaWaylandCompositor *compositor)
+{
+  MetaWaylandSeat *seat = compositor->seat;
+  MetaWaylandPointer *pointer = &seat->pointer;
+  MetaWaylandSurface *surface;
+  MetaDisplay *display;
+
+  meta_wayland_seat_handle_event (compositor->seat, event);
+
+  /* HACK: for now, the surfaces from Wayland clients aren't
+     integrated into Mutter's stacking and Mutter won't give them
+     focus on mouse clicks. As a hack to work around this we can just
+     give them input focus on mouse clicks so we can at least test the
+     keyboard support */
+  if (event->type == CLUTTER_BUTTON_PRESS)
+    {
+      surface = pointer->current;
+
+      /* Only focus surfaces that wouldn't be handled by the
+         corresponding X events */
+      if (surface && surface->xid == 0)
+        {
+          meta_wayland_keyboard_set_focus (&seat->keyboard, surface);
+          meta_wayland_data_device_set_keyboard_focus (seat);
+        }
+    }
+
+  meta_wayland_stage_set_cursor_position (META_WAYLAND_STAGE (stage),
+                                          wl_fixed_to_int (pointer->x),
+                                          wl_fixed_to_int (pointer->y));
+
+  if (pointer->current == NULL)
+    meta_wayland_stage_set_default_cursor (META_WAYLAND_STAGE (stage));
+
+  display = meta_get_display ();
+  if (!display)
+    return FALSE;
+
+  switch (event->type)
+    {
+    case CLUTTER_BUTTON_PRESS:
+      if (compositor->implicit_grab_surface == NULL)
+        {
+          compositor->implicit_grab_button = event->button.button;
+          compositor->implicit_grab_surface = pointer->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:
+      return meta_synthetize_motion_event (display, event, compositor);
+
+    case CLUTTER_KEY_PRESS:
+    case CLUTTER_KEY_RELEASE:
+      return meta_synthetize_key_event (display, event, compositor);
+
+    default:
+      return FALSE;
+    }
+}
+
+static gboolean
+meta_synthetize_motion_event (MetaDisplay           *display,
+                             const ClutterEvent    *event,
+                             MetaWaylandCompositor *compositor)
 {
-  /* 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 position. See the comment in
-     event_callback() in display.c */
   MetaWaylandSeat *seat = compositor->seat;
   MetaWaylandPointer *pointer = &seat->pointer;
   MetaWaylandSurface *surface;
-  XGenericEventCookie generic_event;
-  XIDeviceEvent device_event;
+  XGenericEventCookie cookie;
+  XIDeviceEvent xievent;
   unsigned char button_mask[(N_BUTTONS + 7) / 8] = { 0 };
-  MetaDisplay *display = meta_get_display ();
   ClutterModifierType state;
   int i;
 
-  generic_event.type = GenericEvent;
-  generic_event.serial = 0;
-  generic_event.send_event = False;
-  generic_event.display = display->xdisplay;
-  generic_event.extension = display->xinput_opcode;
-  generic_event.evtype = XI_Motion;
-  /* Mutter assumes the data for the event is already retrieved by GDK
-   * so we don't need the cookie */
-  generic_event.cookie = 0;
-  generic_event.data = &device_event;
-
-  memcpy (&device_event, &generic_event, sizeof (XGenericEvent));
-
-  device_event.time = clutter_event_get_time (event);
-  device_event.deviceid = clutter_event_get_device_id (event);
-  device_event.sourceid = 0; /* not used, not sure what this should be */
-  device_event.detail = 0;
-  device_event.root = DefaultRootWindow (display->xdisplay);
-  device_event.flags = 0 /* not used for motion events */;
+  cookie.type = GenericEvent;
+  cookie.serial = 0;
+  cookie.send_event = False;
+  cookie.display = display->xdisplay;
+  cookie.extension = display->xinput_opcode;
+  cookie.evtype = XI_Motion;
+  cookie.cookie = 0;
+  cookie.data = &xievent;
+
+  xievent.type = GenericEvent;
+  xievent.serial = 0;
+  xievent.send_event = False;
+  xievent.display = display->xdisplay;
+  xievent.extension = display->xinput_opcode;
+  xievent.evtype = XI_Motion;
+  xievent.time = clutter_event_get_time (event);
+  xievent.deviceid = META_VIRTUAL_CORE_KEYBOARD_ID;
+  xievent.sourceid = clutter_event_get_device_id (event);
+  xievent.detail = NotifyNormal;
+  xievent.root = DefaultRootWindow (display->xdisplay);
 
   if (compositor->implicit_grab_surface)
     surface = compositor->implicit_grab_surface;
@@ -1595,137 +1669,186 @@ synthesize_motion_event (MetaWaylandCompositor *compositor,
 
   if (surface == pointer->current)
     {
-      device_event.event_x = wl_fixed_to_int (pointer->current_x);
-      device_event.event_y = wl_fixed_to_int (pointer->current_y);
+      xievent.event_x = wl_fixed_to_double (pointer->current_x);
+      xievent.event_y = wl_fixed_to_double (pointer->current_y);
     }
   else if (surface && surface->window)
     {
       ClutterActor *window_actor =
         CLUTTER_ACTOR (meta_window_get_compositor_private (surface->window));
+      float ax, ay;
 
       if (window_actor)
         {
-          float ax, ay;
-
           clutter_actor_transform_stage_point (window_actor,
                                                wl_fixed_to_double (pointer->x),
                                                wl_fixed_to_double (pointer->y),
                                                &ax, &ay);
 
-          device_event.event_x = ax;
-          device_event.event_y = ay;
+          xievent.event_x = ax;
+          xievent.event_y = ay;
         }
       else
         {
-          device_event.event_x = wl_fixed_to_double (pointer->x);
-          device_event.event_y = wl_fixed_to_double (pointer->y);
+          xievent.event_x = wl_fixed_to_double (pointer->x);
+          xievent.event_y = wl_fixed_to_double (pointer->y);
         }
     }
   else
     {
-      device_event.event_x = wl_fixed_to_double (pointer->x);
-      device_event.event_y = wl_fixed_to_double (pointer->y);
+      xievent.event_x = wl_fixed_to_double (pointer->x);
+      xievent.event_y = wl_fixed_to_double (pointer->y);
     }
 
+  xievent.root_x = wl_fixed_to_double (pointer->x);
+  xievent.root_y = wl_fixed_to_double (pointer->y);
+
+  /* FIXME: if the surface is not a X11 window, we're not processing window
+     keybindings for it, because we report the events on the root window */
   if (surface && surface->xid != None)
-    device_event.event = surface->xid;
+    xievent.event = surface->xid;
   else
-    device_event.event = device_event.root;
+    xievent.event = xievent.root;
 
   /* Mutter doesn't really know about the sub-windows. This assumes it
      doesn't care either */
-  device_event.child = device_event.event;
-  device_event.root_x = wl_fixed_to_double (pointer->x);
-  device_event.root_y = wl_fixed_to_double (pointer->y);
+  xievent.child = xievent.event;
+  xievent.flags = 0;
 
   state = clutter_event_get_state (event);
 
   for (i = 0; i < N_BUTTONS; i++)
     if ((state & (CLUTTER_BUTTON1_MASK << i)))
       XISetMask (button_mask, i + 1);
-  device_event.buttons.mask_len = N_BUTTONS + 1;
-  device_event.buttons.mask = button_mask;
+  xievent.buttons.mask_len = N_BUTTONS + 1;
+  xievent.buttons.mask = button_mask;
 
-  device_event.valuators.mask_len = 0;
-  device_event.valuators.mask = NULL;
-  device_event.valuators.values = NULL;
+  xievent.valuators.mask_len = 0;
+  xievent.valuators.mask = NULL;
+  xievent.valuators.values = NULL;
 
-  memset (&device_event.mods, 0, sizeof (device_event.mods));
-  device_event.mods.effective =
+  memset (&xievent.mods, 0, sizeof (xievent.mods));
+  xievent.mods.effective =
     state & (CLUTTER_MODIFIER_MASK &
              ~(((CLUTTER_BUTTON1_MASK << N_BUTTONS) - 1) ^
                (CLUTTER_BUTTON1_MASK - 1)));
 
-  memset (&device_event.group, 0, sizeof (device_event.group));
+  memset (&xievent.group, 0, sizeof (xievent.group));
 
-  meta_display_handle_event (display, (XEvent *) &generic_event);
+  return meta_display_handle_event (display, (XEvent *) &cookie);
 }
 
 static gboolean
-event_cb (ClutterActor *stage,
-          const ClutterEvent *event,
-          MetaWaylandCompositor *compositor)
+meta_synthetize_key_event (MetaDisplay           *display,
+                          const ClutterEvent    *event,
+                          MetaWaylandCompositor *compositor)
 {
   MetaWaylandSeat *seat = compositor->seat;
+  MetaWaylandKeyboard *keyboard = &seat->keyboard;
   MetaWaylandPointer *pointer = &seat->pointer;
   MetaWaylandSurface *surface;
-  MetaDisplay *display;
+  XGenericEventCookie cookie;
+  XIDeviceEvent xievent;
+  unsigned char button_mask[(N_BUTTONS + 7) / 8] = { 0 };
+  ClutterModifierType state;
+  int i;
 
-  meta_wayland_seat_handle_event (compositor->seat, event);
+  /* I think in real XLib cookie and xievent share some of the underlying
+     memory (that would explain why the first fields are the same), but I'm not
+     sure and I don't care */
+
+  cookie.type = GenericEvent;
+  cookie.serial = 0;
+  cookie.send_event = False;
+  cookie.display = display->xdisplay;
+  cookie.extension = display->xinput_opcode;
+  cookie.evtype = event->type == CLUTTER_KEY_PRESS ? XI_KeyPress : XI_KeyRelease;
+  cookie.cookie = 0;
+  cookie.data = &xievent;
+
+  xievent.type = GenericEvent;
+  xievent.serial = 0;
+  xievent.send_event = False;
+  xievent.display = display->xdisplay;
+  xievent.extension = display->xinput_opcode;
+  xievent.evtype = cookie.evtype;
+  xievent.time = clutter_event_get_time (event);
+  xievent.deviceid = META_VIRTUAL_CORE_KEYBOARD_ID;
+  xievent.sourceid = clutter_event_get_device_id (event);
+  xievent.detail = clutter_event_get_key_code (event);
+  xievent.root = DefaultRootWindow (display->xdisplay);
+
+  surface = keyboard->focus;
 
-  /* HACK: for now, the surfaces from Wayland clients aren't
-     integrated into Mutter's stacking and Mutter won't give them
-     focus on mouse clicks. As a hack to work around this we can just
-     give them input focus on mouse clicks so we can at least test the
-     keyboard support */
-  if (event->type == CLUTTER_BUTTON_PRESS)
+  if (surface == pointer->current)
     {
-      surface = pointer->current;
+      xievent.event_x = wl_fixed_to_double (pointer->current_x);
+      xievent.event_y = wl_fixed_to_double (pointer->current_y);
+    }
+  else if (surface && surface->window)
+    {
+      ClutterActor *window_actor =
+        CLUTTER_ACTOR (meta_window_get_compositor_private (surface->window));
+      float ax, ay;
 
-      /* Only focus surfaces that wouldn't be handled by the
-         corresponding X events */
-      if (surface && surface->xid == 0)
+      if (window_actor)
         {
-          meta_wayland_keyboard_set_focus (&seat->keyboard, surface);
-          meta_wayland_data_device_set_keyboard_focus (seat);
+          clutter_actor_transform_stage_point (window_actor,
+                                               wl_fixed_to_double (pointer->x),
+                                               wl_fixed_to_double (pointer->y),
+                                               &ax, &ay);
+
+          xievent.event_x = ax;
+          xievent.event_y = ay;
+        }
+      else
+        {
+          xievent.event_x = wl_fixed_to_double (pointer->x);
+          xievent.event_y = wl_fixed_to_double (pointer->y);
         }
     }
+  else
+    {
+      xievent.event_x = wl_fixed_to_double (pointer->x);
+      xievent.event_y = wl_fixed_to_double (pointer->y);
+    }
 
-  meta_wayland_stage_set_cursor_position (META_WAYLAND_STAGE (stage),
-                                          wl_fixed_to_int (pointer->x),
-                                          wl_fixed_to_int (pointer->y));
+  xievent.root_x = wl_fixed_to_double (pointer->x);
+  xievent.root_y = wl_fixed_to_double (pointer->y);
 
-  if (pointer->current == NULL)
-    meta_wayland_stage_set_default_cursor (META_WAYLAND_STAGE (stage));
+  /* FIXME: if the surface is not a X11 window, we're not processing window
+     keybindings for it, because we report the events on the root window */
+  if (surface && surface->xid != None)
+    xievent.event = surface->xid;
+  else
+    xievent.event = xievent.root;
 
-  display = meta_get_display ();
-  if (!display)
-    return FALSE;
+  /* Mutter doesn't really know about the sub-windows. This assumes it
+     doesn't care either */
+  xievent.child = xievent.event;
+  xievent.flags = 0;
 
-  switch (event->type)
-    {
-    case CLUTTER_BUTTON_PRESS:
-      if (compositor->implicit_grab_surface == NULL)
-        {
-          compositor->implicit_grab_button = event->button.button;
-          compositor->implicit_grab_surface = pointer->current;
-        }
-      return FALSE;
+  state = clutter_event_get_state (event);
 
-    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;
+  for (i = 0; i < N_BUTTONS; i++)
+    if ((state & (CLUTTER_BUTTON1_MASK << i)))
+      XISetMask (button_mask, i + 1);
+  xievent.buttons.mask_len = N_BUTTONS + 1;
+  xievent.buttons.mask = button_mask;
 
-    case CLUTTER_MOTION:
-      synthesize_motion_event (compositor, event);
-      return FALSE;
+  xievent.valuators.mask_len = 0;
+  xievent.valuators.mask = NULL;
+  xievent.valuators.values = NULL;
 
-    default:
-      return FALSE;
-    }
+  memset (&xievent.mods, 0, sizeof (xievent.mods));
+  xievent.mods.effective =
+    state & (CLUTTER_MODIFIER_MASK &
+             ~(((CLUTTER_BUTTON1_MASK << N_BUTTONS) - 1) ^
+               (CLUTTER_BUTTON1_MASK - 1)));
+
+  memset (&xievent.group, 0, sizeof (xievent.group));
+
+  return meta_display_handle_event (display, (XEvent *) &cookie);
 }
 
 static gboolean
@@ -1760,18 +1883,18 @@ event_emission_hook_cb (GSignalInvocationHint *ihint,
     case CLUTTER_BUTTON_RELEASE:
     case CLUTTER_SCROLL:
       if (actor == clutter_get_pointer_grab ())
-        event_cb (clutter_actor_get_stage (actor),
-                  event,
-                  compositor);
+        captured_event_cb (clutter_actor_get_stage (actor),
+                          event,
+                          compositor);
       break;
 
       /* Keyboard events */
     case CLUTTER_KEY_PRESS:
     case CLUTTER_KEY_RELEASE:
       if (actor == clutter_get_keyboard_grab ())
-        event_cb (clutter_actor_get_stage (actor),
-                  event,
-                  compositor);
+        captured_event_cb (clutter_actor_get_stage (actor),
+                          event,
+                          compositor);
 
     default:
       break;
@@ -1937,7 +2060,7 @@ meta_wayland_init (void)
 
   g_signal_connect (compositor->stage,
                     "captured-event",
-                    G_CALLBACK (event_cb),
+                    G_CALLBACK (captured_event_cb),
                     compositor);
   /* If something sets a grab on an actor then the captured event
    * signal won't get emitted but we still want to see these events so


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