diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Display.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Display.java index c2524f8e8c9..eeba3421a5e 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Display.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Display.java @@ -4636,7 +4636,9 @@ void sendEvent (int eventType, Event event) { if (event == null) event = new Event (); event.display = this; event.type = eventType; - if (event.time == 0) event.time = getLastEventTime (); + // See comment in Widget.sendEvent(int, Event, boolean) for why + // getLastEventTime() is not used as a fallback here. + if (event.time == 0) event.time = (int) (System.nanoTime() / 1_000_000L); sendEvent (eventTable, event); } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Widget.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Widget.java index 3804a64ecf6..8d49f138b4c 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Widget.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Widget.java @@ -1674,7 +1674,17 @@ void sendEvent (int eventType, Event event, boolean send) { event.display = display; event.widget = this; if (event.time == 0) { - event.time = display.getLastEventTime (); + /* + * display.getLastEventTime() only reflects the time of the last *native* + * NSEvent that was processed; it is not updated by synthetic events + * dispatched directly (e.g. notifyListeners()) without going through the + * native event queue. Relying on it here would make consecutive + * synthetic events (as commonly created by tests) carry the same, stale + * time, which can incorrectly look like a repeated/duplicate event to + * code (e.g. KeyBindingDispatcher) that de-duplicates by event time. + * Use a distinct, always-advancing timestamp instead. + */ + event.time = (int) (System.nanoTime() / 1_000_000L); } if (send) { sendEvent (event); diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java index 4a1bc89cce3..769d255e887 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java @@ -5875,7 +5875,9 @@ void sendEvent (int eventType, Event event) { if (event == null) event = new Event (); event.display = this; event.type = eventType; - if (event.time == 0) event.time = getLastEventTime (); + // See comment in Widget.sendEvent(int, Event, boolean) for why + // getLastEventTime() is not used as a fallback here. + if (event.time == 0) event.time = (int) (System.nanoTime() / 1_000_000L); if (!filterEvent (event)) { if (eventTable != null) sendEvent (eventTable, event); } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Widget.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Widget.java index 3471c7e010d..cee92f17042 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Widget.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Widget.java @@ -1679,7 +1679,18 @@ void sendEvent (int eventType, Event event, boolean send) { event.display = display; event.widget = this; if (event.time == 0) { - event.time = display.getLastEventTime (); + /* + * display.getLastEventTime() only reflects the time of the last *native* + * GDK event that was processed; it is not updated by synthetic events + * dispatched directly (e.g. notifyListeners()) without going through the + * native event queue. Relying on it here would make consecutive + * synthetic events (as commonly created by tests) carry the same, stale + * time, which can incorrectly look like a repeated/duplicate event to + * code (e.g. KeyBindingDispatcher) that de-duplicates by event time. + * Use a distinct, always-advancing timestamp instead, consistent with + * how other synthetic events (see sendJDKInternalEvent) are timestamped. + */ + event.time = (int) (System.nanoTime() / 1_000_000L); } if (send) { sendEvent (event);