943 lines
30 KiB
C
943 lines
30 KiB
C
/*
|
|
EasyTab.h - Single-header multi-platform tablet library
|
|
https://github.com/ApoorvaJ/EasyTab
|
|
|
|
----------------------------------------------------------------------------
|
|
USAGE
|
|
----------------------------------------------------------------------------
|
|
1) Add the following lines in exactly one of your cpp files to compile the
|
|
implementation.
|
|
|
|
#define EASYTAB_IMPLEMENTATION
|
|
#include "easytab.h"
|
|
|
|
2) Call EasyTab_Load() with correct parameters to initialize EasyTab. These
|
|
parameters vary per OS, so look at the function declarations or examples
|
|
below. Function returns EASYTAB_OK if initialization was successful.
|
|
|
|
3) Call EasyTab_HandleEvent() in your message-handling code. The function
|
|
returns EASYTAB_OK if the message was a tablet message, and
|
|
EASYTAB_EVENT_NOT_HANDLED otherwise.
|
|
|
|
4) Call EasyTab_Unload() in your shutdown code.
|
|
|
|
5) Once initialized, you can query tablet state using the EasyTab pointer.
|
|
e.g.:
|
|
|
|
EasyTab->PosX // X position of the pen
|
|
EasyTab->PosY // Y position of the pen
|
|
EasyTab->Pressure // Pressure of the pen ranging from 0.0f to 1.0f
|
|
|
|
For more info, have a look at the EasyTabInfo struct below.
|
|
|
|
|
|
* Add -lXi to compiler options to link XInput on Linux.
|
|
|
|
----------------------------------------------------------------------------
|
|
EXAMPLES
|
|
----------------------------------------------------------------------------
|
|
1) Windows:
|
|
|
|
int CALLBACK WinMain(...)
|
|
{
|
|
HWND Window;
|
|
|
|
...
|
|
|
|
if (EasyTab_Load(Window) != EASYTAB_OK) // Load
|
|
{
|
|
OutputDebugStringA("Tablet init failed\n");
|
|
}
|
|
|
|
...
|
|
|
|
// Once you've set up EasyTab loading, unloading and event handling,
|
|
// use the EasyTab variable at any point in your program to access
|
|
// the tablet state:
|
|
// EasyTab->PosX
|
|
// EasyTab->PosY
|
|
// EasyTab->Pressure
|
|
// For more tablet information, look at the EasyTabInfo struct.
|
|
|
|
...
|
|
|
|
EasyTab_Unload(); // Unload
|
|
}
|
|
|
|
LRESULT CALLBACK WindowProc(
|
|
HWND Window,
|
|
UINT Message,
|
|
WPARAM WParam,
|
|
LPARAM LParam)
|
|
{
|
|
if (EasyTab_HandleEvent(Window, Message, LParam, WParam) == EASYTAB_OK) // Event
|
|
{
|
|
return true; // Tablet event handled
|
|
}
|
|
|
|
switch (Message)
|
|
{
|
|
...
|
|
}
|
|
}
|
|
|
|
|
|
2) Linux:
|
|
|
|
int main(...)
|
|
{
|
|
Display* Disp;
|
|
Window Win;
|
|
|
|
...
|
|
|
|
if (EasyTab_Load(Disp, Win) != EASYTAB_OK) // Load
|
|
{
|
|
printf("Tablet init failed\n");
|
|
}
|
|
|
|
...
|
|
|
|
while (XPending(Disp)) // Event loop
|
|
{
|
|
XEvent Event;
|
|
XNextEvent(XlibDisplay, &Event);
|
|
|
|
if (EasyTab_HandleEvent(&Event) == EASYTAB_OK) // Event
|
|
{
|
|
continue; // Tablet event handled
|
|
}
|
|
|
|
switch (Event.type)
|
|
{
|
|
...
|
|
}
|
|
}
|
|
|
|
...
|
|
|
|
// Once you've set up EasyTab loading, unloading and event handling,
|
|
// use the EasyTab variable at any point in your program to access
|
|
// the tablet state:
|
|
// EasyTab->PosX
|
|
// EasyTab->PosY
|
|
// EasyTab->Pressure
|
|
// For more tablet information, look at the EasyTabInfo struct.
|
|
|
|
...
|
|
|
|
EasyTab_Unload(); // Unload
|
|
}
|
|
|
|
----------------------------------------------------------------------------
|
|
CREDITS
|
|
----------------------------------------------------------------------------
|
|
Apoorva Joshi apoorvaj.io
|
|
Sergio Gonzalez s3rg.io
|
|
|
|
This library is coded in the spirit of the stb libraries and follows the stb
|
|
guidelines.
|
|
|
|
*/
|
|
|
|
// TODO: Null checks and warnings for EasyTab
|
|
// TODO: Differentiate between stylus and eraser in the API
|
|
// TODO: Linux support for relative mode
|
|
// TODO: Documentation for relative mode
|
|
|
|
// =============================================================================
|
|
// EasyTab header section
|
|
// =============================================================================
|
|
|
|
#ifndef EASYTAB_H
|
|
#define EASYTAB_H
|
|
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#ifdef __linux__
|
|
#include <X11/extensions/XInput.h>
|
|
#endif // __linux__
|
|
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#endif // _WIN32
|
|
|
|
typedef enum
|
|
{
|
|
EASYTAB_OK = 0,
|
|
|
|
// Errors
|
|
EASYTAB_MEMORY_ERROR = -1,
|
|
EASYTAB_X11_ERROR = -2,
|
|
EASYTAB_DLL_LOAD_ERROR = -3,
|
|
EASYTAB_WACOM_WIN32_ERROR = -4,
|
|
EASYTAB_INVALID_FUNCTION_ERROR = -5,
|
|
|
|
EASYTAB_EVENT_NOT_HANDLED = -16,
|
|
} EasyTabResult;
|
|
|
|
typedef enum
|
|
{
|
|
EASYTAB_TRACKING_MODE_SYSTEM = 0,
|
|
EASYTAB_TRACKING_MODE_RELATIVE = 1,
|
|
} EasyTabTrackingMode;
|
|
|
|
#ifdef WIN32
|
|
// -----------------------------------------------------------------------------
|
|
// wintab.h
|
|
// -----------------------------------------------------------------------------
|
|
#if 1
|
|
|
|
DECLARE_HANDLE(HMGR);
|
|
DECLARE_HANDLE(HCTX);
|
|
|
|
typedef DWORD WTPKT;
|
|
typedef DWORD FIX32;
|
|
|
|
// Messages
|
|
#if 1
|
|
|
|
#define WT_DEFBASE 0x7FF0
|
|
#define WT_MAXOFFSET 0xF
|
|
|
|
#define _WT_PACKET(b) ((b)+0)
|
|
#define _WT_CTXOPEN(b) ((b)+1)
|
|
#define _WT_CTXCLOSE(b) ((b)+2)
|
|
#define _WT_CTXUPDATE(b) ((b)+3)
|
|
#define _WT_CTXOVERLAP(b) ((b)+4)
|
|
#define _WT_PROXIMITY(b) ((b)+5)
|
|
#define _WT_INFOCHANGE(b) ((b)+6)
|
|
#define _WT_CSRCHANGE(b) ((b)+7) /* 1.1 */
|
|
#define _WT_PACKETEXT(b) ((b)+8) /* 1.4 */
|
|
#define _WT_MAX(b) ((b)+WT_MAXOFFSET)
|
|
|
|
#define WT_PACKET _WT_PACKET(WT_DEFBASE)
|
|
#define WT_CTXOPEN _WT_CTXOPEN(WT_DEFBASE)
|
|
#define WT_CTXCLOSE _WT_CTXCLOSE(WT_DEFBASE)
|
|
#define WT_CTXUPDATE _WT_CTXUPDATE(WT_DEFBASE)
|
|
#define WT_CTXOVERLAP _WT_CTXOVERLAP(WT_DEFBASE)
|
|
#define WT_PROXIMITY _WT_PROXIMITY(WT_DEFBASE)
|
|
#define WT_INFOCHANGE _WT_INFOCHANGE(WT_DEFBASE)
|
|
#define WT_CSRCHANGE _WT_CSRCHANGE(WT_DEFBASE) /* 1.1 */
|
|
#define WT_PACKETEXT _WT_PACKETEXT(WT_DEFBASE) /* 1.4 */
|
|
#define WT_MAX _WT_MAX(WT_DEFBASE)
|
|
|
|
#endif // Messages
|
|
|
|
// Flags
|
|
#if 1
|
|
|
|
#define CTX_NAME 1
|
|
#define CTX_OPTIONS 2
|
|
#define CTX_STATUS 3
|
|
#define CTX_LOCKS 4
|
|
#define CTX_MSGBASE 5
|
|
#define CTX_DEVICE 6
|
|
#define CTX_PKTRATE 7
|
|
#define CTX_PKTDATA 8
|
|
#define CTX_PKTMODE 9
|
|
#define CTX_MOVEMASK 10
|
|
#define CTX_BTNDNMASK 11
|
|
#define CTX_BTNUPMASK 12
|
|
#define CTX_INORGX 13
|
|
#define CTX_INORGY 14
|
|
#define CTX_INORGZ 15
|
|
#define CTX_INEXTX 16
|
|
#define CTX_INEXTY 17
|
|
#define CTX_INEXTZ 18
|
|
#define CTX_OUTORGX 19
|
|
#define CTX_OUTORGY 20
|
|
#define CTX_OUTORGZ 21
|
|
#define CTX_OUTEXTX 22
|
|
#define CTX_OUTEXTY 23
|
|
#define CTX_OUTEXTZ 24
|
|
#define CTX_SENSX 25
|
|
#define CTX_SENSY 26
|
|
#define CTX_SENSZ 27
|
|
#define CTX_SYSMODE 28
|
|
#define CTX_SYSORGX 29
|
|
#define CTX_SYSORGY 30
|
|
#define CTX_SYSEXTX 31
|
|
#define CTX_SYSEXTY 32
|
|
#define CTX_SYSSENSX 33
|
|
#define CTX_SYSSENSY 34
|
|
#define CTX_MAX 34
|
|
|
|
// Context option values
|
|
#define CXO_SYSTEM 0x0001
|
|
#define CXO_PEN 0x0002
|
|
#define CXO_MESSAGES 0x0004
|
|
#define CXO_MARGIN 0x8000
|
|
#define CXO_MGNINSIDE 0x4000
|
|
#define CXO_CSRMESSAGES 0x0008 /* 1.1 */
|
|
|
|
#define DVC_NAME 1
|
|
#define DVC_HARDWARE 2
|
|
#define DVC_NCSRTYPES 3
|
|
#define DVC_FIRSTCSR 4
|
|
#define DVC_PKTRATE 5
|
|
#define DVC_PKTDATA 6
|
|
#define DVC_PKTMODE 7
|
|
#define DVC_CSRDATA 8
|
|
#define DVC_XMARGIN 9
|
|
#define DVC_YMARGIN 10
|
|
#define DVC_ZMARGIN 11
|
|
#define DVC_X 12
|
|
#define DVC_Y 13
|
|
#define DVC_Z 14
|
|
#define DVC_NPRESSURE 15
|
|
#define DVC_TPRESSURE 16
|
|
#define DVC_ORIENTATION 17
|
|
#define DVC_ROTATION 18 /* 1.1 */
|
|
#define DVC_PNPID 19 /* 1.1 */
|
|
#define DVC_MAX 19
|
|
|
|
#define PK_CONTEXT 0x0001 // reporting context
|
|
#define PK_STATUS 0x0002 // status bits
|
|
#define PK_TIME 0x0004 // time stamp
|
|
#define PK_CHANGED 0x0008 // change bit vector
|
|
#define PK_SERIAL_NUMBER 0x0010 // packet serial number
|
|
#define PK_CURSOR 0x0020 // reporting cursor
|
|
#define PK_BUTTONS 0x0040 // button information
|
|
#define PK_X 0x0080 // x axis
|
|
#define PK_Y 0x0100 // y axis
|
|
#define PK_Z 0x0200 // z axis
|
|
#define PK_NORMAL_PRESSURE 0x0400 // normal or tip pressure
|
|
#define PK_TANGENT_PRESSURE 0x0800 // tangential or barrel pressure
|
|
#define PK_ORIENTATION 0x1000 // orientation info: tilts
|
|
#define PK_ROTATION 0x2000 // rotation info; 1.1
|
|
|
|
// constants for use with pktdef.h
|
|
#define PKEXT_ABSOLUTE 1
|
|
#define PKEXT_RELATIVE 2
|
|
|
|
#define WTI_DEFCONTEXT 3
|
|
#define WTI_DEFSYSCTX 4
|
|
#define WTI_DEVICES 100
|
|
#define WTI_DDCTXS 400 /* 1.1 */
|
|
#define WTI_DSCTXS 500 /* 1.1 */
|
|
|
|
#endif // Flags
|
|
|
|
typedef struct tagAXIS {
|
|
LONG axMin;
|
|
LONG axMax;
|
|
UINT axUnits;
|
|
FIX32 axResolution;
|
|
} AXIS, *PAXIS, NEAR *NPAXIS, FAR *LPAXIS;
|
|
|
|
#define LCNAMELEN 40
|
|
typedef struct tagLOGCONTEXTA {
|
|
char lcName[LCNAMELEN];
|
|
UINT lcOptions;
|
|
UINT lcStatus;
|
|
UINT lcLocks;
|
|
UINT lcMsgBase;
|
|
UINT lcDevice;
|
|
UINT lcPktRate;
|
|
WTPKT lcPktData;
|
|
WTPKT lcPktMode;
|
|
WTPKT 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;
|
|
FIX32 lcSensX;
|
|
FIX32 lcSensY;
|
|
FIX32 lcSensZ;
|
|
BOOL lcSysMode;
|
|
int lcSysOrgX;
|
|
int lcSysOrgY;
|
|
int lcSysExtX;
|
|
int lcSysExtY;
|
|
FIX32 lcSysSensX;
|
|
FIX32 lcSysSensY;
|
|
} LOGCONTEXTA, *PLOGCONTEXTA, NEAR *NPLOGCONTEXTA, FAR *LPLOGCONTEXTA;
|
|
|
|
typedef struct tagEXTENSIONBASE { /* 1.4 */
|
|
HCTX nContext;
|
|
UINT nStatus;
|
|
DWORD nTime;
|
|
UINT nSerialNumber;
|
|
} EXTENSIONBASE;
|
|
|
|
#endif // wintab.h
|
|
// -----------------------------------------------------------------------------
|
|
|
|
#define PACKETDATA PK_X | PK_Y | PK_BUTTONS | PK_NORMAL_PRESSURE
|
|
#define PACKETMODE 0
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// pktdef.h
|
|
// -----------------------------------------------------------------------------
|
|
#if 1
|
|
|
|
// TODO: Simplify this file if we have a fixed packet format.
|
|
// The macros here are too ugly.
|
|
|
|
#ifndef PACKETNAME
|
|
/* if no packet name prefix */
|
|
#define __PFX(x) x
|
|
#define __IFX(x,y) x ## y
|
|
#else
|
|
/* add prefixes and infixes to packet format names */
|
|
#define __PFX(x) __PFX2(PACKETNAME,x)
|
|
#define __PFX2(p,x) __PFX3(p,x)
|
|
#define __PFX3(p,x) p ## x
|
|
#define __IFX(x,y) __IFX2(x,PACKETNAME,y)
|
|
#define __IFX2(x,i,y) __IFX3(x,i,y)
|
|
#define __IFX3(x,i,y) x ## i ## y
|
|
#endif
|
|
|
|
#define __SFX2(x,s) __SFX3(x,s)
|
|
#define __SFX3(x,s) x ## s
|
|
|
|
#define __TAG __IFX(tag,PACKET)
|
|
#define __TYPES __PFX(PACKET), * __IFX(P,PACKET), NEAR * __IFX(NP,PACKET), FAR * __IFX(LP,PACKET)
|
|
|
|
#define __TAGE __IFX(tag,PACKETEXT)
|
|
#define __TYPESE __PFX(PACKETEXT), * __IFX(P,PACKETEXT), NEAR * __IFX(NP,PACKETEXT), FAR * __IFX(LP,PACKETEXT)
|
|
|
|
#define __DATA (__PFX(PACKETDATA))
|
|
#define __MODE (__PFX(PACKETMODE))
|
|
#define __EXT(x) __SFX2(__PFX(PACKET),x)
|
|
|
|
|
|
typedef struct __TAG {
|
|
#if (__DATA & PK_CONTEXT)
|
|
HCTX pkContext;
|
|
#endif
|
|
#if (__DATA & PK_STATUS)
|
|
UINT pkStatus;
|
|
#endif
|
|
#if (__DATA & PK_TIME)
|
|
DWORD pkTime;
|
|
#endif
|
|
#if (__DATA & PK_CHANGED)
|
|
WTPKT pkChanged;
|
|
#endif
|
|
#if (__DATA & PK_SERIAL_NUMBER)
|
|
UINT pkSerialNumber;
|
|
#endif
|
|
#if (__DATA & PK_CURSOR)
|
|
UINT pkCursor;
|
|
#endif
|
|
#if (__DATA & PK_BUTTONS)
|
|
DWORD pkButtons;
|
|
#endif
|
|
#if (__DATA & PK_X)
|
|
LONG pkX;
|
|
#endif
|
|
#if (__DATA & PK_Y)
|
|
LONG pkY;
|
|
#endif
|
|
#if (__DATA & PK_Z)
|
|
LONG pkZ;
|
|
#endif
|
|
#if (__DATA & PK_NORMAL_PRESSURE)
|
|
#if (__MODE & PK_NORMAL_PRESSURE)
|
|
/* relative */
|
|
int pkNormalPressure;
|
|
#else
|
|
/* absolute */
|
|
UINT pkNormalPressure;
|
|
#endif
|
|
#endif
|
|
#if (__DATA & PK_TANGENT_PRESSURE)
|
|
#if (__MODE & PK_TANGENT_PRESSURE)
|
|
/* relative */
|
|
int pkTangentPressure;
|
|
#else
|
|
/* absolute */
|
|
UINT pkTangentPressure;
|
|
#endif
|
|
#endif
|
|
#if (__DATA & PK_ORIENTATION)
|
|
ORIENTATION pkOrientation;
|
|
#endif
|
|
#if (__DATA & PK_ROTATION)
|
|
ROTATION pkRotation; /* 1.1 */
|
|
#endif
|
|
|
|
#ifndef NOWTEXTENSIONS
|
|
/* extensions begin here. */
|
|
#if (__EXT(FKEYS) == PKEXT_RELATIVE) || (__EXT(FKEYS) == PKEXT_ABSOLUTE)
|
|
UINT pkFKeys;
|
|
#endif
|
|
#if (__EXT(TILT) == PKEXT_RELATIVE) || (__EXT(TILT) == PKEXT_ABSOLUTE)
|
|
TILT pkTilt;
|
|
#endif
|
|
#endif
|
|
|
|
} __TYPES;
|
|
|
|
#ifndef NOWTEXTENSIONS
|
|
typedef struct __TAGE {
|
|
EXTENSIONBASE pkBase;
|
|
|
|
#if (__EXT(EXPKEYS) == PKEXT_RELATIVE) || (__EXT(EXPKEYS) == PKEXT_ABSOLUTE)
|
|
EXPKEYSDATA pkExpKeys; /* 1.4 */
|
|
#endif
|
|
#if (__EXT(TOUCHSTRIP) == PKEXT_RELATIVE) || (__EXT(TOUCHSTRIP) == PKEXT_ABSOLUTE)
|
|
SLIDERDATA pkTouchStrip; /* 1.4 */
|
|
#endif
|
|
#if (__EXT(TOUCHRING) == PKEXT_RELATIVE) || (__EXT(TOUCHRING) == PKEXT_ABSOLUTE)
|
|
SLIDERDATA pkTouchRing; /* 1.4 */
|
|
#endif
|
|
|
|
} __TYPESE;
|
|
#endif
|
|
|
|
#undef PACKETNAME
|
|
#undef __TAG
|
|
#undef __TAGE
|
|
#undef __TAG2
|
|
#undef __TYPES
|
|
#undef __TYPESE
|
|
#undef __TYPES2
|
|
#undef __DATA
|
|
#undef __MODE
|
|
#undef __PFX
|
|
#undef __PFX2
|
|
#undef __PFX3
|
|
#undef __IFX
|
|
#undef __IFX2
|
|
#undef __IFX3
|
|
#undef __SFX2
|
|
#undef __SFX3
|
|
|
|
#endif // pktdef.h
|
|
// -----------------------------------------------------------------------------
|
|
|
|
typedef UINT (WINAPI * WTINFOA) (UINT, UINT, LPVOID);
|
|
typedef HCTX (WINAPI * WTOPENA) (HWND, LPLOGCONTEXTA, BOOL);
|
|
typedef BOOL (WINAPI * WTGETA) (HCTX, LPLOGCONTEXTA);
|
|
typedef BOOL (WINAPI * WTSETA) (HCTX, LPLOGCONTEXTA);
|
|
typedef BOOL (WINAPI * WTCLOSE) (HCTX);
|
|
typedef BOOL (WINAPI * WTENABLE) (HCTX, BOOL);
|
|
typedef BOOL (WINAPI * WTPACKET) (HCTX, UINT, LPVOID);
|
|
typedef BOOL (WINAPI * WTOVERLAP) (HCTX, BOOL);
|
|
typedef BOOL (WINAPI * WTSAVE) (HCTX, LPVOID);
|
|
typedef BOOL (WINAPI * WTCONFIG) (HCTX, HWND);
|
|
typedef HCTX (WINAPI * WTRESTORE) (HWND, LPVOID, BOOL);
|
|
typedef BOOL (WINAPI * WTEXTSET) (HCTX, UINT, LPVOID);
|
|
typedef BOOL (WINAPI * WTEXTGET) (HCTX, UINT, LPVOID);
|
|
typedef BOOL (WINAPI * WTQUEUESIZESET) (HCTX, int);
|
|
typedef int (WINAPI * WTDATAPEEK) (HCTX, UINT, UINT, int, LPVOID, LPINT);
|
|
typedef int (WINAPI * WTPACKETSGET) (HCTX, int, LPVOID);
|
|
typedef HMGR (WINAPI * WTMGROPEN) (HWND, UINT);
|
|
typedef BOOL (WINAPI * WTMGRCLOSE) (HMGR);
|
|
typedef HCTX (WINAPI * WTMGRDEFCONTEXT) (HMGR, BOOL);
|
|
typedef HCTX (WINAPI * WTMGRDEFCONTEXTEX) (HMGR, UINT, BOOL);
|
|
|
|
#endif // WIN32
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Enums
|
|
// -----------------------------------------------------------------------------
|
|
|
|
/*
|
|
Use this enum in conjunction with EasyTab->Buttons to check for tablet button
|
|
presses.
|
|
e.g. To check for lower pen button press, use:
|
|
|
|
if (EasyTab->Buttons & EasyTab_Buttons_Pen_Lower)
|
|
{
|
|
// Lower button is pressed
|
|
}
|
|
*/
|
|
enum EasyTab_Buttons_
|
|
{
|
|
EasyTab_Buttons_Pen_Touch = 1 << 0, // Pen is touching tablet
|
|
EasyTab_Buttons_Pen_Lower = 1 << 1, // Lower pen button is pressed
|
|
EasyTab_Buttons_Pen_Upper = 1 << 2, // Upper pen button is pressed
|
|
};
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Structs
|
|
// -----------------------------------------------------------------------------
|
|
typedef struct
|
|
{
|
|
int32_t PosX, PosY;
|
|
float Pressure; // Range: 0.0f to 1.0f
|
|
int32_t Buttons; // Bit field. Use with the EasyTab_Buttons_ enum.
|
|
|
|
int32_t RangeX, RangeY;
|
|
int32_t MaxPressure;
|
|
|
|
#ifdef __linux__
|
|
XDevice* Device;
|
|
uint32_t MotionType;
|
|
XEventClass EventClasses[1024];
|
|
uint32_t NumEventClasses;
|
|
#endif // __linux__
|
|
|
|
#ifdef WIN32
|
|
HINSTANCE Dll;
|
|
HCTX Context;
|
|
|
|
WTINFOA WTInfoA;
|
|
WTOPENA WTOpenA;
|
|
WTGETA WTGetA;
|
|
WTSETA WTSetA;
|
|
WTCLOSE WTClose;
|
|
WTPACKET WTPacket;
|
|
WTENABLE WTEnable;
|
|
WTOVERLAP WTOverlap;
|
|
WTSAVE WTSave;
|
|
WTCONFIG WTConfig;
|
|
WTRESTORE WTRestore;
|
|
WTEXTSET WTExtSet;
|
|
WTEXTGET WTExtGet;
|
|
WTQUEUESIZESET WTQueueSizeSet;
|
|
WTDATAPEEK WTDataPeek;
|
|
WTPACKETSGET WTPacketsGet;
|
|
WTMGROPEN WTMgrOpen;
|
|
WTMGRCLOSE WTMgrClose;
|
|
WTMGRDEFCONTEXT WTMgrDefContext;
|
|
WTMGRDEFCONTEXTEX WTMgrDefContextEx;
|
|
#endif // WIN32
|
|
} EasyTabInfo;
|
|
|
|
extern EasyTabInfo* EasyTab;
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Function declarations
|
|
// -----------------------------------------------------------------------------
|
|
#if defined(__linux__)
|
|
|
|
EasyTabResult EasyTab_Load(Display* Disp, Window Win);
|
|
EasyTabResult EasyTab_HandleEvent(XEvent* Event);
|
|
void EasyTab_Unload(Display* Disp);
|
|
|
|
#elif defined(_WIN32)
|
|
|
|
EasyTabResult EasyTab_Load(HWND Window);
|
|
EasyTabResult EasyTab_Load_Ex(HWND Window,
|
|
EasyTabTrackingMode Mode,
|
|
float RelativeModeSensitivity,
|
|
int32_t MoveCursor);
|
|
EasyTabResult EasyTab_HandleEvent(HWND Window,
|
|
UINT Message,
|
|
LPARAM LParam,
|
|
WPARAM WParam);
|
|
void EasyTab_Unload();
|
|
|
|
#else
|
|
|
|
// Save some trouble when porting.
|
|
#error "Unsupported platform."
|
|
|
|
#endif // __linux__ _WIN32
|
|
// -----------------------------------------------------------------------------
|
|
|
|
#endif // EASYTAB_H
|
|
|
|
|
|
|
|
// =============================================================================
|
|
// EasyTab implementation section
|
|
// =============================================================================
|
|
|
|
#ifdef EASYTAB_IMPLEMENTATION
|
|
|
|
EasyTabInfo* EasyTab;
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Linux implementation
|
|
// -----------------------------------------------------------------------------
|
|
#ifdef __linux__
|
|
|
|
EasyTabResult EasyTab_Load(Display* Disp, Window Win)
|
|
{
|
|
EasyTab = (EasyTabInfo*)calloc(1, sizeof(EasyTabInfo)); // We want init to zero, hence calloc.
|
|
if (!EasyTab) { return EASYTAB_MEMORY_ERROR; }
|
|
|
|
int32_t Count;
|
|
XDeviceInfoPtr Devices = (XDeviceInfoPtr)XListInputDevices(Disp, &Count);
|
|
if (!Devices) { return EASYTAB_X11_ERROR; }
|
|
|
|
for (int32_t i = 0; i < Count; i++)
|
|
{
|
|
if (!strstr(Devices[i].name, "stylus") &&
|
|
!strstr(Devices[i].name, "eraser")) { continue; }
|
|
|
|
EasyTab->Device = XOpenDevice(Disp, Devices[i].id);
|
|
XAnyClassPtr ClassPtr = Devices[i].inputclassinfo;
|
|
|
|
for (int32_t j = 0; j < Devices[i].num_classes; j++)
|
|
{
|
|
#if defined(__cplusplus)
|
|
switch (ClassPtr->c_class)
|
|
#else
|
|
switch (ClassPtr->class)
|
|
#endif
|
|
{
|
|
case ValuatorClass:
|
|
{
|
|
XValuatorInfo *Info = (XValuatorInfo *)ClassPtr;
|
|
// X
|
|
if (Info->num_axes > 0)
|
|
{
|
|
int32_t min = Info->axes[0].min_value;
|
|
EasyTab->RangeX = Info->axes[0].max_value;
|
|
//printf("Max/min x values: %d, %d\n", min, EasyTab->RangeX); // TODO: Platform-print macro
|
|
}
|
|
|
|
// Y
|
|
if (Info->num_axes > 1)
|
|
{
|
|
int32_t min = Info->axes[1].min_value;
|
|
EasyTab->RangeY = Info->axes[1].max_value;
|
|
//printf("Max/min y values: %d, %d\n", min, EasyTab->RangeY);
|
|
}
|
|
|
|
// Pressure
|
|
if (Info->num_axes > 2)
|
|
{
|
|
int32_t min = Info->axes[2].min_value;
|
|
EasyTab->MaxPressure = Info->axes[2].max_value;
|
|
//printf("Max/min pressure values: %d, %d\n", min, EasyTab->MaxPressure);
|
|
}
|
|
|
|
XEventClass EventClass;
|
|
DeviceMotionNotify(EasyTab->Device, EasyTab->MotionType, EventClass);
|
|
if (EventClass)
|
|
{
|
|
EasyTab->EventClasses[EasyTab->NumEventClasses] = EventClass;
|
|
EasyTab->NumEventClasses++;
|
|
}
|
|
} break;
|
|
}
|
|
|
|
ClassPtr = (XAnyClassPtr) ((uint8_t*)ClassPtr + ClassPtr->length); // TODO: Access this as an array to avoid pointer arithmetic?
|
|
}
|
|
|
|
XSelectExtensionEvent(Disp, Win, EasyTab->EventClasses, EasyTab->NumEventClasses);
|
|
}
|
|
|
|
XFreeDeviceList(Devices);
|
|
|
|
if (EasyTab->Device != 0) { return EASYTAB_OK; }
|
|
else { return EASYTAB_X11_ERROR; }
|
|
}
|
|
|
|
EasyTabResult EasyTab_HandleEvent(XEvent* Event)
|
|
{
|
|
if (Event->type != EasyTab->MotionType) { return EASYTAB_EVENT_NOT_HANDLED; }
|
|
|
|
XDeviceMotionEvent* MotionEvent = (XDeviceMotionEvent*)(Event);
|
|
EasyTab->PosX = MotionEvent->x;
|
|
EasyTab->PosY = MotionEvent->y;
|
|
EasyTab->Pressure = (float)MotionEvent->axis_data[2] / (float)EasyTab->MaxPressure;
|
|
return EASYTAB_OK;
|
|
}
|
|
|
|
void EasyTab_Unload(Display* Disp)
|
|
{
|
|
XCloseDevice(Disp, EasyTab->Device);
|
|
free(EasyTab);
|
|
EasyTab = NULL;
|
|
}
|
|
|
|
#endif // __linux__
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Windows implementation
|
|
// -----------------------------------------------------------------------------
|
|
#ifdef WIN32
|
|
|
|
#define GETPROCADDRESS(type, func) \
|
|
EasyTab->func = (type)GetProcAddress(EasyTab->Dll, #func); \
|
|
if (!EasyTab->func) \
|
|
{ \
|
|
OutputDebugStringA("Function " #func " not found in Wintab32.dll.\n"); \
|
|
return EASYTAB_INVALID_FUNCTION_ERROR; \
|
|
}
|
|
|
|
|
|
EasyTabResult EasyTab_Load(HWND Window)
|
|
{
|
|
return EasyTab_Load_Ex(Window, EASYTAB_TRACKING_MODE_SYSTEM, 0, 1);
|
|
}
|
|
|
|
EasyTabResult EasyTab_Load_Ex(HWND Window,
|
|
EasyTabTrackingMode TrackingMode,
|
|
float RelativeModeSensitivity,
|
|
int32_t MoveCursor)
|
|
{
|
|
EasyTab = (EasyTabInfo*)calloc(1, sizeof(EasyTabInfo)); // We want init to zero, hence calloc.
|
|
if (!EasyTab) { return EASYTAB_MEMORY_ERROR; }
|
|
|
|
// Load Wintab DLL and get function addresses
|
|
{
|
|
EasyTab->Dll = LoadLibraryA("Wintab32.dll");
|
|
if (!EasyTab->Dll)
|
|
{
|
|
OutputDebugStringA("Wintab32.dll not found.\n");
|
|
return EASYTAB_DLL_LOAD_ERROR;
|
|
}
|
|
|
|
GETPROCADDRESS(WTINFOA , WTInfoA);
|
|
GETPROCADDRESS(WTOPENA , WTOpenA);
|
|
GETPROCADDRESS(WTGETA , WTGetA);
|
|
GETPROCADDRESS(WTSETA , WTSetA);
|
|
GETPROCADDRESS(WTCLOSE , WTClose);
|
|
GETPROCADDRESS(WTPACKET , WTPacket);
|
|
GETPROCADDRESS(WTENABLE , WTEnable);
|
|
GETPROCADDRESS(WTOVERLAP , WTOverlap);
|
|
GETPROCADDRESS(WTSAVE , WTSave);
|
|
GETPROCADDRESS(WTCONFIG , WTConfig);
|
|
GETPROCADDRESS(WTRESTORE , WTRestore);
|
|
GETPROCADDRESS(WTEXTSET , WTExtSet);
|
|
GETPROCADDRESS(WTEXTGET , WTExtGet);
|
|
GETPROCADDRESS(WTQUEUESIZESET , WTQueueSizeSet);
|
|
GETPROCADDRESS(WTDATAPEEK , WTDataPeek);
|
|
GETPROCADDRESS(WTPACKETSGET , WTPacketsGet);
|
|
GETPROCADDRESS(WTMGROPEN , WTMgrOpen);
|
|
GETPROCADDRESS(WTMGRCLOSE , WTMgrClose);
|
|
GETPROCADDRESS(WTMGRDEFCONTEXT , WTMgrDefContext);
|
|
GETPROCADDRESS(WTMGRDEFCONTEXTEX , WTMgrDefContextEx);
|
|
}
|
|
|
|
if (!EasyTab->WTInfoA(0, 0, NULL))
|
|
{
|
|
OutputDebugStringA("Wintab services not available.\n");
|
|
return EASYTAB_WACOM_WIN32_ERROR;
|
|
}
|
|
|
|
// Open context
|
|
{
|
|
LOGCONTEXTA LogContext = {0};
|
|
AXIS RangeX = {0};
|
|
AXIS RangeY = {0};
|
|
AXIS Pressure = {0};
|
|
|
|
EasyTab->WTInfoA(WTI_DDCTXS, 0, &LogContext);
|
|
EasyTab->WTInfoA(WTI_DEVICES, DVC_X, &RangeX);
|
|
EasyTab->WTInfoA(WTI_DEVICES, DVC_Y, &RangeY);
|
|
EasyTab->WTInfoA(WTI_DEVICES, DVC_NPRESSURE, &Pressure);
|
|
|
|
LogContext.lcPktData = PACKETDATA; // ??
|
|
LogContext.lcOptions |= CXO_MESSAGES;
|
|
if (MoveCursor) { LogContext.lcOptions |= CXO_SYSTEM; }
|
|
LogContext.lcPktMode = PACKETMODE;
|
|
LogContext.lcMoveMask = PACKETDATA;
|
|
LogContext.lcBtnUpMask = LogContext.lcBtnDnMask;
|
|
|
|
LogContext.lcOutOrgX = 0;
|
|
LogContext.lcOutOrgY = 0;
|
|
LogContext.lcOutExtX = GetSystemMetrics(SM_CXSCREEN);
|
|
LogContext.lcOutExtY = -GetSystemMetrics(SM_CYSCREEN);
|
|
|
|
LogContext.lcSysOrgX = 0;
|
|
LogContext.lcSysOrgY = 0;
|
|
LogContext.lcSysExtX = GetSystemMetrics(SM_CXSCREEN);
|
|
LogContext.lcSysExtY = GetSystemMetrics(SM_CYSCREEN);
|
|
|
|
if (TrackingMode == EASYTAB_TRACKING_MODE_RELATIVE)
|
|
{
|
|
LogContext.lcPktMode |= PK_X | PK_Y; // TODO: Should this be included in the
|
|
// PACKETMODE macro define up top?
|
|
LogContext.lcSysMode = 1;
|
|
|
|
if (RelativeModeSensitivity > 1.0f)
|
|
{
|
|
RelativeModeSensitivity = 1.0f;
|
|
}
|
|
else if (RelativeModeSensitivity < 0.0f)
|
|
{
|
|
RelativeModeSensitivity = 0.0f;
|
|
}
|
|
|
|
// Wintab expects sensitivity to be a 32-bit fixed point number
|
|
// with the radix point between the two words. Thus, the type
|
|
// contains 16 bits to the left of the radix point and 16 bits to
|
|
// the right of it.
|
|
//
|
|
// 0x10000 Hex
|
|
// = 65,536 Decimal
|
|
// = 0000 0000 0000 0001 . 0000 0000 0000 0000 Binary
|
|
// = 1.0 Fixed Point
|
|
uint32_t Sensitivity = (uint32_t)(0x10000 * RelativeModeSensitivity);
|
|
|
|
if (MoveCursor)
|
|
{
|
|
LogContext.lcSysSensX = LogContext.lcSysSensY = Sensitivity;
|
|
}
|
|
else
|
|
{
|
|
LogContext.lcSensX = LogContext.lcSensY = Sensitivity;
|
|
}
|
|
}
|
|
|
|
EasyTab->Context = EasyTab->WTOpenA(Window, &LogContext, TRUE);
|
|
|
|
if (!EasyTab->Context)
|
|
{
|
|
OutputDebugStringA("Wintab context couldn't be opened.\n");
|
|
return EASYTAB_WACOM_WIN32_ERROR;
|
|
}
|
|
|
|
// Get tablet capabilites
|
|
{
|
|
EasyTab->MaxPressure = Pressure.axMax;
|
|
EasyTab->RangeX = RangeX.axMax;
|
|
EasyTab->RangeY = RangeY.axMax;
|
|
}
|
|
}
|
|
|
|
return EASYTAB_OK;
|
|
}
|
|
|
|
#undef GETPROCADDRESS
|
|
|
|
EasyTabResult EasyTab_HandleEvent(HWND Window, UINT Message, LPARAM LParam, WPARAM WParam)
|
|
{
|
|
PACKET Packet = { 0 };
|
|
|
|
if (Message == WT_PACKET &&
|
|
(HCTX)LParam == EasyTab->Context &&
|
|
EasyTab->WTPacket(EasyTab->Context, (UINT)WParam, &Packet))
|
|
{
|
|
POINT Point = { 0 };
|
|
Point.x = Packet.pkX;
|
|
Point.y = Packet.pkY;
|
|
ScreenToClient(Window, &Point);
|
|
EasyTab->PosX = Point.x;
|
|
EasyTab->PosY = Point.y;
|
|
|
|
EasyTab->Pressure = (float)Packet.pkNormalPressure / (float)EasyTab->MaxPressure;
|
|
EasyTab->Buttons = Packet.pkButtons;
|
|
return EASYTAB_OK;
|
|
}
|
|
|
|
return EASYTAB_EVENT_NOT_HANDLED;
|
|
}
|
|
|
|
void EasyTab_Unload()
|
|
{
|
|
if (EasyTab->Context) { EasyTab->WTClose(EasyTab->Context); }
|
|
if (EasyTab->Dll) { FreeLibrary(EasyTab->Dll); }
|
|
free(EasyTab);
|
|
EasyTab = NULL;
|
|
}
|
|
|
|
#endif // WIN32
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
#endif // EASYTAB_IMPLEMENTATION
|