diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 57d5c9c0d..f8447a0d6 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -1510,6 +1510,57 @@ typedef void (* GLFWmonitorfun)(GLFWmonitor*,int); */ typedef void (* GLFWjoystickfun)(int,int); +/*! @brief The function signature for pen tablet data callbacks. + * + * This is the function signature for pen tablet data callback functions. + * + * @param[in] x pen position relative to the screen. + * @param[in] y pen position relative to the screen. + * @param[in] z pen position relative to the tablet. + * @param[in] pen pressure from 0.0 to 1.0. + * @param[in] pen pitch in degree. + * @param[in] pen yaw in degree. + * @param[in] pen roll in degree. + * + * @sa @ref pen_tablet_data + * @sa @ref glfwSetPenTabletDataCallback + * + * @since Added in version 3.3. + * + * @ingroup input + */ +typedef void (* GLFWpentabletdatafun)(double,double,double,double,double,double,double); + +/*! @brief The function signature for pen tablet cursor callbacks. + * + * This is the function signature for pen tablet cursor callback functions. + * + * @param[in] pen cursor identifier. + * + * @sa @ref pen_tablet_cursor + * @sa @ref glfwSetPenTabletCursorCallback + * + * @since Added in version 3.3. + * + * @ingroup input + */ +typedef void (* GLFWpentabletcursorfun)(unsigned int); + +/*! @brief The function signature for pen tablet proximity callbacks. + * + * This is the function signature for pen tablet proximity callback functions. + * + * @param[in] pen proximity state. + * + * @sa @ref pen_tablet_proximity + * @sa @ref glfwSetPenTabletProximityCallback + * + * @since Added in version 3.3. + * + * @ingroup input + */ +typedef void (* GLFWpentabletproximityfun)(int); + /*! @brief Video mode type. * * This describes a single video mode. @@ -4963,6 +5014,72 @@ GLFWAPI const char* glfwGetGamepadName(int jid); */ GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state); +/*! @brief Sets the pen tablet data callback. + * + * This function sets the pen tablet data callback, or removes the + * currently set callback. This is called when the pen tablet data is updated. + * + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref pen_tablet_event + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI GLFWpentabletdatafun glfwSetPenTabletDataCallback(GLFWpentabletdatafun cbfun); + +/*! @brief Sets the pen tablet cursor callback. + * + * This function sets the pen tablet cursor callback, or removes the + * currently set callback. This is called when the pen tablet cursor has changed. + * + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref pen_tablet_event + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI GLFWpentabletcursorfun glfwSetPenTabletCursorCallback(GLFWpentabletcursorfun cbfun); + +/*! @brief Sets the pen tablet proximity callback. + * + * This function sets the pen tablet proximity callback, or removes the + * currently set callback. This is called when the pen tablet proximity has changed. + * + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref pen_tablet_event + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI GLFWpentabletproximityfun glfwSetPenTabletProximityCallback(GLFWpentabletproximityfun cbfun); + /*! @brief Sets the clipboard to the specified string. * * This function sets the system clipboard to the specified, UTF-8 encoded diff --git a/src/input.c b/src/input.c index b5e11542a..3b364aa79 100644 --- a/src/input.c +++ b/src/input.c @@ -401,6 +401,29 @@ void _glfwInputJoystickHat(_GLFWjoystick* js, int hat, char value) js->hats[hat] = value; } +// Notifies shared code of the new value of the pen tablet data +// +void _glfwInputPenTabletData(double x, double y, double z, double pressure, double pitch, double yaw, double roll) +{ + if (_glfw.callbacks.pentabletdata) + _glfw.callbacks.pentabletdata(x, y, z, pressure, pitch, yaw, roll); +} + +// Notifies shared code of the new value of the pen tablet cursor +// +void _glfwInputPenTabletCursor(unsigned int cursor) +{ + if (_glfw.callbacks.pentabletcursor) + _glfw.callbacks.pentabletcursor(cursor); +} + +// Notifies shared code of the new value of the pen tablet proximity +// +void _glfwInputPenTabletProximity(int proximity) +{ + if (_glfw.callbacks.pentabletproximity) + _glfw.callbacks.pentabletproximity(proximity); +} ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// @@ -1294,6 +1317,27 @@ GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state) return GLFW_TRUE; } +GLFWAPI GLFWpentabletdatafun glfwSetPenTabletDataCallback(GLFWpentabletdatafun cbfun) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(_glfw.callbacks.pentabletdata, cbfun); + return cbfun; +} + +GLFWAPI GLFWpentabletcursorfun glfwSetPenTabletCursorCallback(GLFWpentabletcursorfun cbfun) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(_glfw.callbacks.pentabletcursor, cbfun); + return cbfun; +} + +GLFWAPI GLFWpentabletproximityfun glfwSetPenTabletProximityCallback(GLFWpentabletproximityfun cbfun) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(_glfw.callbacks.pentabletproximity, cbfun); + return cbfun; +} + GLFWAPI void glfwSetClipboardString(GLFWwindow* handle, const char* string) { assert(string != NULL); diff --git a/src/internal.h b/src/internal.h index abba5baf1..b6ab4e609 100644 --- a/src/internal.h +++ b/src/internal.h @@ -567,6 +567,9 @@ struct _GLFWlibrary struct { GLFWmonitorfun monitor; GLFWjoystickfun joystick; + GLFWpentabletdatafun pentabletdata; + GLFWpentabletcursorfun pentabletcursor; + GLFWpentabletproximityfun pentabletproximity; } callbacks; // This is defined in the window API's platform.h @@ -728,6 +731,10 @@ void _glfwInputJoystickHat(_GLFWjoystick* js, int hat, char value); void _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement); void _glfwInputMonitorWindow(_GLFWmonitor* monitor, _GLFWwindow* window); +void _glfwInputPenTabletData(double x, double y, double z, double pressure, double pitch, double yaw, double roll); +void _glfwInputPenTabletCursor(unsigned int cursor); +void _glfwInputPenTabletProximity(int proximity); + #if defined(__GNUC__) void _glfwInputError(int code, const char* format, ...) __attribute__((format(printf, 2, 3))); diff --git a/src/win32_init.c b/src/win32_init.c index 3ee5eb853..0fa995be1 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -159,6 +159,21 @@ static GLFWbool loadLibraries(void) GetProcAddress(_glfw.win32.ntdll.instance, "RtlVerifyVersionInfo"); } + _glfw.win32.wintab32.instance = LoadLibraryA("Wintab32.dll"); + if (_glfw.win32.wintab32.instance) + { + _glfw.win32.wintab32.WTInfoA = (PFN_WTInfoA) + GetProcAddress(_glfw.win32.wintab32.instance, "WTInfoA"); + _glfw.win32.wintab32.WTOpenA = (PFN_WTOpenA) + GetProcAddress(_glfw.win32.wintab32.instance, "WTOpenA"); + _glfw.win32.wintab32.WTQueueSizeSet = (PFN_WTQueueSizeSet) + GetProcAddress(_glfw.win32.wintab32.instance, "WTQueueSizeSet"); + _glfw.win32.wintab32.WTClose = (PFN_WTClose) + GetProcAddress(_glfw.win32.wintab32.instance, "WTClose"); + _glfw.win32.wintab32.WTPacket = (PFN_WTPacket) + GetProcAddress(_glfw.win32.wintab32.instance, "WTPacket"); + } + return GLFW_TRUE; } @@ -186,6 +201,9 @@ static void freeLibraries(void) if (_glfw.win32.ntdll.instance) FreeLibrary(_glfw.win32.ntdll.instance); + + if (_glfw.win32.wintab32.instance) + FreeLibrary(_glfw.win32.wintab32.instance); } // Create key code translation tables @@ -327,6 +345,46 @@ static void createKeyTables(void) } } +// Init wintab context see https://developer-docs.wacom.com/display/DevDocs/Windows+Wintab+Documentation +// +static void initWintabContext(HWND hwnd) +{ + if (_glfw.win32.wintab32.instance) { + LOGCONTEXTA context = {0}; + + _glfw.win32.wintab32.WTInfoA(4, 0, &context); + context.lcPktData = 0x0080 | 0x0100 | 0x0200 | 0x0040 | 0x0400 | 0x1000 | 0x0008 | 0x0020; // X Y Z BUTTONS NPRESSURE ORIENTATION CHANGED CURSOR + context.lcPktMode = 0; + context.lcMoveMask = context.lcPktData; + context.lcBtnUpMask = context.lcBtnDnMask; + context.lcOptions |= 0x0004; // CXO MESSAGES + context.lcOutOrgX = context.lcInOrgX; + context.lcOutOrgY = context.lcInOrgY; + context.lcOutExtX = context.lcInExtX; + context.lcOutExtY = -context.lcInExtY; + + // open wintab context + _glfw.win32.wintab32.context = _glfw.win32.wintab32.WTOpenA(hwnd, &context, TRUE); + if (_glfw.win32.wintab32.context) { + _glfw.win32.wintab32.WTQueueSizeSet(_glfw.win32.wintab32.context, 256); + _glfw.win32.wintab32.WTInfoA(4, 0, &_glfw.win32.wintab32.contextInfo); + _glfw.win32.wintab32.WTInfoA(100, 15, &_glfw.win32.wintab32.pressureInfo); + _glfw.win32.wintab32.WTInfoA(100, 17, &_glfw.win32.wintab32.orientationInfo); + } + } + else { + _glfw.win32.wintab32.context = 0; + } +} + +// Terminate wintab context +// +static void terminateWintabContext(void) +{ + if (_glfw.win32.wintab32.instance && _glfw.win32.wintab32.context) + _glfw.win32.wintab32.WTClose(_glfw.win32.wintab32.context); +} + // Creates a dummy window for behind-the-scenes work // static HWND createHelperWindow(void) @@ -577,6 +635,7 @@ int _glfwPlatformInit(void) _glfwInitTimerWin32(); _glfwInitJoysticksWin32(); + initWintabContext(_glfw.win32.helperWindowHandle); _glfwPollMonitorsWin32(); return GLFW_TRUE; @@ -604,6 +663,7 @@ void _glfwPlatformTerminate(void) _glfwTerminateEGL(); _glfwTerminateJoysticksWin32(); + terminateWintabContext(); freeLibraries(); } diff --git a/src/win32_platform.h b/src/win32_platform.h index 9a867c357..e925e9965 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -258,6 +258,78 @@ typedef HRESULT (WINAPI * PFN_GetDpiForMonitor)(HMONITOR,MONITOR_DPI_TYPE,UINT*, typedef LONG (WINAPI * PFN_RtlVerifyVersionInfo)(OSVERSIONINFOEXW*,ULONG,ULONGLONG); #define RtlVerifyVersionInfo _glfw.win32.ntdll.RtlVerifyVersionInfo_ +// wintab.dll function pointer typedefs +#define WT_PACKET 0x7FF0 +#define WT_PROXIMITY 0x7FF5 +DECLARE_HANDLE(HCTX); + +typedef struct tagAXIS { + LONG min; + LONG max; + UINT units; + DWORD resolution; +} AXIS; + +typedef struct tagORIENTATION { + int azimuth; + int altitude; + int twist; +} ORIENTATION; + +typedef struct tagPACKET { + DWORD changed; + UINT cursor; + DWORD buttons; + DWORD x; + DWORD y; + DWORD z; + UINT normalPressure; + ORIENTATION orientation; +} PACKET; + +typedef struct LOGCONTEXTA { + char lcName[40]; + UINT lcOptions; + UINT lcStatus; + UINT lcLocks; + UINT lcMsgBase; + UINT lcDevice; + UINT lcPktRate; + DWORD lcPktData; + DWORD lcPktMode; + DWORD lcMoveMask; + DWORD lcBtnDnMask; + DWORD lcBtnUpMask; + LONG lcInOrgX; + LONG lcInOrgY; + LONG lcInOrgZ; + LONG lcInExtX; + LONG lcInExtY; + LONG lcInExtZ; + LONG lcOutOrgX; + LONG lcOutOrgY; + LONG lcOutOrgZ; + LONG lcOutExtX; + LONG lcOutExtY; + LONG lcOutExtZ; + DWORD lcSensX; + DWORD lcSensY; + DWORD lcSensZ; + BOOL lcSysMode; + int lcSysOrgX; + int lcSysOrgY; + int lcSysExtX; + int lcSysExtY; + DWORD lcSysSensX; + DWORD lcSysSensY; +} LOGCONTEXTA, *PLOGCONTEXTA, NEAR *NPLOGCONTEXTA, FAR *LPLOGCONTEXTA; + +typedef UINT (WINAPI * PFN_WTInfoA)(UINT,UINT,LPVOID); +typedef HCTX (WINAPI * PFN_WTOpenA)(HWND,LPLOGCONTEXTA,BOOL); +typedef BOOL (WINAPI * PFN_WTQueueSizeSet)(HCTX,int); +typedef BOOL (WINAPI * PFN_WTClose)(HCTX); +typedef BOOL (WINAPI * PFN_WTPacket)(HCTX,UINT,LPVOID); + typedef VkFlags VkWin32SurfaceCreateFlagsKHR; typedef struct VkWin32SurfaceCreateInfoKHR @@ -383,6 +455,19 @@ typedef struct _GLFWlibraryWin32 PFN_RtlVerifyVersionInfo RtlVerifyVersionInfo_; } ntdll; + struct { + HINSTANCE instance; + PFN_WTInfoA WTInfoA; + PFN_WTOpenA WTOpenA; + PFN_WTQueueSizeSet WTQueueSizeSet; + PFN_WTClose WTClose; + PFN_WTPacket WTPacket; + HCTX context; + LOGCONTEXTA contextInfo; + AXIS pressureInfo; + AXIS orientationInfo[3]; + } wintab32; + } _GLFWlibraryWin32; // Win32-specific per-monitor data diff --git a/src/win32_window.c b/src/win32_window.c index 728b6e07a..038418ee3 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -616,6 +616,48 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, break; } + + case WT_PROXIMITY: + { + _glfwInputPenTabletProximity(lParam != 0); + } + + case WT_PACKET: + if (_glfw.win32.wintab32.instance) + { + #define FIX2DOUBLE(x) ((double)(HIWORD(x))+((double)LOWORD(x)/65536)) + PACKET packet; + LOGCONTEXTA contextInfo = _glfw.win32.wintab32.contextInfo; + AXIS pressureInfo = _glfw.win32.wintab32.pressureInfo; + AXIS altInfo = _glfw.win32.wintab32.orientationInfo[1]; + AXIS aziInfo = _glfw.win32.wintab32.orientationInfo[0]; + AXIS rollInfo = _glfw.win32.wintab32.orientationInfo[2]; + + while (_glfw.win32.wintab32.WTPacket(_glfw.win32.wintab32.context, (UINT)wParam, &packet)) { + + double x, y, z, pressure, pitch=0, yaw=0, roll=0; + + x = ((double)(packet.x - contextInfo.lcInOrgX) / contextInfo.lcInExtX) * contextInfo.lcSysExtX + contextInfo.lcSysOrgX; + y = ((double)(packet.y - contextInfo.lcInOrgY) / contextInfo.lcInExtY) * contextInfo.lcSysExtY + contextInfo.lcSysOrgY; + z = ((double)(packet.z - contextInfo.lcOutOrgZ) / contextInfo.lcOutExtZ); + pressure = (double)(packet.normalPressure - pressureInfo.min) / (pressureInfo.max - pressureInfo.min); + if (aziInfo.resolution && altInfo.resolution) { + double alt = (double)(packet.orientation.altitude - altInfo.min) / (altInfo.max - altInfo.min); + double azi = (double)(packet.orientation.azimuth - aziInfo.min) / (aziInfo.max - aziInfo.min); + pitch = (0.5 - alt) * 180.0; + yaw = azi * 360.0; + } + if (rollInfo.resolution) { // roll seems to be mostly unsupported so this is untested + roll = (double)(packet.orientation.twist - rollInfo.min) / (rollInfo.max - rollInfo.min) * 360.0; + } + + if (packet.changed & 0x0020) { // CURSOR changed + _glfwInputPenTabletCursor(packet.cursor); + } + + _glfwInputPenTabletData(x, y, z, pressure, pitch, yaw, roll); + } + } } return DefWindowProcW(hWnd, uMsg, wParam, lParam); diff --git a/tests/events.c b/tests/events.c index 5a0fde04a..9bcb5f666 100644 --- a/tests/events.c +++ b/tests/events.c @@ -42,6 +42,10 @@ #include "getopt.h" +#if defined(_MSC_VER) && _MSC_VER < 1900 +#define snprintf _snprintf +#endif + // Event index static unsigned int counter = 0; @@ -493,6 +497,24 @@ static void joystick_callback(int jid, int event) } } +void penTabletData_callback(double x, double y, double z, double pressure, double pitch, double yaw, double roll) +{ + printf("%08x at %0.3f: pen: %f %f %f %f %f %f %f\n", + counter++, glfwGetTime(), x, y, z, pressure, pitch, yaw, roll); +} + +void penTabletCursor_callback(unsigned int cursor) +{ + printf("%08x at %0.3f: pen cursor: %d\n", + counter++, glfwGetTime(), cursor); +} + +void penTabletProximity_callback(int proximity) +{ + printf("%08x at %0.3f: pen proximity: %d\n", + counter++, glfwGetTime(), proximity); +} + int main(int argc, char** argv) { Slot* slots; @@ -510,6 +532,9 @@ int main(int argc, char** argv) glfwSetMonitorCallback(monitor_callback); glfwSetJoystickCallback(joystick_callback); + glfwSetPenTabletDataCallback(penTabletData_callback); + glfwSetPenTabletCursorCallback(penTabletCursor_callback); + glfwSetPenTabletProximityCallback(penTabletProximity_callback); while ((ch = getopt(argc, argv, "hfn:")) != -1) {