From 994bedb024d872eba79729f39056bf1aee4bfb66 Mon Sep 17 00:00:00 2001 From: Felipe Ferreira da Silva Date: Thu, 11 May 2017 17:15:16 -0300 Subject: [PATCH] Get event time --- include/GLFW/glfw3.h | 22 ++++++++++++++++++++++ src/cocoa_window.m | 31 +++++++++++++++++++++++++++++++ src/internal.h | 1 + src/mir_platform.h | 1 + src/mir_window.c | 12 ++++++++++++ src/null_window.c | 5 +++++ src/win32_platform.h | 1 + src/win32_window.c | 10 ++++++++++ src/window.c | 10 ++++++++++ src/wl_init.c | 4 ++++ src/wl_platform.h | 2 ++ src/wl_window.c | 5 +++++ src/x11_platform.h | 2 ++ src/x11_window.c | 11 +++++++++++ tests/events.c | 24 ++++++++++++------------ 15 files changed, 129 insertions(+), 12 deletions(-) diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 706fb9584..cc31a5a2b 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -3224,6 +3224,28 @@ GLFWAPI GLFWwindowmaximizefun glfwSetWindowMaximizeCallback(GLFWwindow* window, */ GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* window, GLFWframebuffersizefun cbfun); +/*! @brief Returns the time of the last input event. + * + * This function returns the time, in seconds, of the last event occurence. The + * only events queried are the input events button-press, button-release, + * key-press, key-release and cursor motion, and the proper place to call this + * function is in one of the input callbacks. + * + * @param[in] window The window to check for the input event. + * @return The value, in seconds, of the last input event occurence. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref events + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI double glfwGetEventTime(GLFWwindow* window); + /*! @brief Processes all pending events. * * This function processes only those events that are already in the event diff --git a/src/cocoa_window.m b/src/cocoa_window.m index d2b207bb9..67789c07b 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -433,6 +433,8 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; - (void)mouseDown:(NSEvent *)event { + window->ns.lastEventTime = [event timestamp]; + _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, @@ -441,11 +443,15 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; - (void)mouseDragged:(NSEvent *)event { + window->ns.lastEventTime = [event timestamp]; + [self mouseMoved:event]; } - (void)mouseUp:(NSEvent *)event { + window->ns.lastEventTime = [event timestamp]; + _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_RELEASE, @@ -454,6 +460,8 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; - (void)mouseMoved:(NSEvent *)event { + window->ns.lastEventTime = [event timestamp]; + if (window->cursorMode == GLFW_CURSOR_DISABLED) { const double dx = [event deltaX] - window->ns.cursorWarpDeltaX; @@ -477,6 +485,8 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; - (void)rightMouseDown:(NSEvent *)event { + window->ns.lastEventTime = [event timestamp]; + _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, GLFW_PRESS, @@ -485,11 +495,15 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; - (void)rightMouseDragged:(NSEvent *)event { + window->ns.lastEventTime = [event timestamp]; + [self mouseMoved:event]; } - (void)rightMouseUp:(NSEvent *)event { + window->ns.lastEventTime = [event timestamp]; + _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, GLFW_RELEASE, @@ -498,6 +512,8 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; - (void)otherMouseDown:(NSEvent *)event { + window->ns.lastEventTime = [event timestamp]; + _glfwInputMouseClick(window, (int) [event buttonNumber], GLFW_PRESS, @@ -506,11 +522,15 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; - (void)otherMouseDragged:(NSEvent *)event { + window->ns.lastEventTime = [event timestamp]; + [self mouseMoved:event]; } - (void)otherMouseUp:(NSEvent *)event { + window->ns.lastEventTime = [event timestamp]; + _glfwInputMouseClick(window, (int) [event buttonNumber], GLFW_RELEASE, @@ -569,6 +589,8 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; const int key = translateKey([event keyCode]); const int mods = translateFlags([event modifierFlags]); + window->ns.lastEventTime = [event timestamp]; + _glfwInputKey(window, key, [event keyCode], GLFW_PRESS, mods); [self interpretKeyEvents:[NSArray arrayWithObject:event]]; @@ -600,6 +622,9 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; { const int key = translateKey([event keyCode]); const int mods = translateFlags([event modifierFlags]); + + window->ns.lastEventTime = [event timestamp]; + _glfwInputKey(window, key, [event keyCode], GLFW_RELEASE, mods); } @@ -1433,6 +1458,12 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) [window->ns.object setLevel:NSNormalWindowLevel]; } +double _glfwPlatformGetEventTime(_GLFWwindow* window) +{ + /* Windows events are stored in seconds */ + return window->ns.lastEventTime; +} + void _glfwPlatformPollEvents(void) { for (;;) diff --git a/src/internal.h b/src/internal.h index c2b358133..8ada4247b 100644 --- a/src/internal.h +++ b/src/internal.h @@ -642,6 +642,7 @@ void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled); void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled); void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled); +double _glfwPlatformGetEventTime(_GLFWwindow* window); void _glfwPlatformPollEvents(void); void _glfwPlatformWaitEvents(void); void _glfwPlatformWaitEventsTimeout(double timeout); diff --git a/src/mir_platform.h b/src/mir_platform.h index d3fd10de0..aa17c8a70 100644 --- a/src/mir_platform.h +++ b/src/mir_platform.h @@ -87,6 +87,7 @@ typedef struct _GLFWwindowMir MirEGLNativeWindowType nativeWindow; _GLFWcursor* currentCursor; + int64_t lastEventTime; } _GLFWwindowMir; // Mir-specific per-monitor data diff --git a/src/mir_window.c b/src/mir_window.c index 566f571c1..09dd9226c 100644 --- a/src/mir_window.c +++ b/src/mir_window.c @@ -147,6 +147,8 @@ static void handleKeyEvent(const MirKeyboardEvent* key_event, _GLFWwindow* windo const long text = _glfwKeySym2Unicode(key_code); const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT)); + window->mir.lastEventTime = mir_input_event_get_event_time((MirInputEvent *) key_event); + _glfwInputKey(window, toGLFWKeyCode(scan_code), scan_code, pressed, mods); if (text != -1) @@ -164,6 +166,8 @@ static void handlePointerButton(_GLFWwindow* window, uint32_t newButtonStates = mir_pointer_event_buttons(pointer_event); int publicButton = GLFW_MOUSE_BUTTON_LEFT; + window->mir.lastEventTime = mir_input_event_get_event_time((MirEvent *) pointer_event); + // XOR our old button states our new states to figure out what was added or removed button = newButtonStates ^ oldButtonStates; @@ -201,6 +205,8 @@ static void handlePointerMotion(_GLFWwindow* window, const int hscroll = mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_hscroll); const int vscroll = mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_vscroll); + window->mir.lastEventTime = mir_input_event_get_event_time((MirEvent *) pointer_event); + if (window->cursorMode == GLFW_CURSOR_DISABLED) { if (_glfw.mir.disabledCursorWindow != window) @@ -632,6 +638,12 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) "Mir: Unsupported function %s", __PRETTY_FUNCTION__); } +double _glfwPlatformGetEventTime(_GLFWwindow* window) +{ + /* Mir events are stored in nanoseconds */ + return (double) window->mir.lastEventTime / 1000000000.0; +} + void _glfwPlatformPollEvents(void) { EventNode* node = NULL; diff --git a/src/null_window.c b/src/null_window.c index 137f80c8f..24b5a9270 100644 --- a/src/null_window.c +++ b/src/null_window.c @@ -204,6 +204,11 @@ int _glfwPlatformWindowVisible(_GLFWwindow* window) return GLFW_FALSE; } +double _glfwPlatformGetEventTime(_GLFWwindow* window) +{ + return 0.0; +} + void _glfwPlatformPollEvents(void) { } diff --git a/src/win32_platform.h b/src/win32_platform.h index d51dee25c..6f92b45e2 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -241,6 +241,7 @@ typedef struct _GLFWwindowWin32 // The last received cursor position, regardless of source int lastCursorPosX, lastCursorPosY; + LONG lastEventTime; } _GLFWwindowWin32; // Win32-specific global data diff --git a/src/win32_window.c b/src/win32_window.c index 6d691e196..5a0eb1b06 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -552,6 +552,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, case WM_UNICHAR: { const GLFWbool plain = (uMsg != WM_SYSCHAR); + window->win32.lastEventTime = GetMessageTime(); if (uMsg == WM_UNICHAR && wParam == UNICODE_NOCHAR) { @@ -574,6 +575,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, const int scancode = (lParam >> 16) & 0x1ff; const int action = ((lParam >> 31) & 1) ? GLFW_RELEASE : GLFW_PRESS; const int mods = getKeyMods(); + window->win32.lastEventTime = GetMessageTime(); if (key == _GLFW_KEY_INVALID) break; @@ -608,6 +610,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, case WM_XBUTTONUP: { int i, button, action; + window->win32.lastEventTime = GetMessageTime(); if (uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONUP) button = GLFW_MOUSE_BUTTON_LEFT; @@ -658,6 +661,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, { const int x = GET_X_LPARAM(lParam); const int y = GET_Y_LPARAM(lParam); + window->win32.lastEventTime = GetMessageTime(); // Disabled cursor motion input is provided by WM_INPUT if (window->cursorMode == GLFW_CURSOR_DISABLED) @@ -1451,6 +1455,12 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); } +double _glfwPlatformGetEventTime(_GLFWwindow* window) +{ + /* Windows events are stored in milliseconds */ + return (double) window->win32.lastEventTime / 1000.0; +} + void _glfwPlatformPollEvents(void) { MSG msg; diff --git a/src/window.c b/src/window.c index 546d233d8..f87da1ea1 100644 --- a/src/window.c +++ b/src/window.c @@ -961,6 +961,16 @@ GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* handle return cbfun; } +GLFWAPI double glfwGetEventTime(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(0.0); + + return _glfwPlatformGetEventTime(window); +} + GLFWAPI void glfwPollEvents(void) { _GLFW_REQUIRE_INIT(); diff --git a/src/wl_init.c b/src/wl_init.c index 0927a98a8..3b6a0dca7 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -83,6 +83,7 @@ static void pointerHandleMotion(void* data, if (!window) return; + window->wl.lastEventTime = time; if (window->cursorMode == GLFW_CURSOR_DISABLED) return; else @@ -109,6 +110,7 @@ static void pointerHandleButton(void* data, if (!window) return; + window->wl.lastEventTime = time; _glfw.wl.pointerSerial = serial; /* Makes left, right and middle 0, 1 and 2. Overall order follows evdev @@ -136,6 +138,7 @@ static void pointerHandleAxis(void* data, if (!window) return; + window->wl.lastEventTime = time; /* Wayland scroll events are in pointer motion coordinate space (think * two finger scroll). The factor 10 is commonly used to convert to * "scroll step means 1.0. */ @@ -349,6 +352,7 @@ static void keyboardHandleKey(void* data, if (!window) return; + window->wl.lastEventTime = time; keyCode = toGLFWKeyCode(key); action = state == WL_KEYBOARD_KEY_STATE_PRESSED ? GLFW_PRESS : GLFW_RELEASE; diff --git a/src/wl_platform.h b/src/wl_platform.h index bea32350b..8c609dd09 100644 --- a/src/wl_platform.h +++ b/src/wl_platform.h @@ -100,6 +100,8 @@ typedef struct _GLFWwindowWayland struct zwp_relative_pointer_v1* relativePointer; struct zwp_locked_pointer_v1* lockedPointer; } pointerLock; + + unsigned int lastEventTime; } _GLFWwindowWayland; // Wayland-specific global data diff --git a/src/wl_window.c b/src/wl_window.c index e9c817d50..f317a82f6 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -675,6 +675,11 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) "Wayland: Window attribute setting not implemented yet"); } +double _glfwPlatformGetEventTime(_GLFWwindow* window) +{ + return (double) window->wl.lastEventTime / 1000.0; +} + void _glfwPlatformPollEvents(void) { handleEvents(0); diff --git a/src/x11_platform.h b/src/x11_platform.h index a0037c88b..f9cea3a1a 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -144,6 +144,8 @@ typedef struct _GLFWwindowX11 // The time of the last KeyPress event Time lastKeyTime; + // The time of the last event + Time lastEventTime; } _GLFWwindowX11; // X11-specific global data diff --git a/src/x11_window.c b/src/x11_window.c index a683a821e..b53bc15dd 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -988,6 +988,7 @@ static void processEvent(XEvent *event) const int key = translateKey(keycode); const int mods = translateState(event->xkey.state); const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT)); + window->x11.lastEventTime = event->xkey.time; if (window->x11.ic) { @@ -1081,6 +1082,7 @@ static void processEvent(XEvent *event) { const int key = translateKey(keycode); const int mods = translateState(event->xkey.state); + window->x11.lastEventTime = event->xkey.time; if (!_glfw.x11.xkb.detectable) { @@ -1121,6 +1123,7 @@ static void processEvent(XEvent *event) case ButtonPress: { const int mods = translateState(event->xbutton.state); + window->x11.lastEventTime = event->xbutton.time; if (event->xbutton.button == Button1) _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, mods); @@ -1155,6 +1158,7 @@ static void processEvent(XEvent *event) case ButtonRelease: { const int mods = translateState(event->xbutton.state); + window->x11.lastEventTime = event->xbutton.time; if (event->xbutton.button == Button1) { @@ -1211,6 +1215,7 @@ static void processEvent(XEvent *event) { const int x = event->xmotion.x; const int y = event->xmotion.y; + window->x11.lastEventTime = event->xmotion.time; if (x != window->x11.warpCursorPosX || y != window->x11.warpCursorPosY) { @@ -2295,6 +2300,12 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) XFlush(_glfw.x11.display); } +double _glfwPlatformGetEventTime(_GLFWwindow* window) +{ + /* X11 events are stored in milliseconds */ + return (double) window->x11.lastEventTime / 1000.0; +} + void _glfwPlatformPollEvents(void) { _GLFWwindow* window; diff --git a/tests/events.c b/tests/events.c index 7b42e4fd3..77640aef5 100644 --- a/tests/events.c +++ b/tests/events.c @@ -269,8 +269,8 @@ static void error_callback(int error, const char* description) static void window_pos_callback(GLFWwindow* window, int x, int y) { Slot* slot = glfwGetWindowUserPointer(window); - printf("%08x to %i at %0.3f: Window position: %i %i\n", - counter++, slot->number, glfwGetTime(), x, y); + printf("%08x to %i at %0.3f (event time: %0.3f): Window position: %i %i\n", + counter++, slot->number, glfwGetTime(), glfwGetEventTime(window), x, y); } static void window_size_callback(GLFWwindow* window, int width, int height) @@ -336,8 +336,8 @@ static void window_maximize_callback(GLFWwindow* window, int maximized) static void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) { Slot* slot = glfwGetWindowUserPointer(window); - printf("%08x to %i at %0.3f: Mouse button %i (%s) (with%s) was %s\n", - counter++, slot->number, glfwGetTime(), button, + printf("%08x to %i at %0.3f (event time: %0.3f): Mouse button %i (%s) (with%s) was %s\n", + counter++, slot->number, glfwGetTime(), glfwGetEventTime(window), button, get_button_name(button), get_mods_name(mods), get_action_name(action)); @@ -346,8 +346,8 @@ static void mouse_button_callback(GLFWwindow* window, int button, int action, in static void cursor_position_callback(GLFWwindow* window, double x, double y) { Slot* slot = glfwGetWindowUserPointer(window); - printf("%08x to %i at %0.3f: Cursor position: %f %f\n", - counter++, slot->number, glfwGetTime(), x, y); + printf("%08x to %i at %0.3f (event time: %0.3f): Cursor position: %f %f\n", + counter++, slot->number, glfwGetTime(), glfwGetEventTime(window), x, y); } static void cursor_enter_callback(GLFWwindow* window, int entered) @@ -372,8 +372,8 @@ static void key_callback(GLFWwindow* window, int key, int scancode, int action, if (name) { - printf("%08x to %i at %0.3f: Key 0x%04x Scancode 0x%04x (%s) (%s) (with%s) was %s\n", - counter++, slot->number, glfwGetTime(), key, scancode, + printf("%08x to %i at %0.3f (event time: %0.3f): Key 0x%04x Scancode 0x%04x (%s) (%s) (with%s) was %s\n", + counter++, slot->number, glfwGetTime(), glfwGetEventTime(window), key, scancode, get_key_name(key), name, get_mods_name(mods), @@ -381,8 +381,8 @@ static void key_callback(GLFWwindow* window, int key, int scancode, int action, } else { - printf("%08x to %i at %0.3f: Key 0x%04x Scancode 0x%04x (%s) (with%s) was %s\n", - counter++, slot->number, glfwGetTime(), key, scancode, + printf("%08x to %i at %0.3f (event time: %0.3f): Key 0x%04x Scancode 0x%04x (%s) (with%s) was %s\n", + counter++, slot->number, glfwGetTime(), glfwGetEventTime(window), key, scancode, get_key_name(key), get_mods_name(mods), get_action_name(action)); @@ -406,8 +406,8 @@ static void key_callback(GLFWwindow* window, int key, int scancode, int action, static void char_callback(GLFWwindow* window, unsigned int codepoint) { Slot* slot = glfwGetWindowUserPointer(window); - printf("%08x to %i at %0.3f: Character 0x%08x (%s) input\n", - counter++, slot->number, glfwGetTime(), codepoint, + printf("%08x to %i at %0.3f (event time: %0.3f): Character 0x%08x (%s) input\n", + counter++, slot->number, glfwGetTime(), glfwGetEventTime(window), codepoint, get_character_string(codepoint)); }