From 0e356fd193ae23667718b044fe42ca62262cfbe2 Mon Sep 17 00:00:00 2001 From: Karl Tomlinson Date: Tue, 14 Feb 2012 09:19:59 +1300 Subject: [PATCH] b=726443 rework last event time calculation to avoid problems with gtk_get_current_event_time in nested loops r=roc --HG-- extra : rebase_source : 151b60b10944ccf3ccee0cb69f89728ba1c29977 --- widget/gtk2/nsDragService.cpp | 2 +- widget/gtk2/nsWindow.cpp | 76 +++++++++++++++++++---------------- widget/gtk2/nsWindow.h | 7 ++-- 3 files changed, 46 insertions(+), 39 deletions(-) diff --git a/widget/gtk2/nsDragService.cpp b/widget/gtk2/nsDragService.cpp index 77e57d3afd7..be8f388e200 100644 --- a/widget/gtk2/nsDragService.cpp +++ b/widget/gtk2/nsDragService.cpp @@ -331,7 +331,7 @@ nsDragService::InvokeDragSession(nsIDOMNode *aDOMNode, memset(&event, 0, sizeof(GdkEvent)); event.type = GDK_BUTTON_PRESS; event.button.window = mHiddenWidget->window; - event.button.time = nsWindow::GetCurrentEventTime(); + event.button.time = nsWindow::GetLastUserInputTime(); // start our drag. GdkDragContext *context = gtk_drag_begin(mHiddenWidget, diff --git a/widget/gtk2/nsWindow.cpp b/widget/gtk2/nsWindow.cpp index 33640b44a1a..6ef9829e12e 100644 --- a/widget/gtk2/nsWindow.cpp +++ b/widget/gtk2/nsWindow.cpp @@ -282,15 +282,6 @@ static bool gdk_keyboard_get_modmap_masks(Display* aDisplay, /* initialization static functions */ static nsresult initialize_prefs (void); -static void -UpdateLastInputEventTime() -{ - nsCOMPtr idleService = do_GetService("@mozilla.org/widget/idleservice;1"); - if (idleService) { - idleService->ResetIdleTimeOut(); - } -} - // this is the last window that had a drag event happen on it. nsWindow *nsWindow::sLastDragMotionWindow = NULL; bool nsWindow::sIsDraggingOutOf = false; @@ -298,6 +289,7 @@ bool nsWindow::sIsDraggingOutOf = false; // Time of the last button release event. We use it to detect when the // drag ended before we could properly setup drag and drop. guint32 nsWindow::sLastButtonReleaseTime = 0; +static guint32 sLastUserInputTime = GDK_CURRENT_TIME; static guint32 sRetryGrabTime; static NS_DEFINE_IID(kCDragServiceCID, NS_DRAGSERVICE_CID); @@ -377,6 +369,29 @@ GetBitmapStride(PRInt32 width) #endif } +static inline bool TimestampIsNewerThan(guint32 a, guint32 b) +{ + // Timestamps are just the least significant bits of a monotonically + // increasing function, and so the use of unsigned overflow arithmetic. + return a - b <= G_MAXUINT32/2; +} + +static void +UpdateLastInputEventTime(void *aGdkEvent) +{ + nsCOMPtr idleService = + do_GetService("@mozilla.org/widget/idleservice;1"); + if (idleService) { + idleService->ResetIdleTimeOut(); + } + + guint timestamp = gdk_event_get_time(static_cast(aGdkEvent)); + if (timestamp == GDK_CURRENT_TIME) + return; + + sLastUserInputTime = timestamp; +} + nsWindow::nsWindow() { mIsTopLevel = false; @@ -1427,27 +1442,20 @@ SetUserTimeAndStartupIDForActivatedWindow(GtkWidget* aWindow) } /* static */ guint32 -nsWindow::GetCurrentEventTime() +nsWindow::GetLastUserInputTime() { - static guint32 sLastCurrentEventTime = GDK_CURRENT_TIME; - - guint32 timestamp = gtk_get_current_event_time(); - - if (timestamp == GDK_CURRENT_TIME) { - timestamp = gdk_x11_display_get_user_time(gdk_display_get_default()); - - // The user_time is not updated on all user events, so check that we - // haven't returned a more recent timestamp. If so, use the more - // recent timestamp to ensure that subsequent requests will override - // previous requests. Timestamps are just the least significant bits - // of a monotonically increasing function, and so the use of unsigned - // overflow arithmetic. - if (sLastCurrentEventTime != GDK_CURRENT_TIME && - sLastCurrentEventTime - timestamp <= G_MAXUINT32/2) - return sLastCurrentEventTime; + // gdk_x11_display_get_user_time tracks button and key presses, + // DESKTOP_STARTUP_ID used to start the app, drop events from external + // drags, WM_DELETE_WINDOW delete events, but not usually mouse motion nor + // button and key releases. Therefore use the most recent of + // gdk_x11_display_get_user_time and the last time that we have seen. + guint32 timestamp = + gdk_x11_display_get_user_time(gdk_display_get_default()); + if (sLastUserInputTime != GDK_CURRENT_TIME && + TimestampIsNewerThan(sLastUserInputTime, timestamp)) { + return sLastUserInputTime; } - sLastCurrentEventTime = timestamp; return timestamp; } @@ -1904,7 +1912,7 @@ nsWindow::CaptureMouse(bool aCapture) if (aCapture) { gtk_grab_add(widget); - GrabPointer(GetCurrentEventTime()); + GrabPointer(GetLastUserInputTime()); } else { ReleaseGrabs(); @@ -1936,7 +1944,7 @@ nsWindow::CaptureRollupEvents(nsIRollupListener *aListener, // real grab is only done when there is no dragging if (!nsWindow::DragInProgress()) { gtk_grab_add(widget); - GrabPointer(GetCurrentEventTime()); + GrabPointer(GetLastUserInputTime()); } } else { @@ -5726,7 +5734,7 @@ GetFirstNSWindowForGDKWindow(GdkWindow *aGdkWindow) static gboolean motion_notify_event_cb(GtkWidget *widget, GdkEventMotion *event) { - UpdateLastInputEventTime(); + UpdateLastInputEventTime(event); nsWindow *window = GetFirstNSWindowForGDKWindow(event->window); if (!window) @@ -5743,7 +5751,7 @@ motion_notify_event_cb(GtkWidget *widget, GdkEventMotion *event) static gboolean button_press_event_cb(GtkWidget *widget, GdkEventButton *event) { - UpdateLastInputEventTime(); + UpdateLastInputEventTime(event); nsWindow *window = GetFirstNSWindowForGDKWindow(event->window); if (!window) @@ -5757,7 +5765,7 @@ button_press_event_cb(GtkWidget *widget, GdkEventButton *event) static gboolean button_release_event_cb(GtkWidget *widget, GdkEventButton *event) { - UpdateLastInputEventTime(); + UpdateLastInputEventTime(event); nsWindow *window = GetFirstNSWindowForGDKWindow(event->window); if (!window) @@ -5963,7 +5971,7 @@ key_press_event_cb(GtkWidget *widget, GdkEventKey *event) { LOG(("key_press_event_cb\n")); - UpdateLastInputEventTime(); + UpdateLastInputEventTime(event); // find the window with focus and dispatch this event to that widget nsWindow *window = get_window_for_gtk_widget(widget); @@ -6006,7 +6014,7 @@ key_release_event_cb(GtkWidget *widget, GdkEventKey *event) { LOG(("key_release_event_cb\n")); - UpdateLastInputEventTime(); + UpdateLastInputEventTime(event); // find the window with focus and dispatch this event to that widget nsWindow *window = get_window_for_gtk_widget(widget); diff --git a/widget/gtk2/nsWindow.h b/widget/gtk2/nsWindow.h index 8a26945864a..386f0249c89 100644 --- a/widget/gtk2/nsWindow.h +++ b/widget/gtk2/nsWindow.h @@ -192,11 +192,10 @@ public: NS_IMETHOD HideWindowChrome(bool aShouldHide); /** - * GetCurrentEventTime guesses a timestamp for the most recent user input - * event (when the event is not available). This is intended for pointer - * grab or focus requests, for example. + * GetLastUserInputTime returns a timestamp for the most recent user input + * event. This is intended for pointer grab requests (including drags). */ - static guint32 GetCurrentEventTime(); + static guint32 GetLastUserInputTime(); // utility method, -1 if no change should be made, otherwise returns a // value that can be passed to gdk_window_set_decorations