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",
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);
365 out.tabletData.Xtilt = sin(azmRad) * vecLen;
366 out.tabletData.Ytilt =
float(sin(
M_PI_2 - azmRad) * vecLen);
369 out.time = pkt.pkTime;
373 DWORD buttonsChanged = m_buttons ^ pkt.pkButtons;
375 m_buttons = pkt.pkButtons;
378 for (WORD buttonIndex = 0; buttonsChanged; buttonIndex++, buttonsChanged >>= 1) {
379 if (buttonsChanged & 1) {
380 GHOST_TButton button = mapWintabToGhostButton(pkt.pkCursor, buttonIndex);
385 outWintabInfo.push_back(out);
390 DWORD buttonFlag = 1 << buttonIndex;
396 outWintabInfo.push_back(out);
399 if (!outWintabInfo.empty()) {
400 m_lastTabletData = outWintabInfo.back().tabletData;
404GHOST_TButton GHOST_Wintab::mapWintabToGhostButton(
uint cursor, WORD physicalButton)
406 const WORD numButtons = 32;
407 BYTE logicalButtons[numButtons] = {0};
408 BYTE systemButtons[numButtons] = {0};
410 if (!m_fpInfo(WTI_CURSORS + cursor, CSR_BUTTONMAP, &logicalButtons) ||
411 !m_fpInfo(WTI_CURSORS + cursor, CSR_SYSBTNMAP, &systemButtons))
416 if (physicalButton >= numButtons) {
420 BYTE lb = logicalButtons[physicalButton];
422 if (lb >= numButtons) {
426 switch (systemButtons[lb]) {
442 auto remap = [](
int inPoint, Range in, Range out) ->
int {
443 int absInExt =
abs(in.ext);
444 int absOutExt =
abs(out.ext);
447 int inMagnitude = inPoint - in.org;
450 if ((in.ext < 0) != (out.ext < 0)) {
451 inMagnitude = absInExt - inMagnitude;
455 int outMagnitude = inMagnitude * absOutExt / absInExt;
458 int outPoint = outMagnitude + out.org;
463 x_out = remap(x_in, m_tabletCoord.x, m_systemCoord.x);
464 y_out = remap(y_in, m_tabletCoord.y, m_systemCoord.y);
469 return m_coordTrusted;
477 if (
abs(sysX - wtX) <= 1 &&
abs(sysY - wtY) <= 1) {
478 m_coordTrusted =
true;
482 m_coordTrusted =
false;
487bool GHOST_Wintab::m_debug =
false;
499void GHOST_Wintab::printContextDebugInfo()
506 BYTE logicalButtons[32] = {0};
507 BYTE systemButtons[32] = {0};
508 for (
int i = 0; i < 3; i++) {
509 printf(
"initializeWintab cursor %d buttons\n", i);
510 uint lbut = m_fpInfo(WTI_CURSORS + i, CSR_BUTTONMAP, &logicalButtons);
512 printf(
"%d", logicalButtons[0]);
513 for (
int j = 1; j < lbut; j++) {
514 printf(
", %d", logicalButtons[j]);
519 printf(
"logical button error\n");
521 uint sbut = m_fpInfo(WTI_CURSORS + i, CSR_SYSBTNMAP, &systemButtons);
523 printf(
"%d", systemButtons[0]);
524 for (
int j = 1; j < sbut; j++) {
525 printf(
", %d", systemButtons[j]);
530 printf(
"system button error\n");
537 uint maxcontexts, opencontexts;
538 m_fpInfo(WTI_INTERFACE, IFC_NCONTEXTS, &maxcontexts);
539 m_fpInfo(WTI_STATUS, STA_CONTEXTS, &opencontexts);
540 printf(
"%u max contexts, %u open contexts\n", maxcontexts, opencontexts);
543 printf(
"left: %d, top: %d, width: %d, height: %d\n",
544 ::GetSystemMetrics(SM_XVIRTUALSCREEN),
545 ::GetSystemMetrics(SM_YVIRTUALSCREEN),
546 ::GetSystemMetrics(SM_CXVIRTUALSCREEN),
547 ::GetSystemMetrics(SM_CYVIRTUALSCREEN));
549 auto printContextRanges = [](LOGCONTEXT &lc) {
550 printf(
"lcInOrgX: %d, lcInOrgY: %d, lcInExtX: %d, lcInExtY: %d\n",
555 printf(
"lcOutOrgX: %d, lcOutOrgY: %d, lcOutExtX: %d, lcOutExtY: %d\n",
560 printf(
"lcSysOrgX: %d, lcSysOrgY: %d, lcSysExtX: %d, lcSysExtY: %d\n",
570 m_fpInfo(WTI_DEFSYSCTX, 0, &lc);
571 printf(
"WTI_DEFSYSCTX\n");
572 printContextRanges(lc);
575 m_fpInfo(WTI_DEFSYSCTX, CTX_INORGX, &lc.lcInOrgX);
576 m_fpInfo(WTI_DEFSYSCTX, CTX_INORGY, &lc.lcInOrgY);
577 m_fpInfo(WTI_DEFSYSCTX, CTX_INEXTX, &lc.lcInExtX);
578 m_fpInfo(WTI_DEFSYSCTX, CTX_INEXTY, &lc.lcInExtY);
579 m_fpInfo(WTI_DEFSYSCTX, CTX_OUTORGX, &lc.lcOutOrgX);
580 m_fpInfo(WTI_DEFSYSCTX, CTX_OUTORGY, &lc.lcOutOrgY);
581 m_fpInfo(WTI_DEFSYSCTX, CTX_OUTEXTX, &lc.lcOutExtX);
582 m_fpInfo(WTI_DEFSYSCTX, CTX_OUTEXTY, &lc.lcOutExtY);
583 m_fpInfo(WTI_DEFSYSCTX, CTX_SYSORGX, &lc.lcSysOrgX);
584 m_fpInfo(WTI_DEFSYSCTX, CTX_SYSORGY, &lc.lcSysOrgY);
585 m_fpInfo(WTI_DEFSYSCTX, CTX_SYSEXTX, &lc.lcSysExtX);
586 m_fpInfo(WTI_DEFSYSCTX, CTX_SYSEXTY, &lc.lcSysExtY);
587 printf(
"WTI_DEFSYSCTX CTX_*\n");
588 printContextRanges(lc);
590 for (
uint i = 0; i < m_numDevices; i++) {
592 m_fpInfo(WTI_DSCTXS + i, 0, &lc);
593 printf(
"WTI_DSCTXS %u\n", i);
594 printContextRanges(lc);
597 m_fpInfo(WTI_DSCTXS + i, CTX_INORGX, &lc.lcInOrgX);
598 m_fpInfo(WTI_DSCTXS + i, CTX_INORGY, &lc.lcInOrgY);
599 m_fpInfo(WTI_DSCTXS + i, CTX_INEXTX, &lc.lcInExtX);
600 m_fpInfo(WTI_DSCTXS + i, CTX_INEXTY, &lc.lcInExtY);
601 m_fpInfo(WTI_DSCTXS + i, CTX_OUTORGX, &lc.lcOutOrgX);
602 m_fpInfo(WTI_DSCTXS + i, CTX_OUTORGY, &lc.lcOutOrgY);
603 m_fpInfo(WTI_DSCTXS + i, CTX_OUTEXTX, &lc.lcOutExtX);
604 m_fpInfo(WTI_DSCTXS + i, CTX_OUTEXTY, &lc.lcOutExtY);
605 m_fpInfo(WTI_DSCTXS + i, CTX_SYSORGX, &lc.lcSysOrgX);
606 m_fpInfo(WTI_DSCTXS + i, CTX_SYSORGY, &lc.lcSysOrgY);
607 m_fpInfo(WTI_DSCTXS + i, CTX_SYSEXTX, &lc.lcSysExtX);
608 m_fpInfo(WTI_DSCTXS + i, CTX_SYSEXTY, &lc.lcSysExtY);
609 printf(
"WTI_DSCTX %u CTX_*\n", i);
610 printContextRanges(lc);
614 m_fpInfo(WTI_DEVICES + i, DVC_X, &axis_x);
615 m_fpInfo(WTI_DEVICES + i, DVC_Y, &axis_y);
616 printf(
"WTI_DEVICES %u axis_x org: %d, axis_y org: %d axis_x ext: %d, axis_y ext: %d\n",
620 axis_x.axMax - axis_x.axMin + 1,
621 axis_y.axMax - axis_y.axMin + 1);
625 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)
draw_view in_light_buf[] float
ccl_device_inline float2 fabs(const float2 a)
ccl_device_inline float3 cos(float3 v)
std::shared_ptr< const T > get(const GenericKey &key, FunctionRef< std::unique_ptr< T >()> compute_fn)
GHOST_TabletData tabletData
ccl_device_inline int abs(int x)