9#define _USE_MATH_DEFINES
16 auto handle =
unique_hmodule(::LoadLibrary(
"Wintab32.dll"), &::FreeLibrary);
76 if (!info(WTI_DEFSYSCTX, 0, &lc)) {
81 extractCoordinates(lc, tablet, system);
92 const int maxQueue = 500;
95 int queueSize =
max(0, queueSizeGet(hctx.get()));
97 while (queueSize < maxQueue) {
98 int testSize =
min(queueSize + 16, maxQueue);
99 if (queueSizeSet(hctx.get(), testSize)) {
100 queueSize = testSize;
113 if (!queueSizeSet(hctx.get(), queueSize)) {
122 int sanityQueueSize = queueSizeGet(hctx.get());
123 WINTAB_PRINTF(
"HCTX %p %s queueSize: %d, queueSizeGet: %d\n",
131 return new GHOST_Wintab(std::move(handle),
144void GHOST_Wintab::modifyContext(LOGCONTEXT &lc)
149 lc.lcOptions |= CXO_CSRMESSAGES | CXO_MESSAGES;
153 lc.lcOutOrgX = lc.lcInOrgX;
154 lc.lcOutOrgY = lc.lcInOrgY;
155 lc.lcOutExtX = lc.lcInExtX;
156 lc.lcOutExtY = lc.lcInExtY;
159void GHOST_Wintab::extractCoordinates(LOGCONTEXT &lc, Coord &tablet, Coord &system)
161 tablet.x.org = lc.lcInOrgX;
162 tablet.x.ext = lc.lcInExtX;
163 tablet.y.org = lc.lcInOrgY;
164 tablet.y.ext = lc.lcInExtY;
166 system.x.org = lc.lcSysOrgX;
167 system.x.ext = lc.lcSysExtX;
168 system.y.org = lc.lcSysOrgY;
171 system.y.ext = -lc.lcSysExtY;
185 : m_handle{std::move(handle)},
189 m_fpPacketsGet{packetsGet},
191 m_fpOverlap{overlap},
192 m_context{std::move(hctx)},
193 m_tabletCoord{tablet},
194 m_systemCoord{system},
197 m_fpInfo(WTI_INTERFACE, IFC_NDEVICES, &m_numDevices);
203 printContextDebugInfo();
208 WINTAB_PRINTF(
"Closing Wintab context %p\n", m_context.get());
213 m_fpEnable(m_context.get(),
true);
222 m_fpEnable(m_context.get(),
false);
228 m_fpOverlap(m_context.get(),
true);
240 m_coordTrusted =
false;
242 m_fpOverlap(m_context.get(),
false);
253 m_fpPacketsGet(m_context.get(), m_pkts.size(), m_pkts.data());
260 if (m_fpInfo(WTI_DEFSYSCTX, 0, &lc)) {
261 extractCoordinates(lc, m_tabletCoord, m_systemCoord);
264 m_fpSet(m_context.get(), &lc);
272 BOOL pressureSupport = m_fpInfo(WTI_DEVICES, DVC_NPRESSURE, &
Pressure);
273 m_maxPressure = pressureSupport ?
Pressure.axMax : 0;
274 WINTAB_PRINTF(
"HCTX %p %s maxPressure: %d\n", m_context.get(), __func__, m_maxPressure);
276 BOOL tiltSupport = m_fpInfo(WTI_DEVICES, DVC_ORIENTATION, &Orientation);
278 if (tiltSupport && Orientation[0].axResolution && Orientation[1].axResolution) {
279 m_maxAzimuth = Orientation[0].axMax;
280 m_maxAltitude = Orientation[1].axMax;
283 m_maxAzimuth = m_maxAltitude = 0;
285 WINTAB_PRINTF(
"HCTX %p %s maxAzimuth: %d, maxAltitude: %d\n",
295 if (LOWORD(lParam) == WTI_INTERFACE && HIWORD(lParam) == IFC_NDEVICES) {
296 m_fpInfo(WTI_INTERFACE, IFC_NDEVICES, &m_numDevices);
297 WINTAB_PRINTF(
"HCTX %p %s numDevices: %d\n", m_context.get(), __func__, m_numDevices);
303 return m_numDevices > 0;
308 return m_lastTabletData;
313 const int numPackets = m_fpPacketsGet(m_context.get(), m_pkts.size(), m_pkts.data());
314 outWintabInfo.reserve(numPackets);
316 for (
int i = 0;
i < numPackets;
i++) {
317 const PACKET pkt = m_pkts[
i];
321 switch (pkt.pkCursor % 3) {
337 if (m_maxPressure > 0) {
338 out.tabletData.Pressure = float(pkt.pkNormalPressure) / float(m_maxPressure);
341 if ((m_maxAzimuth > 0) && (m_maxAltitude > 0)) {
355 ORIENTATION ort = pkt.pkOrientation;
358 float altRad = float((
fabs(
float(ort.orAltitude)) /
float(m_maxAltitude)) *
M_PI_2);
359 float azmRad = float((
float(ort.orAzimuth) /
float(m_maxAzimuth)) *
M_PI * 2.0);
362 float vecLen =
cos(altRad);
367 out.tabletData.Xtilt =
sin(azmRad) * vecLen;
370 out.tabletData.Ytilt = -float(
sin(
M_PI_2 - azmRad) * vecLen);
373 out.time = pkt.pkTime;
377 DWORD buttonsChanged = m_buttons ^ pkt.pkButtons;
379 m_buttons = pkt.pkButtons;
382 for (WORD buttonIndex = 0; buttonsChanged; buttonIndex++, buttonsChanged >>= 1) {
383 if (buttonsChanged & 1) {
384 GHOST_TButton button = mapWintabToGhostButton(pkt.pkCursor, buttonIndex);
389 outWintabInfo.push_back(
out);
394 DWORD buttonFlag = 1 << buttonIndex;
400 outWintabInfo.push_back(
out);
403 if (!outWintabInfo.empty()) {
404 m_lastTabletData = outWintabInfo.back().tabletData;
408GHOST_TButton GHOST_Wintab::mapWintabToGhostButton(
uint cursor, WORD physicalButton)
410 const WORD numButtons = 32;
411 BYTE logicalButtons[numButtons] = {0};
412 BYTE systemButtons[numButtons] = {0};
414 if (!m_fpInfo(WTI_CURSORS + cursor, CSR_BUTTONMAP, &logicalButtons) ||
415 !m_fpInfo(WTI_CURSORS + cursor, CSR_SYSBTNMAP, &systemButtons))
420 if (physicalButton >= numButtons) {
424 BYTE lb = logicalButtons[physicalButton];
426 if (lb >= numButtons) {
430 switch (systemButtons[lb]) {
446 auto remap = [](
int inPoint, Range
in, Range
out) ->
int {
447 int absInExt =
abs(
in.ext);
448 int absOutExt =
abs(
out.ext);
451 int inMagnitude = inPoint -
in.org;
454 if ((
in.ext < 0) != (
out.ext < 0)) {
455 inMagnitude = absInExt - inMagnitude;
459 int outMagnitude = inMagnitude * absOutExt / absInExt;
462 int outPoint = outMagnitude +
out.org;
467 x_out = remap(x_in, m_tabletCoord.x, m_systemCoord.x);
468 y_out = remap(y_in, m_tabletCoord.y, m_systemCoord.y);
473 return m_coordTrusted;
481 if (
abs(sysX - wtX) <= 1 &&
abs(sysY - wtY) <= 1) {
482 m_coordTrusted =
true;
486 m_coordTrusted =
false;
491bool GHOST_Wintab::m_debug =
false;
503void GHOST_Wintab::printContextDebugInfo()
510 BYTE logicalButtons[32] = {0};
511 BYTE systemButtons[32] = {0};
512 for (
int i = 0;
i < 3;
i++) {
513 printf(
"initializeWintab cursor %d buttons\n",
i);
514 uint lbut = m_fpInfo(WTI_CURSORS +
i, CSR_BUTTONMAP, &logicalButtons);
516 printf(
"%d", logicalButtons[0]);
517 for (
int j = 1; j < lbut; j++) {
518 printf(
", %d", logicalButtons[j]);
523 printf(
"logical button error\n");
525 uint sbut = m_fpInfo(WTI_CURSORS +
i, CSR_SYSBTNMAP, &systemButtons);
527 printf(
"%d", systemButtons[0]);
528 for (
int j = 1; j < sbut; j++) {
529 printf(
", %d", systemButtons[j]);
534 printf(
"system button error\n");
541 uint maxcontexts, opencontexts;
542 m_fpInfo(WTI_INTERFACE, IFC_NCONTEXTS, &maxcontexts);
543 m_fpInfo(WTI_STATUS, STA_CONTEXTS, &opencontexts);
544 printf(
"%u max contexts, %u open contexts\n", maxcontexts, opencontexts);
547 printf(
"left: %d, top: %d, width: %d, height: %d\n",
548 ::GetSystemMetrics(SM_XVIRTUALSCREEN),
549 ::GetSystemMetrics(SM_YVIRTUALSCREEN),
550 ::GetSystemMetrics(SM_CXVIRTUALSCREEN),
551 ::GetSystemMetrics(SM_CYVIRTUALSCREEN));
553 auto printContextRanges = [](LOGCONTEXT &lc) {
554 printf(
"lcInOrgX: %d, lcInOrgY: %d, lcInExtX: %d, lcInExtY: %d\n",
559 printf(
"lcOutOrgX: %d, lcOutOrgY: %d, lcOutExtX: %d, lcOutExtY: %d\n",
564 printf(
"lcSysOrgX: %d, lcSysOrgY: %d, lcSysExtX: %d, lcSysExtY: %d\n",
574 m_fpInfo(WTI_DEFSYSCTX, 0, &lc);
575 printf(
"WTI_DEFSYSCTX\n");
576 printContextRanges(lc);
579 m_fpInfo(WTI_DEFSYSCTX, CTX_INORGX, &lc.lcInOrgX);
580 m_fpInfo(WTI_DEFSYSCTX, CTX_INORGY, &lc.lcInOrgY);
581 m_fpInfo(WTI_DEFSYSCTX, CTX_INEXTX, &lc.lcInExtX);
582 m_fpInfo(WTI_DEFSYSCTX, CTX_INEXTY, &lc.lcInExtY);
583 m_fpInfo(WTI_DEFSYSCTX, CTX_OUTORGX, &lc.lcOutOrgX);
584 m_fpInfo(WTI_DEFSYSCTX, CTX_OUTORGY, &lc.lcOutOrgY);
585 m_fpInfo(WTI_DEFSYSCTX, CTX_OUTEXTX, &lc.lcOutExtX);
586 m_fpInfo(WTI_DEFSYSCTX, CTX_OUTEXTY, &lc.lcOutExtY);
587 m_fpInfo(WTI_DEFSYSCTX, CTX_SYSORGX, &lc.lcSysOrgX);
588 m_fpInfo(WTI_DEFSYSCTX, CTX_SYSORGY, &lc.lcSysOrgY);
589 m_fpInfo(WTI_DEFSYSCTX, CTX_SYSEXTX, &lc.lcSysExtX);
590 m_fpInfo(WTI_DEFSYSCTX, CTX_SYSEXTY, &lc.lcSysExtY);
591 printf(
"WTI_DEFSYSCTX CTX_*\n");
592 printContextRanges(lc);
594 for (
uint i = 0;
i < m_numDevices;
i++) {
596 m_fpInfo(WTI_DSCTXS +
i, 0, &lc);
598 printContextRanges(lc);
601 m_fpInfo(WTI_DSCTXS +
i, CTX_INORGX, &lc.lcInOrgX);
602 m_fpInfo(WTI_DSCTXS +
i, CTX_INORGY, &lc.lcInOrgY);
603 m_fpInfo(WTI_DSCTXS +
i, CTX_INEXTX, &lc.lcInExtX);
604 m_fpInfo(WTI_DSCTXS +
i, CTX_INEXTY, &lc.lcInExtY);
605 m_fpInfo(WTI_DSCTXS +
i, CTX_OUTORGX, &lc.lcOutOrgX);
606 m_fpInfo(WTI_DSCTXS +
i, CTX_OUTORGY, &lc.lcOutOrgY);
607 m_fpInfo(WTI_DSCTXS +
i, CTX_OUTEXTX, &lc.lcOutExtX);
608 m_fpInfo(WTI_DSCTXS +
i, CTX_OUTEXTY, &lc.lcOutExtY);
609 m_fpInfo(WTI_DSCTXS +
i, CTX_SYSORGX, &lc.lcSysOrgX);
610 m_fpInfo(WTI_DSCTXS +
i, CTX_SYSORGY, &lc.lcSysOrgY);
611 m_fpInfo(WTI_DSCTXS +
i, CTX_SYSEXTX, &lc.lcSysExtX);
612 m_fpInfo(WTI_DSCTXS +
i, CTX_SYSEXTY, &lc.lcSysExtY);
613 printf(
"WTI_DSCTX %u CTX_*\n",
i);
614 printContextRanges(lc);
618 m_fpInfo(WTI_DEVICES +
i, DVC_X, &axis_x);
619 m_fpInfo(WTI_DEVICES +
i, DVC_Y, &axis_y);
620 printf(
"WTI_DEVICES %u axis_x org: %d, axis_y org: %d axis_x ext: %d, axis_y ext: %d\n",
624 axis_x.axMax - axis_x.axMin + 1,
625 axis_y.axMax - axis_y.axMin + 1);
629 printf(
"sysmode %d\n", lc.lcSysMode);
static const GHOST_TabletData GHOST_TABLET_DATA_NONE
@ GHOST_kTabletModeEraser
@ GHOST_kTabletModeStylus
@ GHOST_kButtonMaskMiddle
BOOL(API * GHOST_WIN32_WTOverlap)(HCTX, BOOL)
BOOL(API * GHOST_WIN32_WTEnable)(HCTX, BOOL)
std::unique_ptr< std::remove_pointer_t< HCTX >, GHOST_WIN32_WTClose > unique_hctx
HCTX(API * GHOST_WIN32_WTOpen)(HWND, LPLOGCONTEXTA, BOOL)
BOOL(API * GHOST_WIN32_WTClose)(HCTX)
BOOL(API * GHOST_WIN32_WTGet)(HCTX, LPLOGCONTEXTA)
#define WINTAB_PRINTF(x,...)
std::unique_ptr< std::remove_pointer_t< HMODULE >, decltype(&::FreeLibrary)> unique_hmodule
BOOL(API * GHOST_WIN32_WTQueueSizeSet)(HCTX, int)
BOOL(API * GHOST_WIN32_WTSet)(HCTX, LPLOGCONTEXTA)
UINT(API * GHOST_WIN32_WTInfo)(UINT, UINT, LPVOID)
int(API * GHOST_WIN32_WTPacketsGet)(HCTX, int, LPVOID)
int(API * GHOST_WIN32_WTQueueSizeGet)(HCTX)
void mapWintabToSysCoordinates(int x_in, int y_in, int &x_out, int &y_out)
GHOST_TabletData getLastTabletData()
void getInput(std::vector< GHOST_WintabInfoWin32 > &outWintabInfo)
void processInfoChange(LPARAM lParam)
static GHOST_Wintab * loadWintab(HWND hwnd)
bool testCoordinates(int sysX, int sysY, int wtX, int wtY)
static void setDebug(bool debug)
ccl_device_inline float2 fabs(const float2 a)
std::shared_ptr< const T > get(const GenericKey &key, FunctionRef< std::unique_ptr< T >()> compute_fn)