From ef0eec2721e8085ebcf9a93b57622b74386bf2f2 Mon Sep 17 00:00:00 2001 From: yaw-man Date: Thu, 22 Sep 2022 16:55:25 -0500 Subject: [PATCH] Factoring out tablet API stuff --- lib/rasa/CMakeLists.txt | 27 ++++++++ lib/rasa/cs.cpp | 25 +++++++ lib/rasa/readme.txt | 3 + lib/rasa/tablet.h | 46 +++++++++++++ lib/rasa/wintab.cpp | 81 +++++++++++++++++++++++ lib/rasa/wtutil.cpp | 143 ++++++++++++++++++++++++++++++++++++++++ lib/rasa/wtutil.h | 91 +++++++++++++++++++++++++ 7 files changed, 416 insertions(+) create mode 100644 lib/rasa/CMakeLists.txt create mode 100644 lib/rasa/cs.cpp create mode 100644 lib/rasa/readme.txt create mode 100644 lib/rasa/tablet.h create mode 100644 lib/rasa/wintab.cpp create mode 100644 lib/rasa/wtutil.cpp create mode 100644 lib/rasa/wtutil.h diff --git a/lib/rasa/CMakeLists.txt b/lib/rasa/CMakeLists.txt new file mode 100644 index 0000000..609b386 --- /dev/null +++ b/lib/rasa/CMakeLists.txt @@ -0,0 +1,27 @@ +option(TAB_WINTAB true) +option(TAB_OTD false) + +if(TAB_WINTAB) +add_library(rasa STATIC + wintab.cpp + wtutil.cpp + ) +target_compile_definitions(rasa PUBLIC TAB_WINTAB) +target_include_directories(rasa + "." + "../wintab") +endif() + +if(TAB_OTD) +add_library(rasa STATIC + cs.cpp ) +target_compile_definitions(rasa PUBLIC TAB_OTD) +target_include_directories(rasa + "." + "../otd") +# default compiler flags from CMAKE conflict with managed code compliation. +# probably a better way of doing this. +set_target_properties(rasa PROPERTIES VS_DOTNET_REFERENCES "System") +set_target_properties(rasa PROPERTIES COMPILE_FLAGS "/clr") +set_target_properties(rasa PROPERTIES COMPILE_FLAGS "/Zc:twoPhase-") +endif() \ No newline at end of file diff --git a/lib/rasa/cs.cpp b/lib/rasa/cs.cpp new file mode 100644 index 0000000..5cd1d53 --- /dev/null +++ b/lib/rasa/cs.cpp @@ -0,0 +1,25 @@ +//File for C# <-> C++/CLI interface + +#ifdef YAW_USE_OTD +// text_write.cpp +// compile with: /clr +using namespace System; +using namespace System::IO; + +int func() +{ + String^ fileName = "textfile.txt"; + + StreamWriter^ sw = gcnew StreamWriter(fileName); + sw->WriteLine("A text file is born!"); + sw->Write("You can use WriteLine"); + sw->WriteLine("...or just Write"); + sw->WriteLine("and do {0} output too.", "formatted"); + sw->WriteLine("You can also send non-text objects:"); + sw->WriteLine(DateTime::Now); + sw->Close(); + Console::WriteLine("a new file ('{0}') has been written", fileName); + + return 0; +} +#endif \ No newline at end of file diff --git a/lib/rasa/readme.txt b/lib/rasa/readme.txt new file mode 100644 index 0000000..928f442 --- /dev/null +++ b/lib/rasa/readme.txt @@ -0,0 +1,3 @@ +Minimal tablet API. +Just the bare minimum for polling for x, y, z, and normal pressure. +Compile time switches determine whether to use OpenTabletDriver (C# interface required) or Wintab (Win32 platform required) diff --git a/lib/rasa/tablet.h b/lib/rasa/tablet.h new file mode 100644 index 0000000..c6e23b1 --- /dev/null +++ b/lib/rasa/tablet.h @@ -0,0 +1,46 @@ +//Interface to supported tablet APIs. +#include +#include + +typedef unsigned long ButtonMask; + +#ifdef TAB_WINTAB +#include +#include "MSGPACK.H" +#include "wintab.h" +typedef unsigned long ButtonMask; +#endif + + +struct Packet { + float x; + float y; + float z; + float p; + ButtonMask buttons; + bool operator==(const Packet& p) + { return + this->x == p.x && + this->y == p.y && + this->z == p.z && + this->p == p.p && + this->buttons == p.buttons; + }; + bool operator!=(const Packet& p) { return !(*this == p); } +}; + +class Tablet { +public: + Tablet(uintptr_t handle); + ~Tablet(); + bool GetPacket( Packet &pkt ); + bool initialized; + std::string errormsg = ""; + Packet ext = { 0 }; + +private: +#ifdef TAB_WINTAB + void NewContext(HWND hwnd); + HCTX hctx = NULL; +#endif +}; \ No newline at end of file diff --git a/lib/rasa/wintab.cpp b/lib/rasa/wintab.cpp new file mode 100644 index 0000000..c905c4c --- /dev/null +++ b/lib/rasa/wintab.cpp @@ -0,0 +1,81 @@ +#ifdef TAB_WINTAB +#include "tablet.h" +#define PACKETDATA (PK_X | PK_Y | PK_Z | PK_BUTTONS | PK_NORMAL_PRESSURE) +//#define PACKETMODE +#include "pktdef.h" +#include "wtutil.h" + +Tablet::Tablet(uintptr_t handle) +{ + HWND hwnd = reinterpret_cast(handle); + if (!hwnd) { return; } + + if (!LoadWintab() || !gpWTInfoA(0, 0, NULL)) { + errormsg = "Wintab not installed."; + return; + } + + NewContext(hwnd); +} + +Tablet::~Tablet() { + if (hctx) { gpWTClose(hctx); } + UnloadWintab(); +} + +bool Tablet::GetPacket( Packet& packet ) { + //Serial number of newest packet. + UINT oldest, newest; + //This function returns false when it fails + //which may happen with a full or empty queue. + if (!gpWTQueuePacketsEx(hctx, &oldest, &newest)) { + //Queue may be full, flush it all just in case. + gpWTPacketsGet(hctx, gpWTQueueSizeGet(hctx), nullptr); + return false; + } + + //Store newest packet in pkt, flush older packets. + PACKET pkt; + bool newData = gpWTPacket(hctx, newest, &pkt); + if (!newData) return false; + packet.x = static_cast(pkt.pkX) / ext.x; + packet.y = static_cast(pkt.pkY) / ext.y; + packet.z = static_cast(pkt.pkZ) / ext.z; + packet.p = static_cast(pkt.pkNormalPressure) / ext.p; + packet.buttons = pkt.pkButtons; + return true; +} + +void Tablet::NewContext(HWND hwnd) { + if (hctx) { gpWTClose(hctx); } + LOGCONTEXT ctx = {}; + AXIS TabletX = { 0 }; + AXIS TabletY = { 0 }; + AXIS TabletZ = { 0 }; + AXIS TabletPressure = { 0 }; + gpWTInfoA(WTI_DEFCONTEXT, 0, &ctx); + ctx.lcOptions |= CXO_MESSAGES; //TODO: checker �ela + ctx.lcPktData = PACKETDATA; + ctx.lcPktMode = 0; + + //Tablet extents. + gpWTInfoA(WTI_DEVICES, DVC_X, &TabletX); + gpWTInfoA(WTI_DEVICES, DVC_Y, &TabletY); + gpWTInfoA(WTI_DEVICES, DVC_Z, &TabletZ); + gpWTInfoA(WTI_DEVICES, DVC_NPRESSURE, &TabletPressure); + + ext.x = static_cast(TabletX.axMax); + ext.y = static_cast(TabletY.axMax); + ext.z = static_cast(TabletZ.axMax); + ext.p = static_cast(TabletPressure.axMax); + + hctx = gpWTOpenA(hwnd, &ctx, TRUE); + + if (!hctx) + { + errormsg = "Could not open Wintab context."; + return; + } + initialized = true; +}; +#endif \ No newline at end of file diff --git a/lib/rasa/wtutil.cpp b/lib/rasa/wtutil.cpp new file mode 100644 index 0000000..ac921f1 --- /dev/null +++ b/lib/rasa/wtutil.cpp @@ -0,0 +1,143 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// DESCRIPTION +// Some general-purpose functions for the WinTab demos. +// +// COPYRIGHT +// Copyright (c) 2014-2020 Wacom Co., Ltd. +// All rights reserved. +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include "wtutil.h" + +////////////////////////////////////////////////////////////////////////////// + +HINSTANCE ghWintab = nullptr; + +WTINFOA gpWTInfoA = nullptr; +WTOPENA gpWTOpenA = nullptr; +WTGETA gpWTGetA = nullptr; +WTSETA gpWTSetA = nullptr; +WTCLOSE gpWTClose = nullptr; +WTPACKET gpWTPacket = nullptr; +WTENABLE gpWTEnable = nullptr; +WTOVERLAP gpWTOverlap = nullptr; +WTSAVE gpWTSave = nullptr; +WTCONFIG gpWTConfig = nullptr; +WTRESTORE gpWTRestore = nullptr; +WTEXTSET gpWTExtSet = nullptr; +WTEXTGET gpWTExtGet = nullptr; +WTQUEUESIZESET gpWTQueueSizeSet = nullptr; +WTDATAPEEK gpWTDataPeek = nullptr; +WTPACKETSGET gpWTPacketsGet = nullptr; +WTMGROPEN gpWTMgrOpen = nullptr; +WTMGRCLOSE gpWTMgrClose = nullptr; +WTMGRDEFCONTEXT gpWTMgrDefContext = nullptr; +WTMGRDEFCONTEXTEX gpWTMgrDefContextEx = nullptr; + +// TODO - add more wintab32 function pointers as needed +WTQPACKETSEX gpWTQueuePacketsEx = nullptr; +WTQSIZEGET gpWTQueueSizeGet = nullptr; + +////////////////////////////////////////////////////////////////////////////// +// Purpose +// Find wintab32.dll and load it. +// Find the exported functions we need from it. +// +// Returns +// TRUE on success. +// FALSE on failure. +// +BOOL LoadWintab(void) +{ + // load the wintab32 dll + ghWintab = LoadLibraryA("Wintab32.dll"); + + if (!ghWintab) + { + const DWORD err = GetLastError(); + ShowError("Could not load Wintab32.dll: " + std::to_string(err)); + return FALSE; + } + // Explicitly find the exported Wintab functions in which we are interested. + // We are using the ASCII, not unicode versions (where applicable). + gpWTOpenA = (WTOPENA)GetProcAddress(ghWintab, "WTOpenA"); + gpWTInfoA = (WTINFOA)GetProcAddress(ghWintab, "WTInfoA"); + gpWTGetA = (WTGETA)GetProcAddress(ghWintab, "WTGetA"); + gpWTSetA = (WTSETA)GetProcAddress(ghWintab, "WTSetA"); + gpWTPacket = (WTPACKET)GetProcAddress(ghWintab, "WTPacket"); + gpWTClose = (WTCLOSE)GetProcAddress(ghWintab, "WTClose"); + gpWTEnable = (WTENABLE)GetProcAddress(ghWintab, "WTEnable"); + gpWTOverlap = (WTOVERLAP)GetProcAddress(ghWintab, "WTOverlap"); + gpWTSave = (WTSAVE)GetProcAddress(ghWintab, "WTSave"); + gpWTConfig = (WTCONFIG)GetProcAddress(ghWintab, "WTConfig"); + gpWTRestore = (WTRESTORE)GetProcAddress(ghWintab, "WTRestore"); + gpWTExtSet = (WTEXTSET)GetProcAddress(ghWintab, "WTExtSet"); + gpWTExtGet = (WTEXTGET)GetProcAddress(ghWintab, "WTExtGet"); + gpWTQueueSizeSet = (WTQUEUESIZESET)GetProcAddress(ghWintab, "WTQueueSizeSet"); + gpWTDataPeek = (WTDATAPEEK)GetProcAddress(ghWintab, "WTDataPeek"); + gpWTPacketsGet = (WTPACKETSGET)GetProcAddress(ghWintab, "WTPacketsGet"); + gpWTMgrOpen = (WTMGROPEN)GetProcAddress(ghWintab, "WTMgrOpen"); + gpWTMgrClose = (WTMGRCLOSE)GetProcAddress(ghWintab, "WTMgrClose"); + gpWTMgrDefContext = (WTMGRDEFCONTEXT)GetProcAddress(ghWintab, "WTMgrDefContext"); + gpWTMgrDefContextEx = (WTMGRDEFCONTEXTEX)GetProcAddress(ghWintab, "WTMgrDefContextEx"); + gpWTQueuePacketsEx = (WTQPACKETSEX)GetProcAddress(ghWintab, "WTQueuePacketsEx"); + gpWTQueueSizeGet = (WTQSIZEGET)GetProcAddress(ghWintab, "WTQueueSizeGet"); + + // TODO - don't forget to NULL out pointers in UnloadWintab(). + return TRUE; +} + +////////////////////////////////////////////////////////////////////////////// +// Purpose +// Uninitializes use of wintab32.dll +// +// Returns +// Nothing. +// +void UnloadWintab(void) +{ + if (ghWintab) + { + FreeLibrary(ghWintab); + ghWintab = nullptr; + } + gpWTOpenA = nullptr; + gpWTClose = nullptr; + gpWTInfoA = nullptr; + gpWTPacket = nullptr; + gpWTEnable = nullptr; + gpWTOverlap = nullptr; + gpWTSave = nullptr; + gpWTConfig = nullptr; + gpWTGetA = nullptr; + gpWTSetA = nullptr; + gpWTRestore = nullptr; + gpWTExtSet = nullptr; + gpWTExtGet = nullptr; + gpWTQueueSizeSet = nullptr; + gpWTDataPeek = nullptr; + gpWTPacketsGet = nullptr; + gpWTMgrOpen = nullptr; + gpWTMgrClose = nullptr; + gpWTMgrDefContext = nullptr; + gpWTMgrDefContextEx = nullptr; + gpWTQueuePacketsEx = nullptr; + gpWTQueueSizeGet = nullptr; +} + +////////////////////////////////////////////////////////////////////////////// +// Purpose +// Display error to user. +// +void ShowError(const std::string &pszErrorMessage_I) +{ + //TODO: post error message to buffer in Tablet class + //which in turn can post to TabUI + //MessageBoxA(NULL, pszErrorMessage_I.c_str(), "Error", MB_OK | MB_ICONERROR); +} + +////////////////////////////////////////////////////////////////////////////// + diff --git a/lib/rasa/wtutil.h b/lib/rasa/wtutil.h new file mode 100644 index 0000000..190d0aa --- /dev/null +++ b/lib/rasa/wtutil.h @@ -0,0 +1,91 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// DESCRIPTION +// Defines for the general-purpose functions for the WinTab demos. +// +// COPYRIGHT +// Copyright (c) 2014-2020 Wacom Co., Ltd. +// All rights reserved. +// +/////////////////////////////////////////////////////////////////////////////// + +#pragma once + + +#include +#include +#include +#include + +#include // NOTE: get from wactab header package + +////////////////////////////////////////////////////////////////////////////// +// Ignore warnings about using unsafe string functions. +#pragma warning( disable : 4996 ) + +////////////////////////////////////////////////////////////////////////////// +// Function pointers to Wintab functions exported from wintab32.dll. +using WTINFOA = UINT (API*)(UINT, UINT, LPVOID); +using WTOPENA = HCTX (API*)(HWND, LPLOGCONTEXTA, BOOL); +using WTGETA = BOOL (API*)(HCTX, LPLOGCONTEXT); +using WTSETA = BOOL (API*)(HCTX, LPLOGCONTEXT); +using WTCLOSE = BOOL (API*)(HCTX); +using WTENABLE = BOOL (API*)(HCTX, BOOL); +using WTPACKET = BOOL (API*)(HCTX, UINT, LPVOID); +using WTOVERLAP = BOOL (API*)(HCTX, BOOL); +using WTSAVE = BOOL (API*)(HCTX, LPVOID); +using WTCONFIG = BOOL (API*)(HCTX, HWND); +using WTRESTORE = HCTX (API*)(HWND, LPVOID, BOOL); +using WTEXTSET = BOOL (API*)(HCTX, UINT, LPVOID); +using WTEXTGET = BOOL (API*)(HCTX, UINT, LPVOID); +using WTQUEUESIZESET = BOOL (API*)(HCTX, int); +using WTDATAPEEK = int (API*)(HCTX, UINT, UINT, int, LPVOID, LPINT); +using WTPACKETSGET = int (API*)(HCTX, int, LPVOID); +using WTMGROPEN = HMGR (API*)(HWND, UINT); +using WTMGRCLOSE = BOOL (API*)(HMGR); +using WTMGRDEFCONTEXT = HCTX (API*)(HMGR, BOOL); +using WTMGRDEFCONTEXTEX = HCTX (API*)(HMGR, UINT, BOOL); + +// TODO - add more wintab32 function defs as needed +using WTQPACKETSEX = BOOL(API*)(HCTX, UINT FAR*, UINT FAR*); +using WTQSIZEGET = int (API*)(HCTX); + +////////////////////////////////////////////////////////////////////////////// + +// Loaded Wintab32 API functions. +extern HINSTANCE ghWintab; + +extern WTINFOA gpWTInfoA; +extern WTOPENA gpWTOpenA; +extern WTGETA gpWTGetA; +extern WTSETA gpWTSetA; +extern WTCLOSE gpWTClose; +extern WTPACKET gpWTPacket; +extern WTENABLE gpWTEnable; +extern WTOVERLAP gpWTOverlap; +extern WTSAVE gpWTSave; +extern WTCONFIG gpWTConfig; +extern WTRESTORE gpWTRestore; +extern WTEXTSET gpWTExtSet; +extern WTEXTGET gpWTExtGet; +extern WTQUEUESIZESET gpWTQueueSizeSet; +extern WTDATAPEEK gpWTDataPeek; +extern WTPACKETSGET gpWTPacketsGet; +extern WTMGROPEN gpWTMgrOpen; +extern WTMGRCLOSE gpWTMgrClose; +extern WTMGRDEFCONTEXT gpWTMgrDefContext; +extern WTMGRDEFCONTEXTEX gpWTMgrDefContextEx; + +// TODO - add more wintab32 function pointers as needed +extern WTQPACKETSEX gpWTQueuePacketsEx; +extern WTQSIZEGET gpWTQueueSizeGet; + +////////////////////////////////////////////////////////////////////////////// + +BOOL LoadWintab(void); +void UnloadWintab(void); + +void ShowError(const std::string &pszErrorMessage_I); + +////////////////////////////////////////////////////////////////////////////// +