[Windows] Add support for the WinTab API for pen input.
This commit is contained in:
parent
1c23a0cc7f
commit
d978658f81
|
@ -708,7 +708,7 @@ String InputEventMouseMotion::as_text() const {
|
|||
button_mask_string = itos(get_button_mask());
|
||||
break;
|
||||
}
|
||||
return "InputEventMouseMotion : button_mask=" + button_mask_string + ", position=(" + String(get_position()) + "), relative=(" + String(get_relative()) + "), speed=(" + String(get_speed()) + ")";
|
||||
return "InputEventMouseMotion : button_mask=" + button_mask_string + ", position=(" + String(get_position()) + "), relative=(" + String(get_relative()) + "), speed=(" + String(get_speed()) + "), pressure=(" + rtos(get_pressure()) + "), tilt=(" + String(get_tilt()) + ")";
|
||||
}
|
||||
|
||||
bool InputEventMouseMotion::accumulate(const Ref<InputEvent> &p_event) {
|
||||
|
|
|
@ -59,6 +59,7 @@ class OS {
|
|||
bool _allow_layered;
|
||||
bool _use_vsync;
|
||||
bool _vsync_via_compositor;
|
||||
bool _disable_wintab;
|
||||
|
||||
char *last_error;
|
||||
|
||||
|
@ -148,6 +149,7 @@ public:
|
|||
|
||||
bool is_layered_allowed() const { return _allow_layered; }
|
||||
bool is_hidpi_allowed() const { return _allow_hidpi; }
|
||||
bool is_wintab_disabled() const { return _disable_wintab; }
|
||||
|
||||
void ensure_user_data_dir();
|
||||
|
||||
|
|
|
@ -414,6 +414,9 @@
|
|||
<member name="display/mouse_cursor/tooltip_position_offset" type="Vector2" setter="" getter="" default="Vector2( 10, 10 )">
|
||||
Position offset for tooltips, relative to the mouse cursor's hotspot.
|
||||
</member>
|
||||
<member name="display/window/disable_wintab_api" type="bool" setter="" getter="" default="false">
|
||||
Disables WinTab API and always use Windows Ink API for the pen input (Windows only).
|
||||
</member>
|
||||
<member name="display/window/dpi/allow_hidpi" type="bool" setter="" getter="" default="false">
|
||||
If [code]true[/code], allows HiDPI display on Windows and macOS. This setting has no effect on desktop Linux, as DPI-awareness fallbacks are not supported there.
|
||||
</member>
|
||||
|
|
|
@ -139,6 +139,7 @@ static DisplayServer::ScreenOrientation window_orientation = DisplayServer::SCRE
|
|||
static uint32_t window_flags = 0;
|
||||
static Size2i window_size = Size2i(1024, 600);
|
||||
static bool window_vsync_via_compositor = false;
|
||||
static bool disable_wintab = false;
|
||||
|
||||
static int init_screen = -1;
|
||||
static bool init_fullscreen = false;
|
||||
|
@ -314,6 +315,7 @@ void Main::print_help(const char *p_binary) {
|
|||
OS::get_singleton()->print(" --enable-vsync-via-compositor When vsync is enabled, vsync via the OS' window compositor (Windows only).\n");
|
||||
OS::get_singleton()->print(" --disable-vsync-via-compositor Disable vsync via the OS' window compositor (Windows only).\n");
|
||||
OS::get_singleton()->print(" --single-window Use a single window (no separate subwindows).\n");
|
||||
OS::get_singleton()->print(" --disable-wintab Disable WinTab API and always use Windows Ink API for the pen input (Windows only).\n");
|
||||
OS::get_singleton()->print("\n");
|
||||
#endif
|
||||
|
||||
|
@ -595,6 +597,9 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
|
|||
} else if (I->get() == "--gpu-abort") { // force windowed window
|
||||
|
||||
Engine::singleton->abort_on_gpu_errors = true;
|
||||
} else if (I->get() == "--disable-wintab") {
|
||||
|
||||
disable_wintab = true;
|
||||
} else if (I->get() == "--single-window") { // force single window
|
||||
|
||||
single_window = true;
|
||||
|
@ -1079,6 +1084,13 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
|
|||
|
||||
OS::get_singleton()->_vsync_via_compositor = window_vsync_via_compositor;
|
||||
|
||||
if (!disable_wintab) {
|
||||
// No "--disable_wintab" option
|
||||
disable_wintab = GLOBAL_DEF("display/window/disable_wintab_api", false);
|
||||
}
|
||||
|
||||
OS::get_singleton()->_disable_wintab = disable_wintab;
|
||||
|
||||
/* todo restore
|
||||
OS::get_singleton()->_allow_layered = GLOBAL_DEF("display/window/per_pixel_transparency/allowed", false);
|
||||
video_mode.layered = GLOBAL_DEF("display/window/per_pixel_transparency/enabled", false);
|
||||
|
|
|
@ -36,10 +36,6 @@
|
|||
|
||||
#include <avrt.h>
|
||||
|
||||
#ifndef WM_POINTERUPDATE
|
||||
#define WM_POINTERUPDATE 0x0245
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
static String format_error_message(DWORD id) {
|
||||
|
||||
|
@ -545,6 +541,10 @@ void DisplayServerWindows::delete_sub_window(WindowID p_window) {
|
|||
}
|
||||
#endif
|
||||
|
||||
if (!OS::get_singleton()->is_wintab_disabled() && wintab_available && windows[p_window].wtctx) {
|
||||
wintab_WTClose(windows[p_window].wtctx);
|
||||
windows[p_window].wtctx = 0;
|
||||
}
|
||||
DestroyWindow(windows[p_window].hWnd);
|
||||
windows.erase(p_window);
|
||||
}
|
||||
|
@ -1849,6 +1849,10 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
|
|||
alt_mem = false;
|
||||
};
|
||||
|
||||
if (!OS::get_singleton()->is_wintab_disabled() && wintab_available && windows[window_id].wtctx) {
|
||||
wintab_WTEnable(windows[window_id].wtctx, GET_WM_ACTIVATE_STATE(wParam, lParam));
|
||||
}
|
||||
|
||||
return 0; // Return To The Message Loop
|
||||
}
|
||||
case WM_GETMINMAXINFO: {
|
||||
|
@ -1928,6 +1932,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
|
|||
mm->set_control(control_mem);
|
||||
mm->set_shift(shift_mem);
|
||||
mm->set_alt(alt_mem);
|
||||
|
||||
mm->set_pressure((raw->data.mouse.ulButtons & RI_MOUSE_LEFT_BUTTON_DOWN) ? 1.0f : 0.0f);
|
||||
|
||||
mm->set_button_mask(last_button_state);
|
||||
|
@ -1978,6 +1983,42 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
|
|||
}
|
||||
delete[] lpb;
|
||||
} break;
|
||||
case WT_CSRCHANGE:
|
||||
case WT_PROXIMITY: {
|
||||
if (!OS::get_singleton()->is_wintab_disabled() && wintab_available && windows[window_id].wtctx) {
|
||||
AXIS pressure;
|
||||
if (wintab_WTInfo(WTI_DEVICES + windows[window_id].wtlc.lcDevice, DVC_NPRESSURE, &pressure)) {
|
||||
windows[window_id].min_pressure = int(pressure.axMin);
|
||||
windows[window_id].max_pressure = int(pressure.axMax);
|
||||
}
|
||||
AXIS orientation[3];
|
||||
if (wintab_WTInfo(WTI_DEVICES + windows[window_id].wtlc.lcDevice, DVC_ORIENTATION, &orientation)) {
|
||||
windows[window_id].tilt_supported = orientation[0].axResolution && orientation[1].axResolution;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} break;
|
||||
case WT_PACKET: {
|
||||
if (!OS::get_singleton()->is_wintab_disabled() && wintab_available && windows[window_id].wtctx) {
|
||||
PACKET packet;
|
||||
if (wintab_WTPacket(windows[window_id].wtctx, wParam, &packet)) {
|
||||
|
||||
float pressure = float(packet.pkNormalPressure - windows[window_id].min_pressure) / float(windows[window_id].max_pressure - windows[window_id].min_pressure);
|
||||
windows[window_id].last_pressure = pressure;
|
||||
windows[window_id].last_pressure_update = 0;
|
||||
|
||||
double azim = (packet.pkOrientation.orAzimuth / 10.0f) * (Math_PI / 180);
|
||||
double alt = Math::tan((Math::abs(packet.pkOrientation.orAltitude / 10.0f)) * (Math_PI / 180));
|
||||
|
||||
if (windows[window_id].tilt_supported) {
|
||||
windows[window_id].last_tilt = Vector2(Math::atan(Math::sin(azim) / alt), Math::atan(Math::cos(azim) / alt));
|
||||
} else {
|
||||
windows[window_id].last_tilt = Vector2();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} break;
|
||||
case WM_POINTERUPDATE: {
|
||||
if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) {
|
||||
break;
|
||||
|
@ -2145,7 +2186,21 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
|
|||
mm->set_shift((wParam & MK_SHIFT) != 0);
|
||||
mm->set_alt(alt_mem);
|
||||
|
||||
mm->set_pressure((wParam & MK_LBUTTON) ? 1.0f : 0.0f);
|
||||
if (!OS::get_singleton()->is_wintab_disabled() && wintab_available && windows[window_id].wtctx) {
|
||||
// Note: WinTab sends both WT_PACKET and WM_xBUTTONDOWN/UP/MOUSEMOVE events, use mouse 1/0 pressure only when last_pressure was not update recently.
|
||||
if (windows[window_id].last_pressure_update < 10) {
|
||||
windows[window_id].last_pressure_update++;
|
||||
} else {
|
||||
windows[window_id].last_tilt = Vector2();
|
||||
windows[window_id].last_pressure = (wParam & MK_LBUTTON) ? 1.0f : 0.0f;
|
||||
}
|
||||
} else {
|
||||
windows[window_id].last_tilt = Vector2();
|
||||
windows[window_id].last_pressure = (wParam & MK_LBUTTON) ? 1.0f : 0.0f;
|
||||
}
|
||||
|
||||
mm->set_pressure(windows[window_id].last_pressure);
|
||||
mm->set_tilt(windows[window_id].last_tilt);
|
||||
|
||||
mm->set_button_mask(last_button_state);
|
||||
|
||||
|
@ -2768,6 +2823,39 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
|
|||
|
||||
DragAcceptFiles(wd.hWnd, true);
|
||||
|
||||
if (!OS::get_singleton()->is_wintab_disabled() && wintab_available) {
|
||||
wintab_WTInfo(WTI_DEFSYSCTX, 0, &wd.wtlc);
|
||||
wd.wtlc.lcOptions |= CXO_MESSAGES;
|
||||
wd.wtlc.lcPktData = PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION;
|
||||
wd.wtlc.lcMoveMask = PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE;
|
||||
wd.wtlc.lcPktMode = 0;
|
||||
wd.wtlc.lcOutOrgX = 0;
|
||||
wd.wtlc.lcOutExtX = wd.wtlc.lcInExtX;
|
||||
wd.wtlc.lcOutOrgY = 0;
|
||||
wd.wtlc.lcOutExtY = -wd.wtlc.lcInExtY;
|
||||
wd.wtctx = wintab_WTOpen(wd.hWnd, &wd.wtlc, false);
|
||||
if (wd.wtctx) {
|
||||
wintab_WTEnable(wd.wtctx, true);
|
||||
AXIS pressure;
|
||||
if (wintab_WTInfo(WTI_DEVICES + wd.wtlc.lcDevice, DVC_NPRESSURE, &pressure)) {
|
||||
wd.min_pressure = int(pressure.axMin);
|
||||
wd.max_pressure = int(pressure.axMax);
|
||||
}
|
||||
AXIS orientation[3];
|
||||
if (wintab_WTInfo(WTI_DEVICES + wd.wtlc.lcDevice, DVC_ORIENTATION, &orientation)) {
|
||||
wd.tilt_supported = orientation[0].axResolution && orientation[1].axResolution;
|
||||
}
|
||||
} else {
|
||||
ERR_PRINT("WinTab context creation falied.");
|
||||
}
|
||||
} else {
|
||||
wd.wtctx = 0;
|
||||
}
|
||||
|
||||
wd.last_pressure = 0;
|
||||
wd.last_pressure_update = 0;
|
||||
wd.last_tilt = Vector2();
|
||||
|
||||
// IME
|
||||
wd.im_himc = ImmGetContext(wd.hWnd);
|
||||
ImmReleaseContext(wd.hWnd, wd.im_himc);
|
||||
|
@ -2785,6 +2873,15 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
|
|||
return id;
|
||||
}
|
||||
|
||||
// WinTab API
|
||||
bool DisplayServerWindows::wintab_available = false;
|
||||
WTOpenPtr DisplayServerWindows::wintab_WTOpen = nullptr;
|
||||
WTClosePtr DisplayServerWindows::wintab_WTClose = nullptr;
|
||||
WTInfoPtr DisplayServerWindows::wintab_WTInfo = nullptr;
|
||||
WTPacketPtr DisplayServerWindows::wintab_WTPacket = nullptr;
|
||||
WTEnablePtr DisplayServerWindows::wintab_WTEnable = nullptr;
|
||||
|
||||
// Windows Ink API
|
||||
GetPointerTypePtr DisplayServerWindows::win8p_GetPointerType = nullptr;
|
||||
GetPointerPenInfoPtr DisplayServerWindows::win8p_GetPointerPenInfo = nullptr;
|
||||
|
||||
|
@ -2796,7 +2893,19 @@ typedef enum _SHC_PROCESS_DPI_AWARENESS {
|
|||
|
||||
DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
|
||||
|
||||
//Note: Functions for pen input, available on Windows 8+
|
||||
//Note: Wacom WinTab driver API for pen input, for devices incompatible with Windows Ink.
|
||||
HMODULE wintab_lib = LoadLibraryW(L"wintab32.dll");
|
||||
if (wintab_lib) {
|
||||
wintab_WTOpen = (WTOpenPtr)GetProcAddress(wintab_lib, "WTOpenW");
|
||||
wintab_WTClose = (WTClosePtr)GetProcAddress(wintab_lib, "WTClose");
|
||||
wintab_WTInfo = (WTInfoPtr)GetProcAddress(wintab_lib, "WTInfoW");
|
||||
wintab_WTPacket = (WTPacketPtr)GetProcAddress(wintab_lib, "WTPacket");
|
||||
wintab_WTEnable = (WTEnablePtr)GetProcAddress(wintab_lib, "WTEnable");
|
||||
|
||||
wintab_available = wintab_WTOpen && wintab_WTClose && wintab_WTInfo && wintab_WTPacket && wintab_WTEnable;
|
||||
}
|
||||
|
||||
//Note: Windows Ink API for pen input, available on Windows 8+ only.
|
||||
HMODULE user32_lib = LoadLibraryW(L"user32.dll");
|
||||
if (user32_lib) {
|
||||
win8p_GetPointerType = (GetPointerTypePtr)GetProcAddress(user32_lib, "GetPointerType");
|
||||
|
@ -3013,7 +3122,10 @@ DisplayServerWindows::~DisplayServerWindows() {
|
|||
context_vulkan->window_destroy(MAIN_WINDOW_ID);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (wintab_available && windows[MAIN_WINDOW_ID].wtctx) {
|
||||
wintab_WTClose(windows[MAIN_WINDOW_ID].wtctx);
|
||||
windows[MAIN_WINDOW_ID].wtctx = 0;
|
||||
}
|
||||
DestroyWindow(windows[MAIN_WINDOW_ID].hWnd);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,6 +66,87 @@
|
|||
#include <windows.h>
|
||||
#include <windowsx.h>
|
||||
|
||||
// WinTab API
|
||||
#define WT_PACKET 0x7FF0
|
||||
#define WT_PROXIMITY 0x7FF5
|
||||
#define WT_INFOCHANGE 0x7FF6
|
||||
#define WT_CSRCHANGE 0x7FF7
|
||||
|
||||
#define WTI_DEFSYSCTX 4
|
||||
#define WTI_DEVICES 100
|
||||
#define DVC_NPRESSURE 15
|
||||
#define DVC_TPRESSURE 16
|
||||
#define DVC_ORIENTATION 17
|
||||
#define DVC_ROTATION 18
|
||||
|
||||
#define CXO_MESSAGES 0x0004
|
||||
#define PK_NORMAL_PRESSURE 0x0400
|
||||
#define PK_TANGENT_PRESSURE 0x0800
|
||||
#define PK_ORIENTATION 0x1000
|
||||
|
||||
typedef struct tagLOGCONTEXTW {
|
||||
WCHAR lcName[40];
|
||||
UINT lcOptions;
|
||||
UINT lcStatus;
|
||||
UINT lcLocks;
|
||||
UINT lcMsgBase;
|
||||
UINT lcDevice;
|
||||
UINT lcPktRate;
|
||||
DWORD lcPktData;
|
||||
DWORD lcPktMode;
|
||||
DWORD lcMoveMask;
|
||||
DWORD lcBtnDnMask;
|
||||
DWORD lcBtnUpMask;
|
||||
LONG lcInOrgX;
|
||||
LONG lcInOrgY;
|
||||
LONG lcInOrgZ;
|
||||
LONG lcInExtX;
|
||||
LONG lcInExtY;
|
||||
LONG lcInExtZ;
|
||||
LONG lcOutOrgX;
|
||||
LONG lcOutOrgY;
|
||||
LONG lcOutOrgZ;
|
||||
LONG lcOutExtX;
|
||||
LONG lcOutExtY;
|
||||
LONG lcOutExtZ;
|
||||
DWORD lcSensX;
|
||||
DWORD lcSensY;
|
||||
DWORD lcSensZ;
|
||||
BOOL lcSysMode;
|
||||
int lcSysOrgX;
|
||||
int lcSysOrgY;
|
||||
int lcSysExtX;
|
||||
int lcSysExtY;
|
||||
DWORD lcSysSensX;
|
||||
DWORD lcSysSensY;
|
||||
} LOGCONTEXTW;
|
||||
|
||||
typedef struct tagAXIS {
|
||||
LONG axMin;
|
||||
LONG axMax;
|
||||
UINT axUnits;
|
||||
DWORD axResolution;
|
||||
} AXIS;
|
||||
|
||||
typedef struct tagORIENTATION {
|
||||
int orAzimuth;
|
||||
int orAltitude;
|
||||
int orTwist;
|
||||
} ORIENTATION;
|
||||
|
||||
typedef struct tagPACKET {
|
||||
int pkNormalPressure;
|
||||
int pkTangentPressure;
|
||||
ORIENTATION pkOrientation;
|
||||
} PACKET;
|
||||
|
||||
typedef HANDLE(WINAPI *WTOpenPtr)(HWND p_window, LOGCONTEXTW *p_ctx, BOOL p_enable);
|
||||
typedef BOOL(WINAPI *WTClosePtr)(HANDLE p_ctx);
|
||||
typedef UINT(WINAPI *WTInfoPtr)(UINT p_category, UINT p_index, LPVOID p_output);
|
||||
typedef BOOL(WINAPI *WTPacketPtr)(HANDLE p_ctx, UINT p_param, LPVOID p_packets);
|
||||
typedef BOOL(WINAPI *WTEnablePtr)(HANDLE p_ctx, BOOL p_enable);
|
||||
|
||||
// Windows Ink API
|
||||
#ifndef POINTER_STRUCTURES
|
||||
|
||||
#define POINTER_STRUCTURES
|
||||
|
@ -144,6 +225,10 @@ typedef struct tagPOINTER_PEN_INFO {
|
|||
|
||||
#endif //POINTER_STRUCTURES
|
||||
|
||||
#ifndef WM_POINTERUPDATE
|
||||
#define WM_POINTERUPDATE 0x0245
|
||||
#endif
|
||||
|
||||
typedef BOOL(WINAPI *GetPointerTypePtr)(uint32_t p_id, POINTER_INPUT_TYPE *p_type);
|
||||
typedef BOOL(WINAPI *GetPointerPenInfoPtr)(uint32_t p_id, POINTER_PEN_INFO *p_pen_info);
|
||||
|
||||
|
@ -171,6 +256,15 @@ class DisplayServerWindows : public DisplayServer {
|
|||
|
||||
_THREAD_SAFE_CLASS_
|
||||
|
||||
// WinTab API
|
||||
static bool wintab_available;
|
||||
static WTOpenPtr wintab_WTOpen;
|
||||
static WTClosePtr wintab_WTClose;
|
||||
static WTInfoPtr wintab_WTInfo;
|
||||
static WTPacketPtr wintab_WTPacket;
|
||||
static WTEnablePtr wintab_WTEnable;
|
||||
|
||||
// Windows Ink API
|
||||
static GetPointerTypePtr win8p_GetPointerType;
|
||||
static GetPointerPenInfoPtr win8p_GetPointerPenInfo;
|
||||
|
||||
|
@ -230,6 +324,16 @@ class DisplayServerWindows : public DisplayServer {
|
|||
bool no_focus = false;
|
||||
bool window_has_focus = false;
|
||||
|
||||
HANDLE wtctx;
|
||||
LOGCONTEXTW wtlc;
|
||||
int min_pressure;
|
||||
int max_pressure;
|
||||
bool tilt_supported;
|
||||
|
||||
int last_pressure_update;
|
||||
float last_pressure;
|
||||
Vector2 last_tilt;
|
||||
|
||||
HBITMAP hBitmap; //DIB section for layered window
|
||||
uint8_t *dib_data = nullptr;
|
||||
Size2 dib_size;
|
||||
|
|
Loading…
Reference in New Issue