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