Blender V5.0
GHOST_Wintab.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2021-2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#define _USE_MATH_DEFINES
10
11#include "GHOST_Wintab.hh"
12
13GHOST_Wintab *GHOST_Wintab::loadWintabUnsafe(HWND hwnd)
14{
15 /* Load Wintab library if available. */
16 auto handle = unique_hmodule(::LoadLibrary("Wintab32.dll"), &::FreeLibrary);
17 if (!handle) {
18 return nullptr;
19 }
20
21 /* Get Wintab functions. */
22
23 auto info = (GHOST_WIN32_WTInfo)::GetProcAddress(handle.get(), "WTInfoA");
24 if (!info) {
25 return nullptr;
26 }
27
28 auto open = (GHOST_WIN32_WTOpen)::GetProcAddress(handle.get(), "WTOpenA");
29 if (!open) {
30 return nullptr;
31 }
32
33 auto get = (GHOST_WIN32_WTGet)::GetProcAddress(handle.get(), "WTGetA");
34 if (!get) {
35 return nullptr;
36 }
37
38 auto set = (GHOST_WIN32_WTSet)::GetProcAddress(handle.get(), "WTSetA");
39 if (!set) {
40 return nullptr;
41 }
42
43 auto close = (GHOST_WIN32_WTClose)::GetProcAddress(handle.get(), "WTClose");
44 if (!close) {
45 return nullptr;
46 }
47
48 auto packetsGet = (GHOST_WIN32_WTPacketsGet)::GetProcAddress(handle.get(), "WTPacketsGet");
49 if (!packetsGet) {
50 return nullptr;
51 }
52
53 auto queueSizeGet = (GHOST_WIN32_WTQueueSizeGet)::GetProcAddress(handle.get(), "WTQueueSizeGet");
54 if (!queueSizeGet) {
55 return nullptr;
56 }
57
58 auto queueSizeSet = (GHOST_WIN32_WTQueueSizeSet)::GetProcAddress(handle.get(), "WTQueueSizeSet");
59 if (!queueSizeSet) {
60 return nullptr;
61 }
62
63 auto enable = (GHOST_WIN32_WTEnable)::GetProcAddress(handle.get(), "WTEnable");
64 if (!enable) {
65 return nullptr;
66 }
67
68 auto overlap = (GHOST_WIN32_WTOverlap)::GetProcAddress(handle.get(), "WTOverlap");
69 if (!overlap) {
70 return nullptr;
71 }
72
73 /* Build Wintab context. */
74
75 LOGCONTEXT lc = {0};
76 if (!info(WTI_DEFSYSCTX, 0, &lc)) {
77 return nullptr;
78 }
79
80 Coord tablet, system;
81 extractCoordinates(lc, tablet, system);
82 modifyContext(lc);
83
84 /* The Wintab spec says we must open the context disabled if we are using cursor masks. */
85 auto hctx = unique_hctx(open(hwnd, &lc, FALSE), close);
86 if (!hctx) {
87 return nullptr;
88 }
89
90 /* Wintab provides no way to determine the maximum queue size aside from checking if attempts
91 * to change the queue size are successful. */
92 const int maxQueue = 500;
93 /* < 0 should realistically never happen, but given we cast to size_t later on better safe than
94 * sorry. */
95 int queueSize = max(0, queueSizeGet(hctx.get()));
96
97 while (queueSize < maxQueue) {
98 int testSize = min(queueSize + 16, maxQueue);
99 if (queueSizeSet(hctx.get(), testSize)) {
100 queueSize = testSize;
101 }
102 else {
103 /* From Windows Wintab Documentation for WTQueueSizeSet:
104 * "If the return value is zero, the context has no queue because the function deletes the
105 * original queue before attempting to create a new one. The application must continue
106 * calling the function with a smaller queue size until the function returns a non - zero
107 * value."
108 *
109 * In our case we start with a known valid queue size and in the event of failure roll
110 * back to the last valid queue size. The Wintab spec dates back to 16 bit Windows, thus
111 * assumes memory recently deallocated may not be available, which is no longer a practical
112 * concern. */
113 if (!queueSizeSet(hctx.get(), queueSize)) {
114 /* If a previously valid queue size is no longer valid, there is likely something wrong in
115 * the Wintab implementation and we should not use it. */
116 return nullptr;
117 }
118 break;
119 }
120 }
121
122 int sanityQueueSize = queueSizeGet(hctx.get());
123 WINTAB_PRINTF("HCTX %p %s queueSize: %d, queueSizeGet: %d\n",
124 hctx.get(),
125 __func__,
126 queueSize,
127 sanityQueueSize);
128
129 WINTAB_PRINTF("Loaded Wintab context %p\n", hctx.get());
130
131 return new GHOST_Wintab(std::move(handle),
132 info,
133 get,
134 set,
135 packetsGet,
136 enable,
137 overlap,
138 std::move(hctx),
139 tablet,
140 system,
141 size_t(queueSize));
142}
143
144static int access_violation_exception_filter(unsigned int code, LPEXCEPTION_POINTERS pointers)
145{
146 if (code == EXCEPTION_ACCESS_VIOLATION) {
147 fprintf(stderr,
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;
153 }
154 return EXCEPTION_CONTINUE_SEARCH;
155}
156
157GHOST_Wintab *GHOST_Wintab::loadWintab(HWND hwnd)
158{
159 /* The only way to get the current handler is by seting a new one. */
160 LPTOP_LEVEL_EXCEPTION_FILTER current_filter = SetUnhandledExceptionFilter(nullptr);
161 SetUnhandledExceptionFilter(current_filter);
162
163 /* __except and __finally cannot be used together, as such a second nested __try block is needed.
164 */
165 __try
166 {
167 __try
168 {
169 return GHOST_Wintab::loadWintabUnsafe(hwnd);
170 }
171 __except (access_violation_exception_filter(GetExceptionCode(), GetExceptionInformation()))
172 {
173 }
174 }
175 __finally
176 {
177 /* Restore our handler in case the Wintab driver replaced it. Huion's driver is known to do
178 * this.
179 */
180 SetUnhandledExceptionFilter(current_filter);
181 }
182
183 return nullptr;
184}
185
186void GHOST_Wintab::modifyContext(LOGCONTEXT &lc)
187{
188 lc.lcPktData = PACKETDATA;
189 lc.lcPktMode = PACKETMODE;
190 lc.lcMoveMask = PACKETDATA;
191 lc.lcOptions |= CXO_CSRMESSAGES | CXO_MESSAGES;
192
193 /* Tablet scaling is handled manually because some drivers don't handle HIDPI or multi-display
194 * correctly; reset tablet scale factors to un-scaled tablet coordinates. */
195 lc.lcOutOrgX = lc.lcInOrgX;
196 lc.lcOutOrgY = lc.lcInOrgY;
197 lc.lcOutExtX = lc.lcInExtX;
198 lc.lcOutExtY = lc.lcInExtY;
199}
200
201void GHOST_Wintab::extractCoordinates(LOGCONTEXT &lc, Coord &tablet, Coord &system)
202{
203 tablet.x.org = lc.lcInOrgX;
204 tablet.x.ext = lc.lcInExtX;
205 tablet.y.org = lc.lcInOrgY;
206 tablet.y.ext = lc.lcInExtY;
207
208 system.x.org = lc.lcSysOrgX;
209 system.x.ext = lc.lcSysExtX;
210 system.y.org = lc.lcSysOrgY;
211 /* Wintab maps y origin to the tablet's bottom; invert y to match Windows y origin mapping to the
212 * screen top. */
213 system.y.ext = -lc.lcSysExtY;
214}
215
216GHOST_Wintab::GHOST_Wintab(unique_hmodule handle,
220 GHOST_WIN32_WTPacketsGet packetsGet,
222 GHOST_WIN32_WTOverlap overlap,
223 unique_hctx hctx,
224 Coord tablet,
225 Coord system,
226 size_t queueSize)
227 : handle_{std::move(handle)},
228 fp_info_{info},
229 fp_get_{get},
230 fp_set_{set},
231 fp_packets_get_{packetsGet},
232 fp_enable_{enable},
233 fp_overlap_{overlap},
234 context_{std::move(hctx)},
235 tablet_coord_{tablet},
236 system_coord_{system},
237 pkts_{queueSize}
238{
239 fp_info_(WTI_INTERFACE, IFC_NDEVICES, &num_devices_);
240 WINTAB_PRINTF("Wintab Devices: %d\n", num_devices_);
241
243
244 /* Debug info. */
245 printContextDebugInfo();
246}
247
249{
250 WINTAB_PRINTF("Closing Wintab context %p\n", context_.get());
251}
252
254{
255 fp_enable_(context_.get(), true);
256 enabled_ = true;
257}
258
260{
261 if (focused_) {
262 loseFocus();
263 }
264 fp_enable_(context_.get(), false);
265 enabled_ = false;
266}
267
269{
270 fp_overlap_(context_.get(), true);
271 focused_ = true;
272}
273
275{
276 if (last_tablet_data_.Active != GHOST_kTabletModeNone) {
277 leaveRange();
278 }
279
280 /* Mouse mode of tablet or display layout may change when Wintab or Window is inactive. Don't
281 * trust for mouse movement until re-verified. */
282 coord_trusted_ = false;
283
284 fp_overlap_(context_.get(), false);
285 focused_ = false;
286}
287
289{
290 /* Button state can't be tracked while out of range, reset it. */
291 buttons_ = 0;
292 /* Set to none to indicate tablet is inactive. */
293 last_tablet_data_ = GHOST_TABLET_DATA_NONE;
294 /* Clear the packet queue. */
295 fp_packets_get_(context_.get(), pkts_.size(), pkts_.data());
296}
297
299{
300 LOGCONTEXT lc = {0};
301
302 if (fp_info_(WTI_DEFSYSCTX, 0, &lc)) {
303 extractCoordinates(lc, tablet_coord_, system_coord_);
304 modifyContext(lc);
305
306 fp_set_(context_.get(), &lc);
307 }
308}
309
311{
312 AXIS Pressure, Orientation[3];
313
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_);
317
318 BOOL tiltSupport = fp_info_(WTI_DEVICES, DVC_ORIENTATION, &Orientation);
319 /* Check if tablet supports azimuth [0] and altitude [1], encoded in axResolution. */
320 if (tiltSupport && Orientation[0].axResolution && Orientation[1].axResolution) {
321 max_azimuth_ = Orientation[0].axMax;
322 max_altitude_ = Orientation[1].axMax;
323 }
324 else {
325 max_azimuth_ = max_altitude_ = 0;
326 }
327 WINTAB_PRINTF("HCTX %p %s maxAzimuth: %d, maxAltitude: %d\n",
328 context_.get(),
329 __func__,
330 max_azimuth_,
331 max_altitude_);
332}
333
335{
336 /* Update number of connected Wintab digitizers. */
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_);
340 }
341}
342
344{
345 return num_devices_ > 0;
346}
347
349{
350 return last_tablet_data_;
351}
352
353void GHOST_Wintab::getInput(std::vector<GHOST_WintabInfoWin32> &outWintabInfo)
354{
355 const int numPackets = fp_packets_get_(context_.get(), pkts_.size(), pkts_.data());
356 outWintabInfo.reserve(numPackets);
357
358 for (int i = 0; i < numPackets; i++) {
359 const PACKET pkt = pkts_[i];
361
362 /* % 3 for multiple devices ("DualTrack"). */
363 switch (pkt.pkCursor % 3) {
364 case 0:
365 /* Puck - processed as mouse. */
366 out.tabletData.Active = GHOST_kTabletModeNone;
367 break;
368 case 1:
369 out.tabletData.Active = GHOST_kTabletModeStylus;
370 break;
371 case 2:
372 out.tabletData.Active = GHOST_kTabletModeEraser;
373 break;
374 }
375
376 out.x = pkt.pkX;
377 out.y = pkt.pkY;
378
379 if (max_pressure_ > 0) {
380 out.tabletData.Pressure = float(pkt.pkNormalPressure) / float(max_pressure_);
381 }
382
383 if ((max_azimuth_ > 0) && (max_altitude_ > 0)) {
384 /* From the wintab spec:
385 * orAzimuth: Specifies the clockwise rotation of the cursor about the z axis through a
386 * full circular range.
387 * orAltitude: Specifies the angle with the x-y plane through a signed, semicircular range.
388 * Positive values specify an angle upward toward the positive z axis; negative values
389 * specify an angle downward toward the negative z axis.
390 *
391 * wintab.h defines orAltitude as a `uint` but documents orAltitude as positive for upward
392 * angles and negative for downward angles. WACOM uses negative altitude values to show that
393 * the pen is inverted; therefore we cast orAltitude as an `int` and then use the absolute
394 * value.
395 */
396
397 ORIENTATION ort = pkt.pkOrientation;
398
399 /* Convert raw fixed point data to radians. */
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);
402
403 /* Find length of the stylus' projected vector on the XY plane. */
404 float vecLen = cos(altRad);
405
406 /* From there calculate X and Y components based on azimuth. */
407
408 /* Blender expects: -1.0f (left) to +1.0f (right). */
409 out.tabletData.Xtilt = sin(azmRad) * vecLen;
410
411 /* Blender expects: -1.0f (away from user) to +1.0f (toward user). */
412 out.tabletData.Ytilt = -float(sin(M_PI_2 - azmRad) * vecLen);
413 }
414
415 out.time = pkt.pkTime;
416
417 /* Some Wintab libraries don't handle relative button input, so we track button presses
418 * manually. */
419 DWORD buttonsChanged = buttons_ ^ pkt.pkButtons;
420 /* We only needed the prior button state to compare to current, so we can overwrite it now. */
421 buttons_ = pkt.pkButtons;
422
423 /* Iterate over button flag indices until all flags are clear. */
424 for (WORD buttonIndex = 0; buttonsChanged; buttonIndex++, buttonsChanged >>= 1) {
425 if (buttonsChanged & 1) {
426 GHOST_TButton button = mapWintabToGhostButton(pkt.pkCursor, buttonIndex);
427
428 if (button != GHOST_kButtonMaskNone) {
429 /* If this is not the first button found, push info for the prior Wintab button. */
430 if (out.button != GHOST_kButtonMaskNone) {
431 outWintabInfo.push_back(out);
432 }
433
434 out.button = button;
435
436 DWORD buttonFlag = 1 << buttonIndex;
437 out.type = pkt.pkButtons & buttonFlag ? GHOST_kEventButtonDown : GHOST_kEventButtonUp;
438 }
439 }
440 }
441
442 outWintabInfo.push_back(out);
443 }
444
445 if (!outWintabInfo.empty()) {
446 last_tablet_data_ = outWintabInfo.back().tabletData;
447 }
448}
449
450GHOST_TButton GHOST_Wintab::mapWintabToGhostButton(uint cursor, WORD physicalButton)
451{
452 const WORD numButtons = 32;
453 BYTE logicalButtons[numButtons] = {0};
454 BYTE systemButtons[numButtons] = {0};
455
456 if (!fp_info_(WTI_CURSORS + cursor, CSR_BUTTONMAP, &logicalButtons) ||
457 !fp_info_(WTI_CURSORS + cursor, CSR_SYSBTNMAP, &systemButtons))
458 {
460 }
461
462 if (physicalButton >= numButtons) {
464 }
465
466 BYTE lb = logicalButtons[physicalButton];
467
468 if (lb >= numButtons) {
470 }
471
472 switch (systemButtons[lb]) {
473 case SBN_LCLICK:
475 case SBN_RCLICK:
477 case SBN_MCLICK:
479 default:
481 }
482}
483
484void GHOST_Wintab::mapWintabToSysCoordinates(int x_in, int y_in, int &x_out, int &y_out)
485{
486 /* Maps from range [in.org, in.org + abs(in.ext)] to [out.org, out.org + abs(out.ext)], in
487 * reverse if in.ext and out.ext have differing sign. */
488 auto remap = [](int inPoint, Range in, Range out) -> int {
489 int absInExt = abs(in.ext);
490 int absOutExt = abs(out.ext);
491
492 /* Translate input from range [in.org, in.org + absInExt] to [0, absInExt] */
493 int inMagnitude = inPoint - in.org;
494
495 /* If signs of extents differ, reverse input over range. */
496 if ((in.ext < 0) != (out.ext < 0)) {
497 inMagnitude = absInExt - inMagnitude;
498 }
499
500 /* Scale from [0, absInExt] to [0, absOutExt]. */
501 int outMagnitude = inMagnitude * absOutExt / absInExt;
502
503 /* Translate from range [0, absOutExt] to [out.org, out.org + absOutExt]. */
504 int outPoint = outMagnitude + out.org;
505
506 return outPoint;
507 };
508
509 x_out = remap(x_in, tablet_coord_.x, system_coord_.x);
510 y_out = remap(y_in, tablet_coord_.y, system_coord_.y);
511}
512
514{
515 return coord_trusted_;
516}
517
518bool GHOST_Wintab::testCoordinates(int sysX, int sysY, int wtX, int wtY)
519{
520 mapWintabToSysCoordinates(wtX, wtY, wtX, wtY);
521
522 /* Allow off by one pixel tolerance in case of rounding error. */
523 if (abs(sysX - wtX) <= 1 && abs(sysY - wtY) <= 1) {
524 coord_trusted_ = true;
525 return true;
526 }
527 else {
528 coord_trusted_ = false;
529 return false;
530 }
531}
532
533bool GHOST_Wintab::debug_ = false;
534
536{
537 debug_ = debug;
538}
539
541{
542 return debug_;
543}
544
545void GHOST_Wintab::printContextDebugInfo()
546{
547 if (!debug_) {
548 return;
549 }
550
551 /* Print button maps. */
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);
557 if (lbut) {
558 printf("%d", logicalButtons[0]);
559 for (int j = 1; j < lbut; j++) {
560 printf(", %d", logicalButtons[j]);
561 }
562 printf("\n");
563 }
564 else {
565 printf("logical button error\n");
566 }
567 uint sbut = fp_info_(WTI_CURSORS + i, CSR_SYSBTNMAP, &systemButtons);
568 if (sbut) {
569 printf("%d", systemButtons[0]);
570 for (int j = 1; j < sbut; j++) {
571 printf(", %d", systemButtons[j]);
572 }
573 printf("\n");
574 }
575 else {
576 printf("system button error\n");
577 }
578 }
579
580 /* Print context information. */
581
582 /* Print open context constraints. */
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);
587
588 /* Print system information. */
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));
594
595 auto printContextRanges = [](LOGCONTEXT &lc) {
596 printf("lcInOrgX: %d, lcInOrgY: %d, lcInExtX: %d, lcInExtY: %d\n",
597 lc.lcInOrgX,
598 lc.lcInOrgY,
599 lc.lcInExtX,
600 lc.lcInExtY);
601 printf("lcOutOrgX: %d, lcOutOrgY: %d, lcOutExtX: %d, lcOutExtY: %d\n",
602 lc.lcOutOrgX,
603 lc.lcOutOrgY,
604 lc.lcOutExtX,
605 lc.lcOutExtY);
606 printf("lcSysOrgX: %d, lcSysOrgY: %d, lcSysExtX: %d, lcSysExtY: %d\n",
607 lc.lcSysOrgX,
608 lc.lcSysOrgY,
609 lc.lcSysExtX,
610 lc.lcSysExtY);
611 };
612
613 LOGCONTEXT lc;
614
615 /* Print system context. */
616 fp_info_(WTI_DEFSYSCTX, 0, &lc);
617 printf("WTI_DEFSYSCTX\n");
618 printContextRanges(lc);
619
620 /* Print system context, manually populated. */
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);
635
636 for (uint i = 0; i < num_devices_; i++) {
637 /* Print individual device system context. */
638 fp_info_(WTI_DSCTXS + i, 0, &lc);
639 printf("WTI_DSCTXS %u\n", i);
640 printContextRanges(lc);
641
642 /* Print individual device system context, manually populated. */
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);
657
658 /* Print device axis. */
659 AXIS axis_x, axis_y;
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",
663 i,
664 axis_x.axMin,
665 axis_y.axMin,
666 axis_x.axMax - axis_x.axMin + 1,
667 axis_y.axMax - axis_y.axMin + 1);
668 }
669
670 /* Other stuff while we have a log-context. */
671 printf("sysmode %d\n", lc.lcSysMode);
672}
#define M_PI_2
#define M_PI
unsigned int uint
#define FALSE
@ GHOST_kEventButtonUp
@ GHOST_kEventButtonDown
static const GHOST_TabletData GHOST_TABLET_DATA_NONE
@ GHOST_kTabletModeEraser
@ GHOST_kTabletModeStylus
@ GHOST_kTabletModeNone
GHOST_TButton
@ GHOST_kButtonMaskRight
@ GHOST_kButtonMaskNone
@ GHOST_kButtonMaskLeft
@ 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)
#define PACKETMODE
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,...)
#define PACKETDATA
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()
bool devicesPresent()
void getInput(std::vector< GHOST_WintabInfoWin32 > &outWintabInfo)
void processInfoChange(LPARAM lParam)
void updateCursorInfo()
void remapCoordinates()
static GHOST_Wintab * loadWintab(HWND hwnd)
bool trustCoordinates()
static bool getDebug()
bool testCoordinates(int sysX, int sysY, int wtX, int wtY)
static void setDebug(bool debug)
nullptr float
#define in
#define out
#define printf(...)
#define sin
#define cos
#define abs
ccl_device_inline float2 fabs(const float2 a)
std::shared_ptr< const T > get(const GenericKey &key, FunctionRef< std::unique_ptr< T >()> compute_fn)
#define min(a, b)
Definition sort.cc:36
i
Definition text_draw.cc:230
max
Definition text_draw.cc:251