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",
146 if (code == EXCEPTION_ACCESS_VIOLATION) {
148 "Error loading Wintab library: Access Violation at 0x%p: 0x%p, 0x%p\n",
149 pointers->ExceptionRecord->ExceptionAddress,
150 (
void *)pointers->ExceptionRecord->ExceptionInformation[0],
151 (
void *)pointers->ExceptionRecord->ExceptionInformation[1]);
152 return EXCEPTION_EXECUTE_HANDLER;
154 return EXCEPTION_CONTINUE_SEARCH;
160 LPTOP_LEVEL_EXCEPTION_FILTER current_filter = SetUnhandledExceptionFilter(
nullptr);
161 SetUnhandledExceptionFilter(current_filter);
169 return GHOST_Wintab::loadWintabUnsafe(hwnd);
180 SetUnhandledExceptionFilter(current_filter);
186void GHOST_Wintab::modifyContext(LOGCONTEXT &lc)
191 lc.lcOptions |= CXO_CSRMESSAGES | CXO_MESSAGES;
195 lc.lcOutOrgX = lc.lcInOrgX;
196 lc.lcOutOrgY = lc.lcInOrgY;
197 lc.lcOutExtX = lc.lcInExtX;
198 lc.lcOutExtY = lc.lcInExtY;
201void GHOST_Wintab::extractCoordinates(LOGCONTEXT &lc, Coord &tablet, Coord &system)
203 tablet.x.org = lc.lcInOrgX;
204 tablet.x.ext = lc.lcInExtX;
205 tablet.y.org = lc.lcInOrgY;
206 tablet.y.ext = lc.lcInExtY;
208 system.x.org = lc.lcSysOrgX;
209 system.x.ext = lc.lcSysExtX;
210 system.y.org = lc.lcSysOrgY;
213 system.y.ext = -lc.lcSysExtY;
227 : handle_{std::move(handle)},
231 fp_packets_get_{packetsGet},
233 fp_overlap_{overlap},
234 context_{std::move(hctx)},
235 tablet_coord_{tablet},
236 system_coord_{system},
239 fp_info_(WTI_INTERFACE, IFC_NDEVICES, &num_devices_);
245 printContextDebugInfo();
250 WINTAB_PRINTF(
"Closing Wintab context %p\n", context_.get());
255 fp_enable_(context_.get(),
true);
264 fp_enable_(context_.get(),
false);
270 fp_overlap_(context_.get(),
true);
282 coord_trusted_ =
false;
284 fp_overlap_(context_.get(),
false);
295 fp_packets_get_(context_.get(), pkts_.size(), pkts_.data());
302 if (fp_info_(WTI_DEFSYSCTX, 0, &lc)) {
303 extractCoordinates(lc, tablet_coord_, system_coord_);
306 fp_set_(context_.get(), &lc);
314 BOOL pressureSupport = fp_info_(WTI_DEVICES, DVC_NPRESSURE, &
Pressure);
315 max_pressure_ = pressureSupport ?
Pressure.axMax : 0;
316 WINTAB_PRINTF(
"HCTX %p %s maxPressure: %d\n", context_.get(), __func__, max_pressure_);
318 BOOL tiltSupport = fp_info_(WTI_DEVICES, DVC_ORIENTATION, &Orientation);
320 if (tiltSupport && Orientation[0].axResolution && Orientation[1].axResolution) {
321 max_azimuth_ = Orientation[0].axMax;
322 max_altitude_ = Orientation[1].axMax;
325 max_azimuth_ = max_altitude_ = 0;
327 WINTAB_PRINTF(
"HCTX %p %s maxAzimuth: %d, maxAltitude: %d\n",
337 if (LOWORD(lParam) == WTI_INTERFACE && HIWORD(lParam) == IFC_NDEVICES) {
338 fp_info_(WTI_INTERFACE, IFC_NDEVICES, &num_devices_);
339 WINTAB_PRINTF(
"HCTX %p %s numDevices: %d\n", context_.get(), __func__, num_devices_);
345 return num_devices_ > 0;
350 return last_tablet_data_;
355 const int numPackets = fp_packets_get_(context_.get(), pkts_.size(), pkts_.data());
356 outWintabInfo.reserve(numPackets);
358 for (
int i = 0;
i < numPackets;
i++) {
359 const PACKET pkt = pkts_[
i];
363 switch (pkt.pkCursor % 3) {
379 if (max_pressure_ > 0) {
380 out.tabletData.Pressure =
float(pkt.pkNormalPressure) /
float(max_pressure_);
383 if ((max_azimuth_ > 0) && (max_altitude_ > 0)) {
397 ORIENTATION ort = pkt.pkOrientation;
400 float altRad =
float((
fabs(
float(ort.orAltitude)) /
float(max_altitude_)) *
M_PI_2);
401 float azmRad =
float((
float(ort.orAzimuth) /
float(max_azimuth_)) *
M_PI * 2.0);
404 float vecLen =
cos(altRad);
409 out.tabletData.Xtilt =
sin(azmRad) * vecLen;
415 out.time = pkt.pkTime;
419 DWORD buttonsChanged = buttons_ ^ pkt.pkButtons;
421 buttons_ = pkt.pkButtons;
424 for (WORD buttonIndex = 0; buttonsChanged; buttonIndex++, buttonsChanged >>= 1) {
425 if (buttonsChanged & 1) {
426 GHOST_TButton button = mapWintabToGhostButton(pkt.pkCursor, buttonIndex);
431 outWintabInfo.push_back(
out);
436 DWORD buttonFlag = 1 << buttonIndex;
442 outWintabInfo.push_back(
out);
445 if (!outWintabInfo.empty()) {
446 last_tablet_data_ = outWintabInfo.back().tabletData;
450GHOST_TButton GHOST_Wintab::mapWintabToGhostButton(
uint cursor, WORD physicalButton)
452 const WORD numButtons = 32;
453 BYTE logicalButtons[numButtons] = {0};
454 BYTE systemButtons[numButtons] = {0};
456 if (!fp_info_(WTI_CURSORS + cursor, CSR_BUTTONMAP, &logicalButtons) ||
457 !fp_info_(WTI_CURSORS + cursor, CSR_SYSBTNMAP, &systemButtons))
462 if (physicalButton >= numButtons) {
466 BYTE lb = logicalButtons[physicalButton];
468 if (lb >= numButtons) {
472 switch (systemButtons[lb]) {
488 auto remap = [](
int inPoint, Range
in, Range
out) ->
int {
489 int absInExt =
abs(
in.ext);
490 int absOutExt =
abs(
out.ext);
493 int inMagnitude = inPoint -
in.org;
496 if ((
in.ext < 0) != (
out.ext < 0)) {
497 inMagnitude = absInExt - inMagnitude;
501 int outMagnitude = inMagnitude * absOutExt / absInExt;
504 int outPoint = outMagnitude +
out.org;
509 x_out = remap(x_in, tablet_coord_.x, system_coord_.x);
510 y_out = remap(y_in, tablet_coord_.y, system_coord_.y);
515 return coord_trusted_;
523 if (
abs(sysX - wtX) <= 1 &&
abs(sysY - wtY) <= 1) {
524 coord_trusted_ =
true;
528 coord_trusted_ =
false;
533bool GHOST_Wintab::debug_ =
false;
545void GHOST_Wintab::printContextDebugInfo()
552 BYTE logicalButtons[32] = {0};
553 BYTE systemButtons[32] = {0};
554 for (
int i = 0;
i < 3;
i++) {
555 printf(
"initializeWintab cursor %d buttons\n",
i);
556 uint lbut = fp_info_(WTI_CURSORS +
i, CSR_BUTTONMAP, &logicalButtons);
558 printf(
"%d", logicalButtons[0]);
559 for (
int j = 1; j < lbut; j++) {
560 printf(
", %d", logicalButtons[j]);
565 printf(
"logical button error\n");
567 uint sbut = fp_info_(WTI_CURSORS +
i, CSR_SYSBTNMAP, &systemButtons);
569 printf(
"%d", systemButtons[0]);
570 for (
int j = 1; j < sbut; j++) {
571 printf(
", %d", systemButtons[j]);
576 printf(
"system button error\n");
583 uint maxcontexts, opencontexts;
584 fp_info_(WTI_INTERFACE, IFC_NCONTEXTS, &maxcontexts);
585 fp_info_(WTI_STATUS, STA_CONTEXTS, &opencontexts);
586 printf(
"%u max contexts, %u open contexts\n", maxcontexts, opencontexts);
589 printf(
"left: %d, top: %d, width: %d, height: %d\n",
590 ::GetSystemMetrics(SM_XVIRTUALSCREEN),
591 ::GetSystemMetrics(SM_YVIRTUALSCREEN),
592 ::GetSystemMetrics(SM_CXVIRTUALSCREEN),
593 ::GetSystemMetrics(SM_CYVIRTUALSCREEN));
595 auto printContextRanges = [](LOGCONTEXT &lc) {
596 printf(
"lcInOrgX: %d, lcInOrgY: %d, lcInExtX: %d, lcInExtY: %d\n",
601 printf(
"lcOutOrgX: %d, lcOutOrgY: %d, lcOutExtX: %d, lcOutExtY: %d\n",
606 printf(
"lcSysOrgX: %d, lcSysOrgY: %d, lcSysExtX: %d, lcSysExtY: %d\n",
616 fp_info_(WTI_DEFSYSCTX, 0, &lc);
617 printf(
"WTI_DEFSYSCTX\n");
618 printContextRanges(lc);
621 fp_info_(WTI_DEFSYSCTX, CTX_INORGX, &lc.lcInOrgX);
622 fp_info_(WTI_DEFSYSCTX, CTX_INORGY, &lc.lcInOrgY);
623 fp_info_(WTI_DEFSYSCTX, CTX_INEXTX, &lc.lcInExtX);
624 fp_info_(WTI_DEFSYSCTX, CTX_INEXTY, &lc.lcInExtY);
625 fp_info_(WTI_DEFSYSCTX, CTX_OUTORGX, &lc.lcOutOrgX);
626 fp_info_(WTI_DEFSYSCTX, CTX_OUTORGY, &lc.lcOutOrgY);
627 fp_info_(WTI_DEFSYSCTX, CTX_OUTEXTX, &lc.lcOutExtX);
628 fp_info_(WTI_DEFSYSCTX, CTX_OUTEXTY, &lc.lcOutExtY);
629 fp_info_(WTI_DEFSYSCTX, CTX_SYSORGX, &lc.lcSysOrgX);
630 fp_info_(WTI_DEFSYSCTX, CTX_SYSORGY, &lc.lcSysOrgY);
631 fp_info_(WTI_DEFSYSCTX, CTX_SYSEXTX, &lc.lcSysExtX);
632 fp_info_(WTI_DEFSYSCTX, CTX_SYSEXTY, &lc.lcSysExtY);
633 printf(
"WTI_DEFSYSCTX CTX_*\n");
634 printContextRanges(lc);
636 for (
uint i = 0;
i < num_devices_;
i++) {
638 fp_info_(WTI_DSCTXS +
i, 0, &lc);
640 printContextRanges(lc);
643 fp_info_(WTI_DSCTXS +
i, CTX_INORGX, &lc.lcInOrgX);
644 fp_info_(WTI_DSCTXS +
i, CTX_INORGY, &lc.lcInOrgY);
645 fp_info_(WTI_DSCTXS +
i, CTX_INEXTX, &lc.lcInExtX);
646 fp_info_(WTI_DSCTXS +
i, CTX_INEXTY, &lc.lcInExtY);
647 fp_info_(WTI_DSCTXS +
i, CTX_OUTORGX, &lc.lcOutOrgX);
648 fp_info_(WTI_DSCTXS +
i, CTX_OUTORGY, &lc.lcOutOrgY);
649 fp_info_(WTI_DSCTXS +
i, CTX_OUTEXTX, &lc.lcOutExtX);
650 fp_info_(WTI_DSCTXS +
i, CTX_OUTEXTY, &lc.lcOutExtY);
651 fp_info_(WTI_DSCTXS +
i, CTX_SYSORGX, &lc.lcSysOrgX);
652 fp_info_(WTI_DSCTXS +
i, CTX_SYSORGY, &lc.lcSysOrgY);
653 fp_info_(WTI_DSCTXS +
i, CTX_SYSEXTX, &lc.lcSysExtX);
654 fp_info_(WTI_DSCTXS +
i, CTX_SYSEXTY, &lc.lcSysExtY);
655 printf(
"WTI_DSCTX %u CTX_*\n",
i);
656 printContextRanges(lc);
660 fp_info_(WTI_DEVICES +
i, DVC_X, &axis_x);
661 fp_info_(WTI_DEVICES +
i, DVC_Y, &axis_y);
662 printf(
"WTI_DEVICES %u axis_x org: %d, axis_y org: %d axis_x ext: %d, axis_y ext: %d\n",
666 axis_x.axMax - axis_x.axMin + 1,
667 axis_y.axMax - axis_y.axMin + 1);
671 printf(
"sysmode %d\n", lc.lcSysMode);
static const GHOST_TabletData GHOST_TABLET_DATA_NONE
@ GHOST_kTabletModeEraser
@ GHOST_kTabletModeStylus
@ GHOST_kButtonMaskMiddle
static int access_violation_exception_filter(unsigned int code, LPEXCEPTION_POINTERS pointers)
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)