diff --git a/README.md b/README.md index e6111ea4b..2dc397db9 100644 --- a/README.md +++ b/README.md @@ -368,6 +368,7 @@ skills. - Yuri Kunde Schlesner - Konstantin Käfer - Eric Larson + - Quinten Lansu - Robin Leffmann - Glenn Lewis - Shane Liesegang diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index a4c64f483..875e29648 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -310,6 +310,11 @@ extern "C" { * @ingroup input */ #define GLFW_REPEAT 2 + /*! @brief The touch was moved. + * + * @ingroup input + */ + #define GLFW_MOVE 3 /*! @} */ /*! @defgroup hat_state Joystick hat states @@ -998,6 +1003,7 @@ extern "C" { #define GLFW_STICKY_KEYS 0x00033002 #define GLFW_STICKY_MOUSE_BUTTONS 0x00033003 #define GLFW_LOCK_KEY_MODS 0x00033004 +#define GLFW_TOUCH 0x00033005 #define GLFW_CURSOR_NORMAL 0x00034001 #define GLFW_CURSOR_HIDDEN 0x00034002 @@ -1459,6 +1465,42 @@ typedef void (* GLFWcharmodsfun)(GLFWwindow*,unsigned int,int); */ typedef void (* GLFWdropfun)(GLFWwindow*,int,const char**); +/*! @brief Touch point info. +* +* This describes the touch point info. +* +* @sa @ref touch +* +* @since Added in version 3.2.1 (touch branch) +* +* @ingroup touch +*/ +typedef struct GLFWtouch +{ + /*! Touch id + */ + int id; + /*! Touch action + */ + int action; + /*! X position + */ + double x; + /*! Y position + */ + double y; +} GLFWtouch; + +/*! @brief The function signature for touch callbacks. +* @param[in] window The window that received the event. +* @param[in] touchPoints All valid touch points +* @param[in] count The number of valid touch points +* @ingroup event +* +* @sa glfwSetTouchCallback +*/ +typedef void(*GLFWtouchfun)(GLFWwindow*, GLFWtouch*, int); + /*! @brief The function signature for monitor configuration callbacks. * * This is the function signature for monitor configuration callback functions. @@ -4811,6 +4853,21 @@ GLFWAPI int glfwJoystickIsGamepad(int jid); */ GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun); +/*! @brief Sets the touch callback. +* +* This function sets the touch callback, which is called when a touch is +* started, ended or moved. +* +* @param[in] window The window whose callback to set. +* @param[in] cbfun The new scroll callback, or `NULL` to remove the currently +* set callback. +* @return The previously set callback, or `NULL` if no callback was set or an +* error occurred. +* +* @ingroup input +*/ +GLFWAPI GLFWtouchfun glfwSetTouchCallback(GLFWwindow* window, GLFWtouchfun cbfun); + /*! @brief Adds the specified SDL_GameControllerDB gamepad mappings. * * This function parses the specified ASCII encoded string and updates the diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 7cf4a593d..8261743cd 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1636,6 +1636,10 @@ void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) *ypos = contentRect.size.height - pos.y - 1; } +void _glfwPlatformSetTouchInput(_GLFWwindow* window, int enabled) +{ +} + void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) { updateCursorImage(window); diff --git a/src/input.c b/src/input.c index b0bb3de47..567719f19 100644 --- a/src/input.c +++ b/src/input.c @@ -108,6 +108,18 @@ static _GLFWmapping* findValidMapping(const _GLFWjoystick* js) return mapping; } +// Set touch input for the specified window +static void setTouchInput(_GLFWwindow* window, int enabled) +{ + if (window->touchInput == enabled) + return; + + _glfwPlatformSetTouchInput(window, enabled); + + window->touchInput = enabled; +} + + // Parses an SDL_GameControllerDB line and adds it to the mapping list // static GLFWbool parseMapping(_GLFWmapping* mapping, const char* string) @@ -401,6 +413,13 @@ void _glfwInputJoystickHat(_GLFWjoystick* js, int hat, char value) js->hats[hat] = value; } +// Notifies shared code of the new value of a touch event +// +void _glfwInputTouch(_GLFWwindow* window, GLFWtouch* touchPoints, int count) +{ + if (window->callbacks.touch) + window->callbacks.touch((GLFWwindow*)window, touchPoints, count); +} ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// @@ -475,6 +494,8 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* handle, int mode) return window->stickyMouseButtons; case GLFW_LOCK_KEY_MODS: return window->lockKeyMods; + case GLFW_TOUCH: + return window->touchInput; } _glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode); @@ -1074,6 +1095,15 @@ GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun) return cbfun; } +GLFWAPI GLFWtouchfun glfwSetTouchCallback(GLFWwindow* handle, GLFWtouchfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*)handle; + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.touch, cbfun); + setTouchInput(window, 1); + return cbfun; +} + GLFWAPI int glfwUpdateGamepadMappings(const char* string) { int jid; diff --git a/src/internal.h b/src/internal.h index 9fc626dde..b6aab6469 100644 --- a/src/internal.h +++ b/src/internal.h @@ -387,6 +387,7 @@ struct _GLFWwindow GLFWbool stickyKeys; GLFWbool stickyMouseButtons; GLFWbool lockKeyMods; + GLFWbool touchInput; int cursorMode; char mouseButtons[GLFW_MOUSE_BUTTON_LAST + 1]; char keys[GLFW_KEY_LAST + 1]; @@ -413,6 +414,7 @@ struct _GLFWwindow GLFWcharfun character; GLFWcharmodsfun charmods; GLFWdropfun drop; + GLFWtouchfun touch; } callbacks; // This is defined in the window API's platform.h @@ -596,6 +598,7 @@ extern _GLFWlibrary _glfw; int _glfwPlatformInit(void); void _glfwPlatformTerminate(void); const char* _glfwPlatformGetVersionString(void); +void _glfwPlatformSetTouchInput(_GLFWwindow* window, int enabled); void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos); void _glfwPlatformSetCursorPos(_GLFWwindow* window, double xpos, double ypos); @@ -725,6 +728,7 @@ void _glfwInputJoystick(_GLFWjoystick* js, int event); void _glfwInputJoystickAxis(_GLFWjoystick* js, int axis, float value); void _glfwInputJoystickButton(_GLFWjoystick* js, int button, char value); void _glfwInputJoystickHat(_GLFWjoystick* js, int hat, char value); +void _glfwInputTouch(_GLFWwindow* window, GLFWtouch* touchPoints, int count); void _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement); void _glfwInputMonitorWindow(_GLFWmonitor* monitor, _GLFWwindow* window); diff --git a/src/mir_window.c b/src/mir_window.c index a0d17db2e..81ec0a091 100644 --- a/src/mir_window.c +++ b/src/mir_window.c @@ -713,6 +713,10 @@ void _glfwPlatformPostEmptyEvent(void) { } +void _glfwPlatformSetTouchInput(_GLFWwindow* window, int enabled) +{ +} + void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) { if (width) diff --git a/src/null_window.c b/src/null_window.c index 6a54cfe56..6e9713754 100644 --- a/src/null_window.c +++ b/src/null_window.c @@ -248,6 +248,10 @@ void _glfwPlatformPostEmptyEvent(void) { } +void _glfwPlatformSetTouchInput(_GLFWwindow* window, int enabled) +{ +} + void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) { } diff --git a/src/win32_init.c b/src/win32_init.c index a913846d4..92500239d 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -100,6 +100,22 @@ static GLFWbool loadLibraries(void) GetProcAddress(_glfw.win32.user32.instance, "SetProcessDPIAware"); _glfw.win32.user32.ChangeWindowMessageFilterEx_ = (PFN_ChangeWindowMessageFilterEx) GetProcAddress(_glfw.win32.user32.instance, "ChangeWindowMessageFilterEx"); + _glfw.win32.user32.GetTouchInputInfo_ = (PFN_GetTouchInputInfo) + GetProcAddress(_glfw.win32.user32.instance, "GetTouchInputInfo"); + _glfw.win32.user32.CloseTouchInputHandle_ = (PFN_CloseTouchInputHandle) + GetProcAddress(_glfw.win32.user32.instance, "CloseTouchInputHandle"); + _glfw.win32.user32.RegisterTouchWindow_ = (PFN_RegisterTouchWindow) + GetProcAddress(_glfw.win32.user32.instance, "RegisterTouchWindow"); + _glfw.win32.user32.UnregisterTouchWindow_ = (PFN_UnregisterTouchWindow) + GetProcAddress(_glfw.win32.user32.instance, "UnregisterTouchWindow"); + + if (_glfw.win32.user32.GetTouchInputInfo_ && + _glfw.win32.user32.CloseTouchInputHandle_ && + _glfw.win32.user32.RegisterTouchWindow_ && + _glfw.win32.user32.UnregisterTouchWindow_) + { + _glfw.win32.touch.available = GLFW_TRUE; + } _glfw.win32.dinput8.instance = LoadLibraryA("dinput8.dll"); if (_glfw.win32.dinput8.instance) diff --git a/src/win32_platform.h b/src/win32_platform.h index 50b57441b..cdc26d6f2 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -192,6 +192,35 @@ BOOL IsWindowsVersionOrGreater(WORD major, WORD minor, WORD sp); #define DIDFT_OPTIONAL 0x80000000 #endif +// touch functionality +#if WINVER < 0x0601 + +#define WM_TOUCH 0x0240 + +DECLARE_HANDLE(HTOUCHINPUT); + +typedef struct tagTOUCHINPUT +{ + LONG x; + LONG y; + HANDLE hSource; + DWORD dwID; + DWORD dwFlags; + DWORD dwMask; + DWORD dwTime; + ULONG_PTR dwExtraInfo; + DWORD cxContact; + DWORD cyContext; +} TOUCHINPUT, *PTOUCHINPUT; + +#define TOUCH_COORD_TO_PIXEL(x) ((x) / 100) + +#define TOUCHEVENTF_MOVE 0x0001 +#define TOUCHEVENTF_DOWN 0x0002 +#define TOUCHEVENTF_UP 0x0004 + +#endif /*WINVER < 0x0601*/ + // winmm.dll function pointer typedefs typedef DWORD (WINAPI * PFN_timeGetTime)(void); #define timeGetTime _glfw.win32.winmm.GetTime @@ -209,8 +238,16 @@ typedef HRESULT (WINAPI * PFN_DirectInput8Create)(HINSTANCE,DWORD,REFIID,LPVOID* // user32.dll function pointer typedefs typedef BOOL (WINAPI * PFN_SetProcessDPIAware)(void); typedef BOOL (WINAPI * PFN_ChangeWindowMessageFilterEx)(HWND,UINT,DWORD,CHANGEFILTERSTRUCT*); +typedef BOOL(WINAPI * PFN_GetTouchInputInfo)(HTOUCHINPUT, UINT, PTOUCHINPUT, int); +typedef BOOL(WINAPI * PFN_CloseTouchInputHandle)(HTOUCHINPUT); +typedef BOOL(WINAPI * PFN_RegisterTouchWindow)(HWND, LONG); +typedef BOOL(WINAPI * PFN_UnregisterTouchWindow)(HWND); #define SetProcessDPIAware _glfw.win32.user32.SetProcessDPIAware_ #define ChangeWindowMessageFilterEx _glfw.win32.user32.ChangeWindowMessageFilterEx_ +#define GetTouchInputInfo _glfw.win32.user32.GetTouchInputInfo_ +#define CloseTouchInputHandle _glfw.win32.user32.CloseTouchInputHandle_ +#define RegisterTouchWindow _glfw.win32.user32.RegisterTouchWindow_ +#define UnregisterTouchWindow _glfw.win32.user32.UnregisterTouchWindow_ // dwmapi.dll function pointer typedefs typedef HRESULT (WINAPI * PFN_DwmIsCompositionEnabled)(BOOL*); @@ -326,8 +363,16 @@ typedef struct _GLFWlibraryWin32 HINSTANCE instance; PFN_SetProcessDPIAware SetProcessDPIAware_; PFN_ChangeWindowMessageFilterEx ChangeWindowMessageFilterEx_; + PFN_GetTouchInputInfo GetTouchInputInfo_; + PFN_CloseTouchInputHandle CloseTouchInputHandle_; + PFN_RegisterTouchWindow RegisterTouchWindow_; + PFN_UnregisterTouchWindow UnregisterTouchWindow_; } user32; + struct { + GLFWbool available; + } touch; + struct { HINSTANCE instance; PFN_DwmIsCompositionEnabled IsCompositionEnabled; diff --git a/src/win32_window.c b/src/win32_window.c index a689bdfe6..d08ed7059 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -759,6 +759,37 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, case WM_RBUTTONDOWN: case WM_MBUTTONDOWN: case WM_XBUTTONDOWN: + { + const int mods = getKeyMods(); + + if (window->touchInput) + { + // Skip emulated button events when touch input is enabled + if ((GetMessageExtraInfo() & 0xffffff00) == 0xff515700) + break; + } + + SetCapture(hWnd); + + if (uMsg == WM_LBUTTONDOWN) + _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, mods); + else if (uMsg == WM_RBUTTONDOWN) + _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, GLFW_PRESS, mods); + else if (uMsg == WM_MBUTTONDOWN) + _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_PRESS, mods); + else + { + if (HIWORD(wParam) == XBUTTON1) + _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_4, GLFW_PRESS, mods); + else if (HIWORD(wParam) == XBUTTON2) + _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_5, GLFW_PRESS, mods); + + return TRUE; + } + + return 0; + } + case WM_LBUTTONUP: case WM_RBUTTONUP: case WM_MBUTTONUP: @@ -776,6 +807,14 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, button = GLFW_MOUSE_BUTTON_4; else button = GLFW_MOUSE_BUTTON_5; + if (window->touchInput) + { + // Skip emulated button events when touch input is enabled + if ((GetMessageExtraInfo() & 0xffffff00) == 0xff515700) + break; + } + + ReleaseCapture(); if (uMsg == WM_LBUTTONDOWN || uMsg == WM_RBUTTONDOWN || uMsg == WM_MBUTTONDOWN || uMsg == WM_XBUTTONDOWN) @@ -838,7 +877,9 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, _glfwInputCursorEnter(window, GLFW_TRUE); } - return 0; + // NOTE: WM_MOUSEMOVE messages must be passed on to DefWindowProc + // for WM_TOUCH messages to be emitted + break; } case WM_INPUT: @@ -1038,6 +1079,75 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, return 0; } + case WM_TOUCH: + { + TOUCHINPUT* inputs; + UINT count = LOWORD(wParam); + + if (!_glfw.win32.touch.available) + return 0; + + inputs = (TOUCHINPUT*) malloc(sizeof(TOUCHINPUT) * count); + + if (GetTouchInputInfo((HTOUCHINPUT) lParam, + count, inputs, sizeof(TOUCHINPUT))) + { + UINT i; + int width, height, xpos, ypos; + + _glfwPlatformGetWindowSize(window, &width, &height); + _glfwPlatformGetWindowPos(window, &xpos, &ypos); + + //Create storage + GLFWtouch* touchPoints = calloc(count, sizeof(GLFWtouch)); + + //Count valid points + int validCount = 0; + + for (i = 0; i < count; i++) + { + int action; + POINT pos; + + pos.x = TOUCH_COORD_TO_PIXEL(inputs[i].x) - xpos; + pos.y = TOUCH_COORD_TO_PIXEL(inputs[i].y) - ypos; + + // Discard any points that lie outside of the client area + if (pos.x < 0 || pos.x >= width || + pos.y < 0 || pos.y >= height) + { + continue; + } + + if (inputs[i].dwFlags & TOUCHEVENTF_DOWN) + action = GLFW_PRESS; + else if (inputs[i].dwFlags & TOUCHEVENTF_UP) + action = GLFW_RELEASE; + else if (inputs[i].dwFlags & TOUCHEVENTF_MOVE) + action = GLFW_MOVE; + else + action = GLFW_REPEAT; + + touchPoints[validCount].id = (int)inputs[i].dwID; + touchPoints[validCount].action = action; + touchPoints[validCount].x = inputs[i].x / 100.0 - xpos; + touchPoints[validCount].y = inputs[i].y / 100.0 - ypos; + + validCount++; + } + + if(validCount > 0) + _glfwInputTouch(window, touchPoints, validCount); + + CloseTouchInputHandle((HTOUCHINPUT) lParam); + + free(touchPoints); + } + + free(inputs); + break; + } + case WM_PAINT: { _glfwInputWindowDamage(window); @@ -1799,6 +1909,17 @@ void _glfwPlatformPostEmptyEvent(void) PostMessage(_glfw.win32.helperWindowHandle, WM_NULL, 0, 0); } +void _glfwPlatformSetTouchInput(_GLFWwindow* window, int enabled) +{ + if (!_glfw.win32.touch.available) + return; + + if (enabled) + RegisterTouchWindow(window->win32.handle, 0); + else + UnregisterTouchWindow(window->win32.handle); +} + void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) { POINT pos; diff --git a/src/wl_window.c b/src/wl_window.c index c165ba73a..8212ca644 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -1194,6 +1194,10 @@ void _glfwPlatformPostEmptyEvent(void) wl_display_sync(_glfw.wl.display); } +void _glfwPlatformSetTouchInput(_GLFWwindow* window, int enabled) +{ +} + void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) { if (xpos) diff --git a/src/x11_window.c b/src/x11_window.c index 1c4e9c3c0..33f195cfc 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -2732,6 +2732,10 @@ void _glfwPlatformPostEmptyEvent(void) XFlush(_glfw.x11.display); } +void _glfwPlatformSetTouchInput(_GLFWwindow* window, int enabled) +{ +} + void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) { Window root, child; diff --git a/tests/events.c b/tests/events.c index 9a0144d79..f510d82d4 100644 --- a/tests/events.c +++ b/tests/events.c @@ -50,6 +50,7 @@ typedef struct GLFWwindow* window; int number; int closeable; + int touch; } Slot; static void usage(void) @@ -203,6 +204,8 @@ static const char* get_action_name(int action) return "released"; case GLFW_REPEAT: return "repeated"; + case GLFW_MOVE: + return "moved"; } return "caused unknown action"; @@ -402,6 +405,15 @@ static void key_callback(GLFWwindow* window, int key, int scancode, int action, switch (key) { + case GLFW_KEY_T: + { + slot->touch = !slot->touch; + glfwSetInputMode(window, GLFW_TOUCH, slot->touch); + + printf("(( touch %s ))\n", slot->touch ? "enabled" : "disabled"); + break; + } + case GLFW_KEY_C: { slot->closeable = !slot->closeable; @@ -502,6 +514,20 @@ static void joystick_callback(int jid, int event) } } +static void touch_callback(GLFWwindow* window, GLFWtouch* touchPoints, int count) +{ + printf("Priting info about all touch points"); + int i; + for (i = 0; i < count; ++i) { + printf("%08x at %0.3f: Touch %i %s at position %0.3f %0.3f\n", + counter++, + glfwGetTime(), + touchPoints[i].id, + get_action_name(touchPoints[i].action), + touchPoints[i].x, touchPoints[i].y); + } +} + int main(int argc, char** argv) { Slot* slots; @@ -573,6 +599,7 @@ int main(int argc, char** argv) char title[128]; slots[i].closeable = GLFW_TRUE; + slots[i].touch = GLFW_FALSE; slots[i].number = i + 1; snprintf(title, sizeof(title), "Event Linter (Window %i)", slots[i].number); @@ -618,6 +645,7 @@ int main(int argc, char** argv) glfwSetCharCallback(slots[i].window, char_callback); glfwSetCharModsCallback(slots[i].window, char_mods_callback); glfwSetDropCallback(slots[i].window, drop_callback); + glfwSetTouchCallback(slots[i].window, touch_callback); glfwMakeContextCurrent(slots[i].window); gladLoadGLLoader((GLADloadproc) glfwGetProcAddress);