This commit is contained in:
Anaël Seghezzi 2019-03-09 13:29:14 +00:00 committed by GitHub
commit d7ee3f6caa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 723 additions and 20 deletions

View File

@ -1510,6 +1510,59 @@ typedef void (* GLFWmonitorfun)(GLFWmonitor*,int);
*/ */
typedef void (* GLFWjoystickfun)(int,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 radian.
* @param[in] pen yaw in radian.
* @param[in] pen roll in radian.
*
* @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.
* It is called when the tablet cursor is changed, from stylus to eraser for example.
*
* @param[in] pen cursor identifier. 1 is usually a stylus, 2 and 3 an eraser.
*
* @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.
* It is called when a tablet device (pen etc) entering or is exiting tablet proximity.
*
* @param[in] pen proximity state. 1 = entering, 0 = exiting.
*
* @sa @ref pen_tablet_proximity
* @sa @ref glfwSetPenTabletProximityCallback
*
* @since Added in version 3.3.
*
* @ingroup input
*/
typedef void (* GLFWpentabletproximityfun)(int);
/*! @brief Video mode type. /*! @brief Video mode type.
* *
* This describes a single video mode. * This describes a single video mode.
@ -4994,6 +5047,72 @@ GLFWAPI const char* glfwGetGamepadName(int jid);
*/ */
GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state); 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. /*! @brief Sets the clipboard to the specified string.
* *
* This function sets the system clipboard to the specified, UTF-8 encoded * This function sets the system clipboard to the specified, UTF-8 encoded

View File

@ -26,6 +26,7 @@
#include "internal.h" #include "internal.h"
#include <math.h>
#include <float.h> #include <float.h>
#include <string.h> #include <string.h>
@ -44,6 +45,8 @@
#define NSEventMaskAny NSAnyEventMask #define NSEventMaskAny NSAnyEventMask
#define NSEventTypeApplicationDefined NSApplicationDefined #define NSEventTypeApplicationDefined NSApplicationDefined
#define NSBitmapFormatAlphaNonpremultiplied NSAlphaNonpremultipliedBitmapFormat #define NSBitmapFormatAlphaNonpremultiplied NSAlphaNonpremultipliedBitmapFormat
#define NSEventSubtypeTabletPoint NSTabletPointEventSubtype
#define NSEventSubtypeTabletProximity NSTabletProximityEventSubtype
#endif #endif
// Returns the style mask corresponding to the window settings // Returns the style mask corresponding to the window settings
@ -437,6 +440,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
- (void)mouseDown:(NSEvent *)event - (void)mouseDown:(NSEvent *)event
{ {
[self handlePenTablet:event];
_glfwInputMouseClick(window, _glfwInputMouseClick(window,
GLFW_MOUSE_BUTTON_LEFT, GLFW_MOUSE_BUTTON_LEFT,
GLFW_PRESS, GLFW_PRESS,
@ -450,14 +454,69 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
- (void)mouseUp:(NSEvent *)event - (void)mouseUp:(NSEvent *)event
{ {
[self handlePenTablet:event];
_glfwInputMouseClick(window, _glfwInputMouseClick(window,
GLFW_MOUSE_BUTTON_LEFT, GLFW_MOUSE_BUTTON_LEFT,
GLFW_RELEASE, GLFW_RELEASE,
translateFlags([event modifierFlags])); translateFlags([event modifierFlags]));
} }
- (void)handlePenTablet:(NSEvent *)event
{
if ([window->ns.object isKeyWindow])
{
if ([event subtype] == NSEventSubtypeTabletPoint)
{
const double pressure = [event pressure];
const NSPoint tilt = [event tilt];
const NSPoint pos = [NSEvent mouseLocation];
const double posz = [event absoluteZ];
double tx = tilt.x * 1.5707963267949;
double ty = tilt.y * 1.5707963267949;
double sinx = sin(tx);
double siny = sin(ty);
double cosx = cos(tx);
double cosy = cos(ty);
/*double matrix[9] = { // full matrix for reference
0.0, -cosy, siny,
cosx, -sinx*siny, -sinx*cosy,
sinx, cosx*siny, cosx*cosy
};*/
double v[3] = {sinx, cosx*siny, cosx*cosy};
double yaw = atan2(v[0], v[1]);
double pitch = 3.141592653589793 - acos(v[2]);
if (yaw < 0.0) yaw += 6.28318530717959;
_glfwInputPenTabletData(
pos.x,
CGDisplayBounds(CGMainDisplayID()).size.height - pos.y,
posz / 1024.0,
pressure,
pitch,
yaw,
0.0);
}
if ([event subtype] == NSEventSubtypeTabletProximity)
{
static unsigned int s_cursor = 0;
unsigned int cursor = [event pointingDeviceType];
_glfwInputPenTabletProximity([event isEnteringProximity]);
if (cursor != s_cursor)
{
_glfwInputPenTabletCursor(cursor);
s_cursor = cursor;
}
}
}
}
- (void)mouseMoved:(NSEvent *)event - (void)mouseMoved:(NSEvent *)event
{ {
[self handlePenTablet:event];
if (window->cursorMode == GLFW_CURSOR_DISABLED) if (window->cursorMode == GLFW_CURSOR_DISABLED)
{ {
const double dx = [event deltaX] - window->ns.cursorWarpDeltaX; const double dx = [event deltaX] - window->ns.cursorWarpDeltaX;
@ -481,6 +540,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
- (void)rightMouseDown:(NSEvent *)event - (void)rightMouseDown:(NSEvent *)event
{ {
[self handlePenTablet:event];
_glfwInputMouseClick(window, _glfwInputMouseClick(window,
GLFW_MOUSE_BUTTON_RIGHT, GLFW_MOUSE_BUTTON_RIGHT,
GLFW_PRESS, GLFW_PRESS,
@ -494,6 +554,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
- (void)rightMouseUp:(NSEvent *)event - (void)rightMouseUp:(NSEvent *)event
{ {
[self handlePenTablet:event];
_glfwInputMouseClick(window, _glfwInputMouseClick(window,
GLFW_MOUSE_BUTTON_RIGHT, GLFW_MOUSE_BUTTON_RIGHT,
GLFW_RELEASE, GLFW_RELEASE,
@ -502,6 +563,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
- (void)otherMouseDown:(NSEvent *)event - (void)otherMouseDown:(NSEvent *)event
{ {
[self handlePenTablet:event];
_glfwInputMouseClick(window, _glfwInputMouseClick(window,
(int) [event buttonNumber], (int) [event buttonNumber],
GLFW_PRESS, GLFW_PRESS,
@ -515,6 +577,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
- (void)otherMouseUp:(NSEvent *)event - (void)otherMouseUp:(NSEvent *)event
{ {
[self handlePenTablet:event];
_glfwInputMouseClick(window, _glfwInputMouseClick(window,
(int) [event buttonNumber], (int) [event buttonNumber],
GLFW_RELEASE, GLFW_RELEASE,

View File

@ -401,6 +401,29 @@ void _glfwInputJoystickHat(_GLFWjoystick* js, int hat, char value)
js->hats[hat] = 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 ////// ////// GLFW internal API //////
@ -1294,6 +1317,27 @@ GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state)
return GLFW_TRUE; 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) GLFWAPI void glfwSetClipboardString(GLFWwindow* handle, const char* string)
{ {
assert(string != NULL); assert(string != NULL);

View File

@ -567,6 +567,9 @@ struct _GLFWlibrary
struct { struct {
GLFWmonitorfun monitor; GLFWmonitorfun monitor;
GLFWjoystickfun joystick; GLFWjoystickfun joystick;
GLFWpentabletdatafun pentabletdata;
GLFWpentabletcursorfun pentabletcursor;
GLFWpentabletproximityfun pentabletproximity;
} callbacks; } callbacks;
// This is defined in the window API's platform.h // This is defined in the window API's platform.h
@ -729,6 +732,10 @@ void _glfwInputJoystickHat(_GLFWjoystick* js, int hat, char value);
void _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement); void _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement);
void _glfwInputMonitorWindow(_GLFWmonitor* monitor, _GLFWwindow* window); 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__) #if defined(__GNUC__)
void _glfwInputError(int code, const char* format, ...) void _glfwInputError(int code, const char* format, ...)
__attribute__((format(printf, 2, 3))); __attribute__((format(printf, 2, 3)));

View File

@ -159,6 +159,21 @@ static GLFWbool loadLibraries(void)
GetProcAddress(_glfw.win32.ntdll.instance, "RtlVerifyVersionInfo"); 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; return GLFW_TRUE;
} }
@ -186,6 +201,9 @@ static void freeLibraries(void)
if (_glfw.win32.ntdll.instance) if (_glfw.win32.ntdll.instance)
FreeLibrary(_glfw.win32.ntdll.instance); FreeLibrary(_glfw.win32.ntdll.instance);
if (_glfw.win32.wintab32.instance)
FreeLibrary(_glfw.win32.wintab32.instance);
} }
// Create key code translation tables // 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 // Creates a dummy window for behind-the-scenes work
// //
static HWND createHelperWindow(void) static HWND createHelperWindow(void)
@ -577,6 +635,7 @@ int _glfwPlatformInit(void)
_glfwInitTimerWin32(); _glfwInitTimerWin32();
_glfwInitJoysticksWin32(); _glfwInitJoysticksWin32();
initWintabContext(_glfw.win32.helperWindowHandle);
_glfwPollMonitorsWin32(); _glfwPollMonitorsWin32();
return GLFW_TRUE; return GLFW_TRUE;
@ -604,6 +663,7 @@ void _glfwPlatformTerminate(void)
_glfwTerminateEGL(); _glfwTerminateEGL();
_glfwTerminateJoysticksWin32(); _glfwTerminateJoysticksWin32();
terminateWintabContext();
freeLibraries(); freeLibraries();
} }

View File

@ -258,6 +258,78 @@ typedef HRESULT (WINAPI * PFN_GetDpiForMonitor)(HMONITOR,MONITOR_DPI_TYPE,UINT*,
typedef LONG (WINAPI * PFN_RtlVerifyVersionInfo)(OSVERSIONINFOEXW*,ULONG,ULONGLONG); typedef LONG (WINAPI * PFN_RtlVerifyVersionInfo)(OSVERSIONINFOEXW*,ULONG,ULONGLONG);
#define RtlVerifyVersionInfo _glfw.win32.ntdll.RtlVerifyVersionInfo_ #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 VkFlags VkWin32SurfaceCreateFlagsKHR;
typedef struct VkWin32SurfaceCreateInfoKHR typedef struct VkWin32SurfaceCreateInfoKHR
@ -383,6 +455,19 @@ typedef struct _GLFWlibraryWin32
PFN_RtlVerifyVersionInfo RtlVerifyVersionInfo_; PFN_RtlVerifyVersionInfo RtlVerifyVersionInfo_;
} ntdll; } 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; } _GLFWlibraryWin32;
// Win32-specific per-monitor data // Win32-specific per-monitor data

View File

@ -616,6 +616,48 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
break; 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 = packet.z / 1024.0;
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 = alt * 3.14159265359;
yaw = azi * 6.28318530718;
}
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); return DefWindowProcW(hWnd, uMsg, wParam, lParam);

View File

@ -494,6 +494,10 @@ static GLFWbool initExtensions(void)
_glfw_dlsym(_glfw.x11.xi.handle, "XIQueryVersion"); _glfw_dlsym(_glfw.x11.xi.handle, "XIQueryVersion");
_glfw.x11.xi.SelectEvents = (PFN_XISelectEvents) _glfw.x11.xi.SelectEvents = (PFN_XISelectEvents)
_glfw_dlsym(_glfw.x11.xi.handle, "XISelectEvents"); _glfw_dlsym(_glfw.x11.xi.handle, "XISelectEvents");
_glfw.x11.xi.QueryDevice = (PFN_XIQueryDevice)
_glfw_dlsym(_glfw.x11.xi.handle, "XIQueryDevice");
_glfw.x11.xi.GetProperty = (PFN_XIGetProperty)
_glfw_dlsym(_glfw.x11.xi.handle, "XIGetProperty");
if (XQueryExtension(_glfw.x11.display, if (XQueryExtension(_glfw.x11.display,
"XInputExtension", "XInputExtension",
@ -849,11 +853,119 @@ static int errorHandler(Display *display, XErrorEvent* event)
return 0; return 0;
} }
// Coordinate Transformation Matrix
//
static float * getDeviceCoordinateTransformationMatrix(Display *display, int deviceid)
{
float *data = NULL;
Atom type;
int format;
unsigned long num_items, bytes_after;
if (XIGetProperty(
display, deviceid,
XInternAtom(display, "Coordinate Transformation Matrix", False),
0, 9, False,
XInternAtom(display, "FLOAT", False),
&type, &format, &num_items, &bytes_after,
(unsigned char **)&data
) != Success)
return NULL;
return data;
}
// XInput2 init pen tablet
//
static void initPenTablet(Display *display)
{
_glfw.x11.xi.tablet_cursor = 0;
_glfw.x11.xi.stylus_deviceid = 0;
_glfw.x11.xi.eraser_deviceid = 0;
_glfw.x11.xi.stylus_CTMatrix = NULL;
_glfw.x11.xi.eraser_CTMatrix = NULL;
if (_glfw.x11.xi.available)
{
int i, n;
XIDeviceInfo *dev_info = XIQueryDevice(display, XIAllDevices, &n);
for (i = 0; i < n; i++)
{
XIDeviceInfo *dev = &dev_info[i];
if (strstr(dev->name, "stylus") || strstr(dev->name, "eraser"))
{
XIEventMask mask;
mask.deviceid = dev->deviceid;
mask.mask_len = XIMaskLen(XI_RawMotion);
mask.mask = calloc(mask.mask_len, sizeof(char));
XISetMask(mask.mask, XI_PropertyEvent); // property event to catch proximity change
XISetMask(mask.mask, XI_RawMotion); // raw motion to catch x, y, pressure and tilt
XISelectEvents(display, DefaultRootWindow(display), &mask, 1);
if (strstr(dev->name, "stylus"))
{
int c;
_glfw.x11.xi.stylus_deviceid = dev->deviceid;
for (c = 0; c < dev->num_classes; c++)
{
if (dev->classes[c]->type == XIValuatorClass)
{
XIValuatorClassInfo *v = (XIValuatorClassInfo*)dev->classes[c];
if (v->number < 32) _glfw.x11.xi.stylus_ClassInfo[v->number] = *v;
}
}
_glfw.x11.xi.stylus_CTMatrix = getDeviceCoordinateTransformationMatrix(display, dev->deviceid);
}
else
{
_glfw.x11.xi.eraser_deviceid = dev->deviceid;
_glfw.x11.xi.eraser_CTMatrix = getDeviceCoordinateTransformationMatrix(display, dev->deviceid);
}
free(mask.mask);
}
}
}
}
// XInput2 terminate pen tablet
//
static void terminatePenTablet(void)
{
if (_glfw.x11.xi.stylus_CTMatrix)
{
XFree(_glfw.x11.xi.stylus_CTMatrix);
_glfw.x11.xi.stylus_CTMatrix = NULL;
}
if (_glfw.x11.xi.eraser_CTMatrix)
{
XFree(_glfw.x11.xi.eraser_CTMatrix);
_glfw.x11.xi.eraser_CTMatrix = NULL;
}
}
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
////// GLFW internal API ////// ////// GLFW internal API //////
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// Apply Coordinate Transformation Matrix to a 2d vector
//
void _glfwApplyCoordTransformMatrix(float *matrix, float *px, float *py)
{
float x = *px;
float y = *py;
*px = x * matrix[0] + y * matrix[1] + matrix[2];
*py = x * matrix[3] + y * matrix[4] + matrix[5];
}
// Sets the X error handler callback // Sets the X error handler callback
// //
void _glfwGrabErrorHandlerX11(void) void _glfwGrabErrorHandlerX11(void)
@ -991,11 +1103,15 @@ int _glfwPlatformInit(void)
_glfwInitTimerPOSIX(); _glfwInitTimerPOSIX();
_glfwPollMonitorsX11(); _glfwPollMonitorsX11();
initPenTablet(_glfw.x11.display);
return GLFW_TRUE; return GLFW_TRUE;
} }
void _glfwPlatformTerminate(void) void _glfwPlatformTerminate(void)
{ {
terminatePenTablet();
if (_glfw.x11.helperWindowHandle) if (_glfw.x11.helperWindowHandle)
{ {
if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) == if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) ==

View File

@ -113,8 +113,12 @@ typedef Bool (* PFN_XF86VidModeGetGammaRampSize)(Display*,int,int*);
typedef Status (* PFN_XIQueryVersion)(Display*,int*,int*); typedef Status (* PFN_XIQueryVersion)(Display*,int*,int*);
typedef int (* PFN_XISelectEvents)(Display*,Window,XIEventMask*,int); typedef int (* PFN_XISelectEvents)(Display*,Window,XIEventMask*,int);
typedef XIDeviceInfo* (* PFN_XIQueryDevice)(Display*,int,int*);
typedef Status (* PFN_XIGetProperty)(Display*,int,Atom,long,long,Bool,Atom,Atom*,int*,unsigned long*,unsigned long*,unsigned char**);
#define XIQueryVersion _glfw.x11.xi.QueryVersion #define XIQueryVersion _glfw.x11.xi.QueryVersion
#define XISelectEvents _glfw.x11.xi.SelectEvents #define XISelectEvents _glfw.x11.xi.SelectEvents
#define XIQueryDevice _glfw.x11.xi.QueryDevice
#define XIGetProperty _glfw.x11.xi.GetProperty
typedef Bool (* PFN_XRenderQueryExtension)(Display*,int*,int*); typedef Bool (* PFN_XRenderQueryExtension)(Display*,int*,int*);
typedef Status (* PFN_XRenderQueryVersion)(Display*dpy,int*,int*); typedef Status (* PFN_XRenderQueryVersion)(Display*dpy,int*,int*);
@ -387,6 +391,14 @@ typedef struct _GLFWlibraryX11
int minor; int minor;
PFN_XIQueryVersion QueryVersion; PFN_XIQueryVersion QueryVersion;
PFN_XISelectEvents SelectEvents; PFN_XISelectEvents SelectEvents;
PFN_XIQueryDevice QueryDevice;
PFN_XIGetProperty GetProperty;
XIValuatorClassInfo stylus_ClassInfo[32];
float *stylus_CTMatrix;
float *eraser_CTMatrix;
unsigned int tablet_cursor;
int stylus_deviceid;
int eraser_deviceid;
} xi; } xi;
struct { struct {
@ -438,6 +450,7 @@ unsigned long _glfwGetWindowPropertyX11(Window window,
unsigned char** value); unsigned char** value);
GLFWbool _glfwIsVisualTransparentX11(Visual* visual); GLFWbool _glfwIsVisualTransparentX11(Visual* visual);
void _glfwApplyCoordTransformMatrix(float *matrix, float *px, float *py);
void _glfwGrabErrorHandlerX11(void); void _glfwGrabErrorHandlerX11(void);
void _glfwReleaseErrorHandlerX11(void); void _glfwReleaseErrorHandlerX11(void);
void _glfwInputErrorX11(int error, const char* message); void _glfwInputErrorX11(int error, const char* message);

View File

@ -32,6 +32,7 @@
#include <sys/select.h> #include <sys/select.h>
#include <math.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -1192,33 +1193,161 @@ static void processEvent(XEvent *event)
{ {
_GLFWwindow* window = _glfw.x11.disabledCursorWindow; _GLFWwindow* window = _glfw.x11.disabledCursorWindow;
if (window && if (event->xcookie.extension == _glfw.x11.xi.majorOpcode &&
window->rawMouseMotion && XGetEventData(_glfw.x11.display, &event->xcookie))
event->xcookie.extension == _glfw.x11.xi.majorOpcode &&
XGetEventData(_glfw.x11.display, &event->xcookie) &&
event->xcookie.evtype == XI_RawMotion)
{ {
XIRawEvent* re = event->xcookie.data; if (event->xcookie.evtype == XI_PropertyEvent)
if (re->valuators.mask_len)
{ {
const double* values = re->raw_values; // the first proximity event seems to arrive one later AFTER XI_RawMotion...
double xpos = window->virtualCursorPosX; XIPropertyEvent* re = event->xcookie.data;
double ypos = window->virtualCursorPosY;
if (XIMaskIsSet(re->valuators.mask, 0)) // pen tablet property
if (re->deviceid == _glfw.x11.xi.stylus_deviceid ||
re->deviceid == _glfw.x11.xi.eraser_deviceid)
{ {
xpos += *values; if (re->what == XIPropertyModified)
values++; {
float *data = NULL;
Atom type;
int format;
unsigned long num_items, bytes_after;
if (XIGetProperty(
_glfw.x11.display, re->deviceid,
re->property,
0, 5, False,
XIAnyPropertyType,
&type, &format, &num_items, &bytes_after,
(unsigned char **)&data) == Success)
{
if (format == 32 && num_items > 4)
_glfwInputPenTabletProximity(((unsigned int *)data)[4] != 0);
XFree(data);
}
}
} }
if (XIMaskIsSet(re->valuators.mask, 1))
ypos += *values;
_glfwInputCursorPos(window, xpos, ypos);
} }
} else if (event->xcookie.evtype == XI_RawMotion)
{
XIRawEvent* re = event->xcookie.data;
if (re->valuators.mask_len)
{
const double* values = re->raw_values;
XFreeEventData(_glfw.x11.display, &event->xcookie); // pen tablet raw motion
if (re->deviceid == _glfw.x11.xi.stylus_deviceid ||
re->deviceid == _glfw.x11.xi.eraser_deviceid)
{
unsigned int cursor = re->deviceid == _glfw.x11.xi.stylus_deviceid ? 1 : 2;
float x = 0.0f;
float y = 0.0f;
double pressure = 0.0;
double tiltx = 0.0;
double tilty = 0.0;
if (cursor != _glfw.x11.xi.tablet_cursor)
{
_glfwInputPenTabletCursor(cursor);
_glfw.x11.xi.tablet_cursor = cursor;
}
if (XIMaskIsSet(re->valuators.mask, 0))
{
XIValuatorClassInfo *v = &_glfw.x11.xi.stylus_ClassInfo[0];
x = (*values - v->min) / (v->max - v->min);
values++;
}
if (XIMaskIsSet(re->valuators.mask, 1))
{
XIValuatorClassInfo *v = &_glfw.x11.xi.stylus_ClassInfo[1];
y = (*values - v->min) / (v->max - v->min);
values++;
}
if (XIMaskIsSet(re->valuators.mask, 2))
{
XIValuatorClassInfo *v = &_glfw.x11.xi.stylus_ClassInfo[2];
pressure = (*values - v->min) / (v->max - v->min);
values++;
}
if (XIMaskIsSet(re->valuators.mask, 3))
{
XIValuatorClassInfo *v = &_glfw.x11.xi.stylus_ClassInfo[3];
tiltx = (*values - v->min) / (v->max - v->min) * 2.0 - 1.0;
values++;
}
if (XIMaskIsSet(re->valuators.mask, 4))
{
XIValuatorClassInfo *v = &_glfw.x11.xi.stylus_ClassInfo[4];
tilty = (*values - v->min) / (v->max - v->min) * 2.0 - 1.0;
values++;
}
// apply coordinate transform matrix depending on current cursor
if (re->deviceid == _glfw.x11.xi.stylus_deviceid)
{
if (_glfw.x11.xi.stylus_CTMatrix)
_glfwApplyCoordTransformMatrix(_glfw.x11.xi.stylus_CTMatrix, &x, &y);
}
else
{
if (_glfw.x11.xi.eraser_CTMatrix)
_glfwApplyCoordTransformMatrix(_glfw.x11.xi.eraser_CTMatrix, &x, &y);
}
// send data
{
double tx = tiltx * 1.5707963267949;
double ty = -tilty * 1.5707963267949;
double sinx = sin(tx);
double siny = sin(ty);
double cosx = cos(tx);
double cosy = cos(ty);
/*double matrix[9] = { // full matrix for reference
0.0, -cosy, siny,
cosx, -sinx*siny, -sinx*cosy,
sinx, cosx*siny, cosx*cosy
};*/
double v[3] = {sinx, cosx*siny, cosx*cosy};
double yaw = atan2(v[0], v[1]);
double pitch = 3.141592653589793 - acos(v[2]);
if (yaw < 0.0) yaw += 6.28318530717959;
_glfwInputPenTabletData(
(double)x * DisplayWidth(_glfw.x11.display, _glfw.x11.screen),
(double)y * DisplayHeight(_glfw.x11.display, _glfw.x11.screen),
0.0, // can't find z coordinate
pressure,
pitch,
yaw,
0.0);
}
}
// mouse raw motion
else if (window && window->rawMouseMotion)
{
double xpos = window->virtualCursorPosX;
double ypos = window->virtualCursorPosY;
if (XIMaskIsSet(re->valuators.mask, 0))
{
xpos += *values;
values++;
}
if (XIMaskIsSet(re->valuators.mask, 1))
ypos += *values;
_glfwInputCursorPos(window, xpos, ypos);
}
}
}
XFreeEventData(_glfw.x11.display, &event->xcookie);
}
} }
return; return;

View File

@ -42,6 +42,10 @@
#include "getopt.h" #include "getopt.h"
#if defined(_MSC_VER) && _MSC_VER < 1900
#define snprintf _snprintf
#endif
// Event index // Event index
static unsigned int counter = 0; 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) int main(int argc, char** argv)
{ {
Slot* slots; Slot* slots;
@ -510,6 +532,9 @@ int main(int argc, char** argv)
glfwSetMonitorCallback(monitor_callback); glfwSetMonitorCallback(monitor_callback);
glfwSetJoystickCallback(joystick_callback); glfwSetJoystickCallback(joystick_callback);
glfwSetPenTabletDataCallback(penTabletData_callback);
glfwSetPenTabletCursorCallback(penTabletCursor_callback);
glfwSetPenTabletProximityCallback(penTabletProximity_callback);
while ((ch = getopt(argc, argv, "hfn:")) != -1) while ((ch = getopt(argc, argv, "hfn:")) != -1)
{ {