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



commit 1075583e9ec26c0347c980b52a20059a74c21fb2
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         |   48 +++++++++-
 src/wayland/meta-wayland.c |  222 ++++++++++++++++++++++++++++++++++++--------
 2 files changed, 230 insertions(+), 40 deletions(-)
---
diff --git a/src/core/display.c b/src/core/display.c
index b50541b..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,10 +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 == MotionNotify &&
-      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 ca8cf9e..6d45ebc 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)
 {
@@ -1541,15 +1548,14 @@ stage_destroy_cb (void)
 }
 
 static gboolean
-event_cb (ClutterActor *stage,
-          const ClutterEvent *event,
-          MetaWaylandCompositor *compositor)
+captured_event_cb (ClutterActor *stage,
+                  const ClutterEvent *event,
+                  MetaWaylandCompositor *compositor)
 {
   MetaWaylandSeat *seat = compositor->seat;
   MetaWaylandPointer *pointer = &seat->pointer;
   MetaWaylandSurface *surface;
   MetaDisplay *display;
-  XMotionEvent xevent;
 
   meta_wayland_seat_handle_event (compositor->seat, event);
 
@@ -1605,19 +1611,48 @@ event_cb (ClutterActor *stage,
       return FALSE;
 
     case CLUTTER_MOTION:
-      break;
+      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;
     }
+}
 
-  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);
+static gboolean
+meta_synthetize_motion_event (MetaDisplay           *display,
+                             const ClutterEvent    *event,
+                             MetaWaylandCompositor *compositor)
+{
+  MetaWaylandSeat *seat = compositor->seat;
+  MetaWaylandPointer *pointer = &seat->pointer;
+  MetaWaylandSurface *surface;
+  XGenericEventCookie cookie;
+  XIDeviceEvent xievent;
+
+  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;
@@ -1626,8 +1661,8 @@ event_cb (ClutterActor *stage,
 
   if (surface == pointer->current)
     {
-      xevent.x = wl_fixed_to_int (pointer->current_x);
-      xevent.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)
     {
@@ -1642,38 +1677,151 @@ event_cb (ClutterActor *stage,
                                                wl_fixed_to_double (pointer->y),
                                                &ax, &ay);
 
-          xevent.x = ax;
-          xevent.y = ay;
+          xievent.event_x = ax;
+          xievent.event_y = ay;
         }
       else
         {
-          xevent.x = wl_fixed_to_int (pointer->x);
-          xevent.y = wl_fixed_to_int (pointer->y);
+          xievent.event_x = wl_fixed_to_double (pointer->x);
+          xievent.event_y = wl_fixed_to_double (pointer->y);
         }
     }
   else
     {
-      xevent.x = wl_fixed_to_int (pointer->x);
-      xevent.y = wl_fixed_to_int (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)
-    xevent.window = surface->xid;
+    xievent.event = surface->xid;
   else
-    xevent.window = xevent.root;
+    xievent.event = xievent.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 = wl_fixed_to_int (pointer->x);
-  xevent.y_root = wl_fixed_to_int (pointer->y);
-  /* The Clutter state flags exactly match the X values */
-  xevent.state = clutter_event_get_state (event);
+  xievent.child = xievent.event;
+  xievent.flags = 0;
 
-  meta_display_handle_event (display, (XEvent *) &xevent);
+  /* Mutter only cares about the effective modifiers, so let's clear
+     this out and set what we have readily available from Clutter.
+     We could fill the other fields from xkbcommon as well, but for
+     now this is easier.
+  */
+  memset (&xievent.buttons, 0, sizeof (XIButtonState));
+  memset (&xievent.valuators, 0, sizeof (XIValuatorState));
+  memset (&xievent.mods, 0, sizeof (XIModifierState));
+  memset (&xievent.group, 0, sizeof (XIGroupState));
 
-  return FALSE;
+  xievent.mods.effective = clutter_event_get_state (event);
+
+  return meta_display_handle_event (display, (XEvent *) &cookie);
+}
+
+static gboolean
+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;
+  XGenericEventCookie cookie;
+  XIDeviceEvent xievent;
+
+  /* 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;
+
+  if (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;
+
+      if (window_actor)
+        {
+          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);
+    }
+
+  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)
+    xievent.event = surface->xid;
+  else
+    xievent.event = xievent.root;
+
+  /* Mutter doesn't really know about the sub-windows. This assumes it
+     doesn't care either */
+  xievent.child = xievent.event;
+  xievent.flags = 0;
+
+  /* Mutter only cares about the effective modifiers, so let's clear
+     this out and set what we have readily available from Clutter.
+     We could fill the other fields from xkbcommon as well, but for
+     now this is easier.
+  */
+  memset (&xievent.buttons, 0, sizeof (XIButtonState));
+  memset (&xievent.valuators, 0, sizeof (XIValuatorState));
+  memset (&xievent.mods, 0, sizeof (XIModifierState));
+  memset (&xievent.group, 0, sizeof (XIGroupState));
+
+  xievent.mods.effective = clutter_event_get_state (event);
+
+  return meta_display_handle_event (display, (XEvent *) &cookie);
 }
 
 static gboolean
@@ -1708,18 +1856,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;
@@ -1885,7 +2033,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]