diff --git a/intern/ghost/intern/GHOST_SystemX11.cc b/intern/ghost/intern/GHOST_SystemX11.cc index 67338b8ce39..587dad92e2b 100644 --- a/intern/ghost/intern/GHOST_SystemX11.cc +++ b/intern/ghost/intern/GHOST_SystemX11.cc @@ -107,6 +107,7 @@ GHOST_SystemX11::GHOST_SystemX11() : GHOST_System(), m_xkb_descr(nullptr), m_start_time(0), + m_start_time_monotonic(0), m_keyboard_vector{0}, m_keycode_last_repeat_key(uint(-1)) { @@ -172,14 +173,23 @@ GHOST_SystemX11::GHOST_SystemX11() m_last_release_keycode = 0; m_last_release_time = 0; - /* compute the initial time */ - timeval tv; - if (gettimeofday(&tv, nullptr) == -1) { - GHOST_ASSERT(false, "Could not instantiate timer!"); + /* Compute the initial time. */ + { + timeval tv; + if (gettimeofday(&tv, nullptr) == -1) { + GHOST_ASSERT(false, "Could not instantiate timer!"); + } + /* Taking care not to overflow the `tv.tv_sec * 1000`. */ + m_start_time = uint64_t(tv.tv_sec) * 1000 + tv.tv_usec / 1000; } - /* Taking care not to overflow the `tv.tv_sec * 1000`. */ - m_start_time = uint64_t(tv.tv_sec) * 1000 + tv.tv_usec / 1000; + { + struct timespec ts = {0, 0}; + if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) { + GHOST_ASSERT(false, "Could not instantiate monotonic timer!"); + } + m_start_time_monotonic = ((uint64_t(ts.tv_sec) * 1000) + (ts.tv_nsec / 1000000)); + } /* Use detectable auto-repeat, mac and windows also do this. */ int use_xkb; @@ -279,6 +289,16 @@ uint64_t GHOST_SystemX11::getMilliSeconds() const return uint64_t(tv.tv_sec) * 1000 + tv.tv_usec / 1000 - m_start_time; } +uint64_t GHOST_SystemX11::ms_from_input_time(const Time timestamp) const +{ + GHOST_ASSERT(timestamp >= m_start_time_monotonic, "Invalid time-stemp"); + /* NOTE(@ideasman42): Return a time compatible with `getMilliSeconds()`, + * this is needed as X11 time-stamps use monotonic time. + * The X11 implementation *could* use any basis, in practice though we are supporting + * XORG/LIBINPUT which uses time-stamps based on the monotonic time. */ + return uint64_t(timestamp) - m_start_time_monotonic; +} + uint8_t GHOST_SystemX11::getNumDisplays() const { return uint8_t(1); @@ -637,6 +657,7 @@ bool GHOST_SystemX11::processEvents(bool waitForEvent) XPeekEvent(m_display, &xev_next); if (ELEM(xev_next.type, KeyPress, KeyRelease)) { + const uint64_t event_ms = ms_from_input_time(xev_next.xkey.time); /* XK_Hyper_L/R currently unused. */ const static KeySym modifiers[] = { XK_Shift_L, @@ -652,7 +673,7 @@ bool GHOST_SystemX11::processEvents(bool waitForEvent) for (int i = 0; i < int(ARRAY_SIZE(modifiers)); i++) { KeyCode kc = XKeysymToKeycode(m_display, modifiers[i]); if (kc != 0 && ((xevent.xkeymap.key_vector[kc >> 3] >> (kc & 7)) & 1) != 0) { - pushEvent(new GHOST_EventKey(getMilliSeconds(), + pushEvent(new GHOST_EventKey(event_ms, GHOST_kEventKeyDown, window, ghost_key_from_keysym(modifiers[i]), @@ -850,16 +871,19 @@ void GHOST_SystemX11::processEvent(XEvent *xe) const XExposeEvent &xee = xe->xexpose; if (xee.count == 0) { - /* Only generate a single expose event - * per read of the event queue. */ + /* Only generate a single expose event per read of the event queue. */ - g_event = new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window); + /* Event has no timestamp. */ + const uint64_t event_ms = getMilliSeconds(); + + g_event = new GHOST_Event(event_ms, GHOST_kEventWindowUpdate, window); } break; } case MotionNotify: { const XMotionEvent &xme = xe->xmotion; + const uint64_t event_ms = ms_from_input_time(xme.time); bool is_tablet = window->GetTabletData().Active != GHOST_kTabletModeNone; @@ -932,7 +956,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe) setCursorPosition(x_new, y_new); /* wrap */ } else { - g_event = new GHOST_EventCursor(getMilliSeconds(), + g_event = new GHOST_EventCursor(event_ms, GHOST_kEventCursorMove, window, xme.x_root + x_accum, @@ -941,7 +965,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe) } } else { - g_event = new GHOST_EventCursor(getMilliSeconds(), + g_event = new GHOST_EventCursor(event_ms, GHOST_kEventCursorMove, window, xme.x_root, @@ -954,6 +978,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe) case KeyPress: case KeyRelease: { XKeyEvent *xke = &(xe->xkey); + const uint64_t event_ms = ms_from_input_time(xke->time); KeySym key_sym; char *utf8_buf = nullptr; char ascii; @@ -1146,7 +1171,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe) } } - g_event = new GHOST_EventKey(getMilliSeconds(), type, window, gkey, is_repeat, utf8_buf); + g_event = new GHOST_EventKey(event_ms, type, window, gkey, is_repeat, utf8_buf); #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) /* when using IM for some languages such as Japanese, @@ -1171,8 +1196,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe) /* Enqueue previous character. */ pushEvent(g_event); - g_event = new GHOST_EventKey( - getMilliSeconds(), type, window, gkey, is_repeat, &utf8_buf[i]); + g_event = new GHOST_EventKey(event_ms, type, window, gkey, is_repeat, &utf8_buf[i]); } } @@ -1187,6 +1211,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe) case ButtonPress: case ButtonRelease: { const XButtonEvent &xbe = xe->xbutton; + const uint64_t event_ms = ms_from_input_time(xbe.time); GHOST_TButton gbmask = GHOST_kButtonMaskLeft; GHOST_TEventType type = (xbe.type == ButtonPress) ? GHOST_kEventButtonDown : GHOST_kEventButtonUp; @@ -1194,13 +1219,13 @@ void GHOST_SystemX11::processEvent(XEvent *xe) /* process wheel mouse events and break, only pass on press events */ if (xbe.button == Button4) { if (xbe.type == ButtonPress) { - g_event = new GHOST_EventWheel(getMilliSeconds(), window, 1); + g_event = new GHOST_EventWheel(event_ms, window, 1); } break; } if (xbe.button == Button5) { if (xbe.type == ButtonPress) { - g_event = new GHOST_EventWheel(getMilliSeconds(), window, -1); + g_event = new GHOST_EventWheel(event_ms, window, -1); } break; } @@ -1234,15 +1259,17 @@ void GHOST_SystemX11::processEvent(XEvent *xe) break; } - g_event = new GHOST_EventButton( - getMilliSeconds(), type, window, gbmask, window->GetTabletData()); + g_event = new GHOST_EventButton(event_ms, type, window, gbmask, window->GetTabletData()); break; } /* change of size, border, layer etc. */ case ConfigureNotify: { // const XConfigureEvent & xce = xe->xconfigure; - g_event = new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window); + /* Event has no timestamp. */ + const uint64_t event_ms = getMilliSeconds(); + + g_event = new GHOST_Event(event_ms, GHOST_kEventWindowSize, window); break; } @@ -1338,8 +1365,9 @@ void GHOST_SystemX11::processEvent(XEvent *xe) * also send grab/un-grab crossings for mouse-wheel events. */ const XCrossingEvent &xce = xe->xcrossing; + const uint64_t event_ms = ms_from_input_time(xce.time); if (xce.mode == NotifyNormal) { - g_event = new GHOST_EventCursor(getMilliSeconds(), + g_event = new GHOST_EventCursor(event_ms, GHOST_kEventCursorMove, window, xce.x_root, @@ -2538,8 +2566,12 @@ GHOST_TSuccess GHOST_SystemX11::pushDragDropEvent(GHOST_TEventType eventType, void *data) { GHOST_SystemX11 *system = ((GHOST_SystemX11 *)getSystem()); + + /* Caller has no timestamp. */ + const uint64_t event_ms = system->getMilliSeconds(); + return system->pushEvent(new GHOST_EventDragnDrop( - system->getMilliSeconds(), eventType, draggedObjectType, window, mouseX, mouseY, data)); + event_ms, eventType, draggedObjectType, window, mouseX, mouseY, data)); } #endif /** diff --git a/intern/ghost/intern/GHOST_SystemX11.hh b/intern/ghost/intern/GHOST_SystemX11.hh index 36894512d07..8bd41f4d265 100644 --- a/intern/ghost/intern/GHOST_SystemX11.hh +++ b/intern/ghost/intern/GHOST_SystemX11.hh @@ -200,6 +200,13 @@ class GHOST_SystemX11 : public GHOST_System { } #endif + /** + * Use this function instead of #GHOST_System::getMilliSeconds, + * passing in the time-stamp from X to input to get the event + * time-stamp with an offset applied to make it compatible with `getMilliSeconds`. + */ + uint64_t ms_from_input_time(const Time timestamp_as_uint) const; + /** Helped function for get data from the clipboard. */ void getClipboard_xcout(const XEvent *evt, Atom sel, @@ -343,6 +350,8 @@ class GHOST_SystemX11 : public GHOST_System { /** Start time at initialization. */ uint64_t m_start_time; + /** Start time at initialization (using `CLOCK_MONOTONIC`). */ + uint64_t m_start_time_monotonic; /** A vector of keyboard key masks. */ char m_keyboard_vector[32];