[mutter/wayland] Support X button events again



commit fa65c380dbdd79f9849b3c70a5037743f8f88341
Author: Jasper St. Pierre <jstpierre mecheye net>
Date:   Thu Dec 5 16:13:03 2013 -0500

    Support X button events again
    
    Do this by duplicating the current code and porting it to use
    X again. A better approach would involve our own event structures,
    and I really don't want to do that right now. We can clean this up
    later.

 src/core/display.c        |  236 +++++++++++++++++++++++++++++++++++++++++++++
 src/core/window-private.h |    6 +-
 src/core/window.c         |  217 ++++++++++++++++++++++++++++++++++++-----
 3 files changed, 430 insertions(+), 29 deletions(-)
---
diff --git a/src/core/display.c b/src/core/display.c
index 5f9a536..f809caa 100644
--- a/src/core/display.c
+++ b/src/core/display.c
@@ -2229,9 +2229,11 @@ handle_input_xevent (MetaDisplay *display,
                      XIEvent     *input_event,
                      gulong       serial)
 {
+  XIDeviceEvent *device_event = (XIDeviceEvent *) input_event;
   XIEnterEvent *enter_event = (XIEnterEvent *) input_event;
   Window modified;
   MetaWindow *window;
+  gboolean frame_was_receiver;
 
   if (input_event == NULL)
     return FALSE;
@@ -2239,8 +2241,242 @@ handle_input_xevent (MetaDisplay *display,
   modified = xievent_get_modified_window (display, input_event);
   window = modified != None ? meta_display_lookup_x_window (display, modified) : NULL;
 
+  frame_was_receiver = FALSE;
+  if (window &&
+      window->frame &&
+      modified == window->frame->xwindow)
+    {
+      /* Note that if the frame and the client both have an
+       * XGrabButton (as is normal with our setup), the event
+       * goes to the frame.
+       */
+      frame_was_receiver = TRUE;
+      meta_topic (META_DEBUG_EVENTS, "Frame was receiver of event for %s\n",
+                  window->desc);
+    }
+
+  if (window && !window->override_redirect &&
+      (input_event->evtype == XI_KeyPress || input_event->evtype == XI_ButtonPress))
+    {
+      if (CurrentTime == display->current_time)
+        {
+          /* We can't use missing (i.e. invalid) timestamps to set user time,
+           * nor do we want to use them to sanity check other timestamps.
+           * See bug 313490 for more details.
+           */
+          meta_warning ("Event has no timestamp! You may be using a broken "
+                        "program such as xse.  Please ask the authors of that "
+                        "program to fix it.\n");
+        }
+      else
+        {
+          meta_window_set_user_time (window, display->current_time);
+          sanity_check_timestamps (display, display->current_time);
+        }
+    }
+
   switch (input_event->evtype)
     {
+    case XI_ButtonPress:
+      if (display->grab_op == META_GRAB_OP_COMPOSITOR)
+        break;
+
+      display->overlay_key_only_pressed = FALSE;
+
+      if (device_event->detail == 4 || device_event->detail == 5)
+        /* Scrollwheel event, do nothing and deliver event to compositor below */
+        break;
+
+      if ((window &&
+           meta_grab_op_is_mouse (display->grab_op) &&
+           (device_event->mods.effective & display->window_grab_modifiers) &&
+           display->grab_button != device_event->detail &&
+           display->grab_window == window) ||
+          grab_op_is_keyboard (display->grab_op))
+        {
+          meta_topic (META_DEBUG_WINDOW_OPS,
+                      "Ending grab op %u on window %s due to button press\n",
+                      display->grab_op,
+                      (display->grab_window ?
+                       display->grab_window->desc :
+                       "none"));
+          if (GRAB_OP_IS_WINDOW_SWITCH (display->grab_op))
+            {
+              MetaScreen *screen;
+              meta_topic (META_DEBUG_WINDOW_OPS,
+                          "Syncing to old stack positions.\n");
+              screen =
+                meta_display_screen_for_root (display, device_event->event);
+
+              if (screen!=NULL)
+                meta_stack_set_positions (screen->stack,
+                                          display->grab_old_window_stacking);
+            }
+          meta_display_end_grab_op (display,
+                                    device_event->time);
+        }
+      else if (window && display->grab_op == META_GRAB_OP_NONE)
+        {
+          gboolean begin_move = FALSE;
+          unsigned int grab_mask;
+          gboolean unmodified;
+
+          grab_mask = display->window_grab_modifiers;
+          if (g_getenv ("MUTTER_DEBUG_BUTTON_GRABS"))
+            grab_mask |= ControlMask;
+
+          /* Two possible sources of an unmodified event; one is a
+           * client that's letting button presses pass through to the
+           * frame, the other is our focus_window_grab on unmodified
+           * button 1.  So for all such events we focus the window.
+           */
+          unmodified = (device_event->mods.effective & grab_mask) == 0;
+
+          if (unmodified ||
+              device_event->detail == 1)
+            {
+              /* don't focus if frame received, will be lowered in
+               * frames.c or special-cased if the click was on a
+               * minimize/close button.
+               */
+              if (!frame_was_receiver)
+                {
+                  if (meta_prefs_get_raise_on_click ())
+                    meta_window_raise (window);
+                  else
+                    meta_topic (META_DEBUG_FOCUS,
+                                "Not raising window on click due to don't-raise-on-click option\n");
+
+                  /* Don't focus panels--they must explicitly request focus.
+                   * See bug 160470
+                   */
+                  if (window->type != META_WINDOW_DOCK)
+                    {
+                      meta_topic (META_DEBUG_FOCUS,
+                                  "Focusing %s due to unmodified button %u press (display.c)\n",
+                                  window->desc, device_event->detail);
+                      meta_window_focus (window, device_event->time);
+                    }
+                  else
+                    /* However, do allow terminals to lose focus due to new
+                     * window mappings after the user clicks on a panel.
+                     */
+                    display->allow_terminal_deactivation = TRUE;
+                }
+
+              /* you can move on alt-click but not on
+               * the click-to-focus
+               */
+              if (!unmodified)
+                begin_move = TRUE;
+            }
+          else if (!unmodified && device_event->detail == meta_prefs_get_mouse_button_resize())
+            {
+              if (window->has_resize_func)
+                {
+                  gboolean north, south;
+                  gboolean west, east;
+                  MetaRectangle frame_rect;
+                  MetaGrabOp op;
+
+                  meta_window_get_frame_rect (window, &frame_rect);
+
+                  west = device_event->root_x <  (frame_rect.x + 1 * frame_rect.width  / 3);
+                  east = device_event->root_x >  (frame_rect.x + 2 * frame_rect.width  / 3);
+                  north = device_event->root_y < (frame_rect.y + 1 * frame_rect.height / 3);
+                  south = device_event->root_y > (frame_rect.y + 2 * frame_rect.height / 3);
+
+                  if (north && west)
+                    op = META_GRAB_OP_RESIZING_NW;
+                  else if (north && east)
+                    op = META_GRAB_OP_RESIZING_NE;
+                  else if (south && west)
+                    op = META_GRAB_OP_RESIZING_SW;
+                  else if (south && east)
+                    op = META_GRAB_OP_RESIZING_SE;
+                  else if (north)
+                    op = META_GRAB_OP_RESIZING_N;
+                  else if (west)
+                    op = META_GRAB_OP_RESIZING_W;
+                  else if (east)
+                    op = META_GRAB_OP_RESIZING_E;
+                  else if (south)
+                    op = META_GRAB_OP_RESIZING_S;
+                  else /* Middle region is no-op to avoid user triggering wrong action */
+                    op = META_GRAB_OP_NONE;
+
+                  if (op != META_GRAB_OP_NONE)
+                    meta_display_begin_grab_op (display,
+                                                window->screen,
+                                                window,
+                                                op,
+                                                TRUE,
+                                                FALSE,
+                                                device_event->detail,
+                                                0,
+                                                device_event->time,
+                                                device_event->root_x,
+                                                device_event->root_y);
+                }
+            }
+          else if (device_event->detail == meta_prefs_get_mouse_button_menu())
+            {
+              if (meta_prefs_get_raise_on_click ())
+                meta_window_raise (window);
+              meta_window_show_menu (window,
+                                     device_event->root_x,
+                                     device_event->root_y,
+                                     device_event->detail,
+                                     device_event->time);
+            }
+
+          if (!frame_was_receiver && unmodified)
+            {
+              /* This is from our synchronous grab since
+               * it has no modifiers and was on the client window
+               */
+
+              meta_verbose ("Allowing events time %u\n",
+                            (unsigned int)device_event->time);
+
+              XIAllowEvents (display->xdisplay, device_event->deviceid,
+                             XIReplayDevice, device_event->time);
+            }
+
+          if (begin_move && window->has_move_func)
+            {
+              meta_display_begin_grab_op (display,
+                                          window->screen,
+                                          window,
+                                          META_GRAB_OP_MOVING,
+                                          TRUE,
+                                          FALSE,
+                                          device_event->detail,
+                                          0,
+                                          device_event->time,
+                                          device_event->root_x,
+                                          device_event->root_y);
+            }
+        }
+      break;
+    case XI_ButtonRelease:
+      if (display->grab_op == META_GRAB_OP_COMPOSITOR)
+        break;
+
+      display->overlay_key_only_pressed = FALSE;
+
+      if (display->grab_window == window &&
+          meta_grab_op_is_mouse (display->grab_op))
+        meta_window_handle_mouse_grab_op_xevent (window, device_event);
+      break;
+    case XI_Motion:
+      if (display->grab_op == META_GRAB_OP_COMPOSITOR)
+        break;
+
+      if (display->grab_window == window &&
+          meta_grab_op_is_mouse (display->grab_op))
+        meta_window_handle_mouse_grab_op_xevent (window, device_event);
+      break;
     case XI_Enter:
       if (display->grab_op == META_GRAB_OP_COMPOSITOR)
         break;
diff --git a/src/core/window-private.h b/src/core/window-private.h
index 0f1630a..307129c 100644
--- a/src/core/window-private.h
+++ b/src/core/window-private.h
@@ -648,8 +648,10 @@ void meta_window_update_sync_request_counter (MetaWindow *window,
                                               gint64      new_counter_value);
 #endif /* HAVE_XSYNC */
 
-void meta_window_handle_mouse_grab_op_event (MetaWindow         *window,
-                                             const ClutterEvent *event);
+void meta_window_handle_mouse_grab_op_event  (MetaWindow         *window,
+                                              const ClutterEvent *event);
+void meta_window_handle_mouse_grab_op_xevent (MetaWindow         *window,
+                                              XIDeviceEvent      *xevent);
 
 GList* meta_window_get_workspaces (MetaWindow *window);
 
diff --git a/src/core/window.c b/src/core/window.c
index bb334f3..3a3fde0 100644
--- a/src/core/window.c
+++ b/src/core/window.c
@@ -10029,20 +10029,96 @@ update_resize (MetaWindow *window,
     g_get_current_time (&window->display->grab_last_moveresize_time);
 }
 
+typedef struct
+{
+  Window  window;
+  int     count;
+  guint32 last_time;
+} EventScannerData;
+
+static Bool
+find_last_time_predicate (Display  *display,
+                          XEvent   *ev,
+                          XPointer  arg)
+{
+  EventScannerData *esd = (void*) arg;
+  XIEvent *xev;
+
+  if (ev->type != GenericEvent)
+    return False;
+
+  /* We are peeking into events not yet handled by GDK,
+   * Allocate cookie events here so we can handle XI2.
+   *
+   * GDK will handle later these events, and eventually
+   * free the cookie data itself.
+   */
+  XGetEventData (display, &ev->xcookie);
+  xev = (XIEvent *) ev->xcookie.data;
+
+  if (xev->evtype != XI_Motion)
+    return False;
+
+  if (esd->window != ((XIDeviceEvent *) xev)->event)
+    return False;
+
+  esd->count += 1;
+  esd->last_time = xev->time;
+
+  return False;
+}
+
 static gboolean
-check_use_this_motion_notify (MetaWindow         *window,
-                              const ClutterEvent *event)
-{
-  /* XXX: Previously this code would walk through the X event queue
-     and filter out motion events that are followed by a later motion
-     event. There currently isn't any API to do the equivalent
-     procedure with the Clutter event queue so this function does
-     nothing. Clutter does its own motion event squashing so it may be
-     the case that this function isn't necessary. If it turns out that
-     we do need additional motion event squashing we could add some
-     extra API to the Clutter event queue and implement this function
-     properly. */
-  return TRUE;
+check_use_this_motion_notify (MetaWindow *window,
+                              XIDeviceEvent *xev)
+{
+  EventScannerData esd;
+  XEvent useless;
+
+  /* This code is copied from Owen's GDK code. */
+
+  if (window->display->grab_motion_notify_time != 0)
+    {
+      /* == is really the right test, but I'm all for paranoia */
+      if (window->display->grab_motion_notify_time <=
+          xev->time)
+        {
+          meta_topic (META_DEBUG_RESIZING,
+                      "Arrived at event with time %u (waiting for %u), using it\n",
+                      (unsigned int)xev->time,
+                      window->display->grab_motion_notify_time);
+          window->display->grab_motion_notify_time = 0;
+          return TRUE;
+        }
+      else
+        return FALSE; /* haven't reached the saved timestamp yet */
+    }
+
+  esd.window = xev->event;
+  esd.count = 0;
+  esd.last_time = 0;
+
+  /* "useless" isn't filled in because the predicate never returns True */
+  XCheckIfEvent (window->display->xdisplay,
+                 &useless,
+                 find_last_time_predicate,
+                 (XPointer) &esd);
+
+  if (esd.count > 0)
+    meta_topic (META_DEBUG_RESIZING,
+                "Will skip %d motion events and use the event with time %u\n",
+                esd.count, (unsigned int) esd.last_time);
+
+  if (esd.last_time == 0)
+    return TRUE;
+  else
+    {
+      /* Save this timestamp, and ignore all motion notify
+       * until we get to the one with this stamp.
+       */
+      window->display->grab_motion_notify_time = esd.last_time;
+      return FALSE;
+    }
 }
 
 static void
@@ -10116,8 +10192,98 @@ meta_window_update_sync_request_counter (MetaWindow *window,
 #endif /* HAVE_XSYNC */
 
 void
-meta_window_handle_mouse_grab_op_event (MetaWindow         *window,
-                                        const ClutterEvent *event)
+meta_window_handle_mouse_grab_op_xevent (MetaWindow         *window,
+                                         XIDeviceEvent      *xevent)
+{
+  gboolean is_window_root = (xevent->root == window->screen->xroot);
+
+  switch (xevent->evtype)
+    {
+    case XI_ButtonRelease:
+      if (xevent->detail == 1 ||
+          xevent->detail == meta_prefs_get_mouse_button_resize ())
+        {
+          meta_display_check_threshold_reached (window->display,
+                                                xevent->root_x,
+                                                xevent->root_y);
+          /* If the user was snap moving then ignore the button
+           * release because they may have let go of shift before
+           * releasing the mouse button and they almost certainly do
+           * not want a non-snapped movement to occur from the button
+           * release.
+           */
+          if (!window->display->grab_last_user_action_was_snap)
+            {
+              if (meta_grab_op_is_moving (window->display->grab_op))
+                {
+                  if (window->tile_mode != META_TILE_NONE)
+                    meta_window_tile (window);
+                  else if (is_window_root)
+                    update_move (window,
+                                 xevent->mods.effective & ShiftMask,
+                                 xevent->root_x,
+                                 xevent->root_y);
+                }
+              else if (meta_grab_op_is_resizing (window->display->grab_op))
+                {
+                  if (is_window_root)
+                    update_resize (window,
+                                   xevent->mods.effective & ShiftMask,
+                                   xevent->root_x,
+                                   xevent->root_y,
+                                   TRUE);
+
+                  /* If a tiled window has been dragged free with a
+                   * mouse resize without snapping back to the tiled
+                   * state, it will end up with an inconsistent tile
+                   * mode on mouse release; cleaning the mode earlier
+                   * would break the ability to snap back to the tiled
+                   * state, so we wait until mouse release.
+                   */
+                  update_tile_mode (window);
+                }
+              meta_display_end_grab_op (window->display, xevent->time);
+            }
+        }
+      break;
+
+    case XI_Motion:
+      meta_display_check_threshold_reached (window->display,
+                                            xevent->root_x,
+                                            xevent->root_y);
+      if (meta_grab_op_is_moving (window->display->grab_op))
+        {
+          if (is_window_root)
+            {
+              if (check_use_this_motion_notify (window, xevent))
+                update_move (window,
+                             xevent->mods.effective & ShiftMask,
+                             xevent->root_x,
+                             xevent->root_y);
+            }
+        }
+      else if (meta_grab_op_is_resizing (window->display->grab_op))
+        {
+          if (is_window_root)
+            {
+              if (check_use_this_motion_notify (window, xevent))
+                update_resize (window,
+                               xevent->mods.effective & ShiftMask,
+                               xevent->root_x,
+                               xevent->root_y,
+                               FALSE);
+            }
+        }
+      break;
+
+    default:
+      break;
+    }
+}
+
+void
+meta_window_handle_mouse_grab_op_event  (MetaWindow         *window,
+                                         const ClutterEvent *event)
 {
   gboolean is_window_root = (event->any.stage != NULL &&
                              window &&
@@ -10170,7 +10336,6 @@ meta_window_handle_mouse_grab_op_event (MetaWindow         *window,
                    */
                   update_tile_mode (window);
                 }
-
               meta_display_end_grab_op (window->display, event->any.time);
             }
         }
@@ -10184,23 +10349,21 @@ meta_window_handle_mouse_grab_op_event (MetaWindow         *window,
         {
           if (is_window_root)
             {
-              if (check_use_this_motion_notify (window, event))
-                update_move (window,
-                             event->button.modifier_state & CLUTTER_SHIFT_MASK,
-                             event->motion.x,
-                             event->motion.y);
+              update_move (window,
+                           event->button.modifier_state & CLUTTER_SHIFT_MASK,
+                           event->motion.x,
+                           event->motion.y);
             }
         }
       else if (meta_grab_op_is_resizing (window->display->grab_op))
         {
           if (is_window_root)
             {
-              if (check_use_this_motion_notify (window, event))
-                update_resize (window,
-                               event->button.modifier_state & CLUTTER_SHIFT_MASK,
-                               event->motion.x,
-                               event->motion.y,
-                               FALSE);
+              update_resize (window,
+                             event->button.modifier_state & CLUTTER_SHIFT_MASK,
+                             event->motion.x,
+                             event->motion.y,
+                             FALSE);
             }
         }
       break;


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