diff --git a/README.md b/README.md index 23e12adea..fa5aea0d5 100644 --- a/README.md +++ b/README.md @@ -241,6 +241,7 @@ skills. - Christoph Kubisch - 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 a856c3c5a..d3cb04a42 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -272,18 +272,18 @@ extern "C" { #define GLFW_FALSE 0 /*! @} */ -/*! @name Key and button actions +/*! @name Key, button and touch actions * @{ */ -/*! @brief The key or mouse button was released. +/*! @brief The key or button was released or the touch ended. * - * The key or mouse button was released. + * The key or button was released or the touch ended. * * @ingroup input */ #define GLFW_RELEASE 0 -/*! @brief The key or mouse button was pressed. +/*! @brief The key or button was pressed or the touch started. * - * The key or mouse button was pressed. + * The key or button was pressed or the touch started. * * @ingroup input */ @@ -295,6 +295,10 @@ extern "C" { * @ingroup input */ #define GLFW_REPEAT 2 +/*! @brief The touch was moved. + * @ingroup input + */ +#define GLFW_MOVE 3 /*! @} */ /*! @defgroup keys Keyboard keys @@ -870,6 +874,7 @@ extern "C" { #define GLFW_CURSOR 0x00033001 #define GLFW_STICKY_KEYS 0x00033002 #define GLFW_STICKY_MOUSE_BUTTONS 0x00033003 +#define GLFW_TOUCH 0x00030004 #define GLFW_CURSOR_NORMAL 0x00034001 #define GLFW_CURSOR_HIDDEN 0x00034002 @@ -1302,6 +1307,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. @@ -3929,6 +3970,21 @@ GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun cb */ GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* window, GLFWdropfun 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 Returns whether the specified joystick is present. * * This function returns whether the specified joystick is present. diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 0f1f245ae..c635842e7 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1490,6 +1490,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 923e3fca4..904fdb1cd 100644 --- a/src/input.c +++ b/src/input.c @@ -35,6 +35,17 @@ // Internal key state used for sticky keys #define _GLFW_STICK 3 +// 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; +} ////////////////////////////////////////////////////////////////////////// ////// GLFW event API ////// @@ -124,6 +135,12 @@ void _glfwInputDrop(_GLFWwindow* window, int count, const char** paths) window->callbacks.drop((GLFWwindow*) window, count, paths); } +void _glfwInputTouch(_GLFWwindow* window, GLFWtouch* touchPoints, int count) +{ + if (window->callbacks.touch) + window->callbacks.touch((GLFWwindow*)window, touchPoints, count); +} + void _glfwInputJoystick(int jid, int event) { if (_glfw.callbacks.joystick) @@ -205,6 +222,8 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* handle, int mode) return window->stickyKeys; case GLFW_STICKY_MOUSE_BUTTONS: return window->stickyMouseButtons; + case GLFW_TOUCH: + return window->touchInput; default: _glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode %i", mode); return 0; @@ -689,6 +708,15 @@ GLFWAPI const char* glfwGetJoystickName(int jid) return _glfw.joysticks[jid].name; } +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 GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun) { _GLFW_REQUIRE_INIT_OR_RETURN(NULL); diff --git a/src/internal.h b/src/internal.h index 5ae0e7772..da12d4130 100644 --- a/src/internal.h +++ b/src/internal.h @@ -388,6 +388,8 @@ struct _GLFWwindow GLFWbool stickyKeys; GLFWbool stickyMouseButtons; + GLFWbool touchInput; + double cursorPosX, cursorPosY; int cursorMode; char mouseButtons[GLFW_MOUSE_BUTTON_LAST + 1]; char keys[GLFW_KEY_LAST + 1]; @@ -413,6 +415,7 @@ struct _GLFWwindow GLFWcharfun character; GLFWcharmodsfun charmods; GLFWdropfun drop; + GLFWtouchfun touch; } callbacks; // This is defined in the window API's platform.h @@ -567,6 +570,14 @@ void _glfwPlatformTerminate(void); */ const char* _glfwPlatformGetVersionString(void); +/*! @brief Sets whether touch input is enabled for the specified window. + * @param[in] window The window whose touch input status to change. + * @param[in] enabled @c GL_TRUE to enable touch input, or @c GL_FALSE to + * disable it. + * @ingroup platform + */ +void _glfwPlatformSetTouchInput(_GLFWwindow* window, int enabled); + /*! @copydoc glfwGetCursorPos * @ingroup platform */ @@ -962,6 +973,14 @@ void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos); */ void _glfwInputCursorEnter(_GLFWwindow* window, GLFWbool entered); +/*! @brief Notifies shared code of a touch start/end event. +* @param[in] window The window that received the event. +* @param[in] touchPoints All valid touch points +* @param[in] count The numer of valid touch points +* @ingroup event +*/ +void _glfwInputTouch(_GLFWwindow* window, GLFWtouch* touchPoints, int count); + /*! @brief Notifies shared code of a monitor connection or disconnection. * @param[in] monitor The monitor that was connected or disconnected. * @param[in] action One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. diff --git a/src/win32_init.c b/src/win32_init.c index c9978c24f..bf3680504 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -85,6 +85,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 8b2af43f5..e2bc49482 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -160,6 +160,34 @@ typedef enum PROCESS_DPI_AWARENESS #define DIDFT_OPTIONAL 0x80000000 #endif +#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 @@ -177,8 +205,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,PCHANGEFILTERSTRUCT); +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 _glfw_SetProcessDPIAware _glfw.win32.user32.SetProcessDPIAware #define _glfw_ChangeWindowMessageFilterEx _glfw.win32.user32.ChangeWindowMessageFilterEx +#define _glfw_GetTouchInputInfo _glfw.win32.user32.GetTouchInputInfo +#define _glfw_CloseTouchInputHandle _glfw.win32.user32.CloseTouchInputHandle +#define _glfw_RegisterTouchWindow _glfw.win32.user32.RegisterTouchWindow +#define _glfw_UnregisterTouchWindow _glfw.win32.user32.UnregisterTouchWindow // dwmapi.dll function pointer typedefs typedef HRESULT (WINAPI * PFN_DwmIsCompositionEnabled)(BOOL*); @@ -279,8 +315,17 @@ 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; + + // dwmapi.dll struct { HINSTANCE instance; PFN_DwmIsCompositionEnabled DwmIsCompositionEnabled; diff --git a/src/win32_window.c b/src/win32_window.c index 514a52069..661e79a83 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -609,6 +609,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: @@ -626,6 +657,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) @@ -683,7 +722,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_MOUSELEAVE: @@ -810,6 +851,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 (_glfw_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); + + _glfw_CloseTouchInputHandle((HTOUCHINPUT) lParam); + + free(touchPoints); + } + + free(inputs); + break; + } + case WM_PAINT: { _glfwInputWindowDamage(window); @@ -1482,6 +1592,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) + _glfw_RegisterTouchWindow(window->win32.handle, 0); + else + _glfw_UnregisterTouchWindow(window->win32.handle); +} + void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) { POINT pos; diff --git a/src/x11_window.c b/src/x11_window.c index 3eff19d3f..f671ed9dc 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -2207,6 +2207,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 73a61ab21..50c42a6f1 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"; @@ -393,6 +396,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; @@ -482,6 +494,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; @@ -553,6 +579,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); @@ -597,6 +624,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);