286 lines
8.3 KiB
C++
286 lines
8.3 KiB
C++
/* SPDX-FileCopyrightText: 2021-2023 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup GHOST
|
|
* Declaration of GHOST_WintabWin32 class.
|
|
*/
|
|
|
|
/* Wacom's Wintab documentation is periodically offline, moved, and increasingly hidden away. You
|
|
* can find a (painstakingly) archived copy of the documentation at
|
|
* https://web.archive.org/web/20201122230125/https://developer-docs-legacy.wacom.com/display/DevDocs/Windows+Wintab+Documentation
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <memory>
|
|
#include <stdio.h>
|
|
#include <vector>
|
|
#include <wtypes.h>
|
|
|
|
#include "GHOST_Types.hh"
|
|
|
|
#include <wintab.h>
|
|
/* PACKETDATA and PACKETMODE modify structs in pktdef.h, so make sure they come first. */
|
|
#define PACKETDATA \
|
|
(PK_BUTTONS | PK_NORMAL_PRESSURE | PK_ORIENTATION | PK_CURSOR | PK_X | PK_Y | PK_TIME)
|
|
#define PACKETMODE 0
|
|
#include <pktdef.h>
|
|
|
|
#if !defined(_MSVC_TRADITIONAL) || _MSVC_TRADITIONAL
|
|
# define WINTAB_PRINTF(x, ...) \
|
|
{ \
|
|
if (GHOST_Wintab::getDebug()) { \
|
|
printf(x, __VA_ARGS__); \
|
|
} \
|
|
} \
|
|
(void)0
|
|
#else
|
|
# define WINTAB_PRINTF(x, ...) \
|
|
{ \
|
|
if (GHOST_Wintab::getDebug()) { \
|
|
printf(x, ##__VA_ARGS__); \
|
|
} \
|
|
} \
|
|
(void)0
|
|
#endif
|
|
|
|
/* Typedefs for Wintab functions to allow dynamic loading. */
|
|
typedef UINT(API *GHOST_WIN32_WTInfo)(UINT, UINT, LPVOID);
|
|
typedef BOOL(API *GHOST_WIN32_WTGet)(HCTX, LPLOGCONTEXTA);
|
|
typedef BOOL(API *GHOST_WIN32_WTSet)(HCTX, LPLOGCONTEXTA);
|
|
typedef HCTX(API *GHOST_WIN32_WTOpen)(HWND, LPLOGCONTEXTA, BOOL);
|
|
typedef BOOL(API *GHOST_WIN32_WTClose)(HCTX);
|
|
typedef int(API *GHOST_WIN32_WTPacketsGet)(HCTX, int, LPVOID);
|
|
typedef int(API *GHOST_WIN32_WTQueueSizeGet)(HCTX);
|
|
typedef BOOL(API *GHOST_WIN32_WTQueueSizeSet)(HCTX, int);
|
|
typedef BOOL(API *GHOST_WIN32_WTEnable)(HCTX, BOOL);
|
|
typedef BOOL(API *GHOST_WIN32_WTOverlap)(HCTX, BOOL);
|
|
|
|
/* Typedefs for Wintab and Windows resource management. */
|
|
typedef std::unique_ptr<std::remove_pointer_t<HMODULE>, decltype(&::FreeLibrary)> unique_hmodule;
|
|
typedef std::unique_ptr<std::remove_pointer_t<HCTX>, GHOST_WIN32_WTClose> unique_hctx;
|
|
|
|
struct GHOST_WintabInfoWin32 {
|
|
int32_t x = 0;
|
|
int32_t y = 0;
|
|
GHOST_TEventType type = GHOST_kEventCursorMove;
|
|
GHOST_TButton button = GHOST_kButtonMaskNone;
|
|
uint64_t time = 0;
|
|
GHOST_TabletData tabletData = GHOST_TABLET_DATA_NONE;
|
|
};
|
|
|
|
class GHOST_Wintab {
|
|
public:
|
|
/**
|
|
* Loads Wintab if available.
|
|
* \param hwnd: Window to attach Wintab context to.
|
|
* \return Pointer to the initialized GHOST_Wintab object, or null if initialization failed.
|
|
*/
|
|
static GHOST_Wintab *loadWintab(HWND hwnd);
|
|
|
|
~GHOST_Wintab();
|
|
|
|
/**
|
|
* Enables Wintab context.
|
|
*/
|
|
void enable();
|
|
|
|
/**
|
|
* Disables the Wintab context and unwinds Wintab state.
|
|
*/
|
|
void disable();
|
|
|
|
/**
|
|
* Brings Wintab context to the top of the overlap order.
|
|
*/
|
|
void gainFocus();
|
|
|
|
/**
|
|
* Puts Wintab context at bottom of overlap order and unwinds Wintab state.
|
|
*/
|
|
void loseFocus();
|
|
|
|
/**
|
|
* Clean up when Wintab leaves tracking range.
|
|
*/
|
|
void leaveRange();
|
|
|
|
/**
|
|
* Handle Wintab coordinate changes when DisplayChange events occur.
|
|
*/
|
|
void remapCoordinates();
|
|
|
|
/**
|
|
* Maps Wintab to Win32 display coordinates.
|
|
* \param x_in: The tablet x coordinate.
|
|
* \param y_in: The tablet y coordinate.
|
|
* \param x_out: Output for the Win32 mapped x coordinate.
|
|
* \param y_out: Output for the Win32 mapped y coordinate.
|
|
*/
|
|
void mapWintabToSysCoordinates(int x_in, int y_in, int &x_out, int &y_out);
|
|
|
|
/**
|
|
* Updates cached Wintab properties for current cursor.
|
|
*/
|
|
void updateCursorInfo();
|
|
|
|
/**
|
|
* Handle Wintab info changes such as change in number of connected tablets.
|
|
* \param lParam: LPARAM of the event.
|
|
*/
|
|
void processInfoChange(LPARAM lParam);
|
|
|
|
/**
|
|
* Whether Wintab devices are present.
|
|
* \return True if Wintab devices are present.
|
|
*/
|
|
bool devicesPresent();
|
|
|
|
/**
|
|
* Translate Wintab packets into GHOST_WintabInfoWin32 structs.
|
|
* \param outWintabInfo: Storage to return resulting GHOST_WintabInfoWin32 data.
|
|
*/
|
|
void getInput(std::vector<GHOST_WintabInfoWin32> &outWintabInfo);
|
|
|
|
/**
|
|
* Whether Wintab coordinates should be trusted.
|
|
* \return True if Wintab coordinates should be trusted.
|
|
*/
|
|
bool trustCoordinates();
|
|
|
|
/**
|
|
* Tests whether Wintab coordinates can be trusted by comparing Win32 and Wintab reported cursor
|
|
* position.
|
|
* \param sysX: System cursor x position.
|
|
* \param sysY: System cursor y position.
|
|
* \param wtX: Wintab cursor x position.
|
|
* \param wtY: Wintab cursor y position.
|
|
* \return True if Win32 and Wintab cursor positions match within tolerance.
|
|
*
|
|
* NOTE: Only test coordinates on button press, not release.
|
|
* This prevents issues when asynchronous mismatch causes mouse movement to replay
|
|
* and snap back, which is only an issue while drawing.
|
|
*/
|
|
bool testCoordinates(int sysX, int sysY, int wtX, int wtY);
|
|
|
|
/**
|
|
* Retrieve the most recent tablet data, or none if pen is not in range.
|
|
* \return Most recent tablet data, or none if pen is not in range.
|
|
*/
|
|
GHOST_TabletData getLastTabletData();
|
|
|
|
/**
|
|
* Sets Wintab debugging.
|
|
* \param debug: True to enable Wintab debugging.
|
|
*/
|
|
static void setDebug(bool debug);
|
|
|
|
/**
|
|
* Returns whether Wintab logging should occur.
|
|
* \return True if Wintab logging should occur.
|
|
*/
|
|
static bool getDebug();
|
|
|
|
private:
|
|
/** Wintab DLL handle. */
|
|
unique_hmodule handle_;
|
|
/** Wintab API functions. */
|
|
GHOST_WIN32_WTInfo fp_info_ = nullptr;
|
|
[[maybe_unused]] GHOST_WIN32_WTGet fp_get_ = nullptr;
|
|
GHOST_WIN32_WTSet fp_set_ = nullptr;
|
|
GHOST_WIN32_WTPacketsGet fp_packets_get_ = nullptr;
|
|
GHOST_WIN32_WTEnable fp_enable_ = nullptr;
|
|
GHOST_WIN32_WTOverlap fp_overlap_ = nullptr;
|
|
|
|
/** Stores the Wintab tablet context. */
|
|
unique_hctx context_;
|
|
/** Whether the context is enabled. */
|
|
bool enabled_ = false;
|
|
/** Whether the context has focus and is at the top of overlap order. */
|
|
bool focused_ = false;
|
|
|
|
/** Pressed button map. */
|
|
DWORD buttons_ = 0;
|
|
|
|
/** Range of a coordinate space. */
|
|
struct Range {
|
|
/** Origin of range. */
|
|
int org = 0;
|
|
/** Extent of range. */
|
|
int ext = 1;
|
|
};
|
|
|
|
/** 2D Coordinate space. */
|
|
struct Coord {
|
|
/** Range of x. */
|
|
Range x = {};
|
|
/** Range of y. */
|
|
Range y = {};
|
|
};
|
|
/** Whether Wintab coordinates are trusted. */
|
|
bool coord_trusted_ = false;
|
|
/** Tablet input range. */
|
|
Coord tablet_coord_ = {};
|
|
/** System output range. */
|
|
Coord system_coord_ = {};
|
|
|
|
int max_pressure_ = 0;
|
|
int max_azimuth_ = 0;
|
|
int max_altitude_ = 0;
|
|
|
|
/** Number of connected Wintab devices. */
|
|
UINT num_devices_ = 0;
|
|
/** Reusable buffer to read in Wintab packets. */
|
|
std::vector<PACKET> pkts_;
|
|
/** Most recently received tablet data, or none if pen is not in range. */
|
|
GHOST_TabletData last_tablet_data_ = GHOST_TABLET_DATA_NONE;
|
|
|
|
/** Whether Wintab logging is enabled. */
|
|
static bool debug_;
|
|
|
|
GHOST_Wintab(unique_hmodule handle,
|
|
GHOST_WIN32_WTInfo info,
|
|
GHOST_WIN32_WTGet get,
|
|
GHOST_WIN32_WTSet set,
|
|
GHOST_WIN32_WTPacketsGet packetsGet,
|
|
GHOST_WIN32_WTEnable enable,
|
|
GHOST_WIN32_WTOverlap overlap,
|
|
unique_hctx hctx,
|
|
Coord tablet,
|
|
Coord system,
|
|
size_t queueSize);
|
|
|
|
/**
|
|
* Convert Wintab system mapped (mouse) buttons into Ghost button mask.
|
|
* \param cursor: The Wintab cursor associated to the button.
|
|
* \param physicalButton: The physical button ID to inspect.
|
|
* \return The system mapped button.
|
|
*/
|
|
GHOST_TButton mapWintabToGhostButton(UINT cursor, WORD physicalButton);
|
|
|
|
/**
|
|
* Called by loadWintab to do the actual loading. A separate function is needed because the __try
|
|
* __except block in loadWintab cannot be combined with C++ stack variable destructors.
|
|
*/
|
|
static GHOST_Wintab *loadWintabUnsafe(HWND hwnd);
|
|
|
|
/**
|
|
* Applies common modifications to Wintab context.
|
|
* \param lc: Wintab context to modify.
|
|
*/
|
|
static void modifyContext(LOGCONTEXT &lc);
|
|
|
|
/**
|
|
* Extracts tablet and system coordinates from Wintab context.
|
|
* \param lc: Wintab context to extract coordinates from.
|
|
* \param tablet: Tablet coordinates.
|
|
* \param system: System coordinates.
|
|
*/
|
|
static void extractCoordinates(LOGCONTEXT &lc, Coord &tablet, Coord &system);
|
|
|
|
/* Prints Wintab Context information. */
|
|
void printContextDebugInfo();
|
|
};
|