Files
workinf_Blender_Wasm/blender-5.2.0/intern/ghost/intern/GHOST_Wintab.hh
2026-08-12 04:47:48 -04:00

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();
};