Skip to content

Commit

Permalink
GHOST/X11: Support X11 time-stamps
Browse files Browse the repository at this point in the history
Resolves #114835 on X11.
  • Loading branch information
ideasman42 committed Dec 6, 2023
1 parent 8580718 commit efef709
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 22 deletions.
76 changes: 54 additions & 22 deletions intern/ghost/intern/GHOST_SystemX11.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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))
{
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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,
Expand All @@ -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]),
Expand Down Expand Up @@ -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. */

/* Event has no timestamp. */
const uint64_t event_ms = getMilliSeconds();

g_event = new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window);
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;

Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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;
Expand Down Expand Up @@ -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,
Expand All @@ -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]);
}
}

Expand All @@ -1187,20 +1211,21 @@ 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;

/* 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;
}
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
/**
Expand Down
9 changes: 9 additions & 0 deletions intern/ghost/intern/GHOST_SystemX11.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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];
Expand Down

0 comments on commit efef709

Please sign in to comment.