Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Peerman <chris_82>2019-01-14 19:46:49 +0300
committerBrecht Van Lommel <brechtvanlommel@gmail.com>2019-01-14 22:48:11 +0300
commit46932079184754ace63b25f854e3f273fbf6f0c5 (patch)
tree2d17a33cccbb5e873b090d319add3485528ace54 /intern/ghost
parenteaf282b375598e63876465735519ab3190bd3214 (diff)
Windows: add support for Windows Ink.
Before this Blender always needed the Wintab driver. This adds support for the native pressure API in Windows 8+, making it possible to get pressure sensitivity on e.g. Microsoft Surface hardware without any extra drivers. By default Blender will automatically use Wintab if available, and if not use Windows Ink instead. There is also a new user preference to explicitly specify which API to use if automatic detection fails. Fixes T57869: no pressure sensitivity with Surface pen or laptop. Code by Christopher Peerman with some tweaks by Brecht Van Lommel. Differential Revision: https://developer.blender.org/D4165
Diffstat (limited to 'intern/ghost')
-rw-r--r--intern/ghost/GHOST_C-api.h7
-rw-r--r--intern/ghost/GHOST_ISystem.h6
-rw-r--r--intern/ghost/GHOST_Types.h6
-rw-r--r--intern/ghost/intern/GHOST_C-api.cpp7
-rw-r--r--intern/ghost/intern/GHOST_System.cpp15
-rw-r--r--intern/ghost/intern/GHOST_System.h13
-rw-r--r--intern/ghost/intern/GHOST_SystemWin32.cpp9
-rw-r--r--intern/ghost/intern/GHOST_WindowWin32.cpp90
-rw-r--r--intern/ghost/intern/GHOST_WindowWin32.h76
9 files changed, 223 insertions, 6 deletions
diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h
index 1ce051d2660..45ec41f63a0 100644
--- a/intern/ghost/GHOST_C-api.h
+++ b/intern/ghost/GHOST_C-api.h
@@ -743,6 +743,13 @@ extern GHOST_TSuccess GHOST_ActivateOpenGLContext(GHOST_ContextHandle contexthan
extern GHOST_TSuccess GHOST_ReleaseOpenGLContext(GHOST_ContextHandle contexthandle);
/**
+ * Set which tablet API to use. Only affects Windows, other platforms have a single API.
+ * \param systemhandle The handle to the system
+ * \param api Enum indicating which API to use.
+ */
+extern void GHOST_SetTabletAPI(GHOST_SystemHandle systemhandle, GHOST_TTabletAPI api);
+
+/**
* Returns the status of the tablet
* \param windowhandle The handle to the window
* \return Status of tablet
diff --git a/intern/ghost/GHOST_ISystem.h b/intern/ghost/GHOST_ISystem.h
index 42ae750e20a..39686c56719 100644
--- a/intern/ghost/GHOST_ISystem.h
+++ b/intern/ghost/GHOST_ISystem.h
@@ -397,6 +397,12 @@ public:
*/
virtual GHOST_TSuccess getButtonState(GHOST_TButtonMask mask, bool& isDown) const = 0;
+ /**
+ * Set which tablet API to use. Only affects Windows, other platforms have a single API.
+ * \param api Enum indicating which API to use.
+ */
+ virtual void setTabletAPI(GHOST_TTabletAPI api) = 0;
+
#ifdef WITH_INPUT_NDOF
/**
* Sets 3D mouse deadzone
diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h
index 1fc1d1a3e56..f786c99342b 100644
--- a/intern/ghost/GHOST_Types.h
+++ b/intern/ghost/GHOST_Types.h
@@ -90,6 +90,12 @@ typedef enum {
GHOST_kTabletModeEraser
} GHOST_TTabletMode;
+typedef enum {
+ GHOST_kTabletAutomatic = 0,
+ GHOST_kTabletNative,
+ GHOST_kTabletWintab,
+} GHOST_TTabletAPI;
+
typedef struct GHOST_TabletData {
GHOST_TTabletMode Active; /* 0=None, 1=Stylus, 2=Eraser */
float Pressure; /* range 0.0 (not touching) to 1.0 (full pressure) */
diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp
index ab0e7b724b8..d3fb7d6f043 100644
--- a/intern/ghost/intern/GHOST_C-api.cpp
+++ b/intern/ghost/intern/GHOST_C-api.cpp
@@ -750,8 +750,13 @@ GHOST_TSuccess GHOST_InvalidateWindow(GHOST_WindowHandle windowhandle)
return window->invalidate();
}
+void GHOST_SetTabletAPI(GHOST_SystemHandle systemhandle, GHOST_TTabletAPI api)
+{
+ GHOST_ISystem *system = (GHOST_ISystem *) systemhandle;
+ system->setTabletAPI(api);
+}
-extern const GHOST_TabletData *GHOST_GetTabletData(GHOST_WindowHandle windowhandle)
+const GHOST_TabletData *GHOST_GetTabletData(GHOST_WindowHandle windowhandle)
{
return ((GHOST_IWindow *)windowhandle)->GetTabletData();
}
diff --git a/intern/ghost/intern/GHOST_System.cpp b/intern/ghost/intern/GHOST_System.cpp
index fc69900acdf..149649f11d1 100644
--- a/intern/ghost/intern/GHOST_System.cpp
+++ b/intern/ghost/intern/GHOST_System.cpp
@@ -52,10 +52,11 @@ GHOST_System::GHOST_System()
m_displayManager(NULL),
m_timerManager(NULL),
m_windowManager(NULL),
- m_eventManager(NULL)
+ m_eventManager(NULL),
#ifdef WITH_INPUT_NDOF
- , m_ndofManager(0)
+ m_ndofManager(0),
#endif
+ m_tabletAPI(GHOST_kTabletAutomatic)
{
}
@@ -297,6 +298,16 @@ GHOST_TSuccess GHOST_System::getButtonState(GHOST_TButtonMask mask, bool& isDown
return success;
}
+void GHOST_System::setTabletAPI(GHOST_TTabletAPI api)
+{
+ m_tabletAPI = api;
+}
+
+bool GHOST_System::useTabletAPI(GHOST_TTabletAPI api) const
+{
+ return (m_tabletAPI == GHOST_kTabletAutomatic || m_tabletAPI == api);
+}
+
#ifdef WITH_INPUT_NDOF
void GHOST_System::setNDOFDeadZone(float deadzone)
{
diff --git a/intern/ghost/intern/GHOST_System.h b/intern/ghost/intern/GHOST_System.h
index ee3c30c35b4..7660ddc947e 100644
--- a/intern/ghost/intern/GHOST_System.h
+++ b/intern/ghost/intern/GHOST_System.h
@@ -247,6 +247,17 @@ public:
*/
GHOST_TSuccess getButtonState(GHOST_TButtonMask mask, bool& isDown) const;
+ /**
+ * Set which tablet API to use. Only affects Windows, other platforms have a single API.
+ * \param api Enum indicating which API to use.
+ */
+ void setTabletAPI(GHOST_TTabletAPI api);
+
+ /**
+ * Test if given tablet API should be used by event handling.
+ */
+ bool useTabletAPI(GHOST_TTabletAPI api) const;
+
#ifdef WITH_INPUT_NDOF
/***************************************************************************************
* Access to 3D mouse.
@@ -380,6 +391,8 @@ protected:
/** Settings of the display before the display went fullscreen. */
GHOST_DisplaySetting m_preFullScreenSetting;
+ /** Which tablet API to use. */
+ GHOST_TTabletAPI m_tabletAPI;
};
inline GHOST_TimerManager *GHOST_System::getTimerManager() const
diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp
index 00852c1ad05..70010f8b558 100644
--- a/intern/ghost/intern/GHOST_SystemWin32.cpp
+++ b/intern/ghost/intern/GHOST_SystemWin32.cpp
@@ -122,6 +122,10 @@
#define WM_DPICHANGED 0x02E0
#endif // WM_DPICHANGED
+#ifndef WM_POINTERUPDATE
+#define WM_POINTERUPDATE 0x0245
+#endif // WM_POINTERUPDATE
+
/* Workaround for some laptop touchpads, some of which seems to
* have driver issues which makes it so window function receives
* the message, but PeekMessage doesn't pick those messages for
@@ -1233,6 +1237,9 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
case WT_PROXIMITY:
window->processWin32TabletInitEvent();
break;
+ case WM_POINTERUPDATE:
+ window->processWin32PointerEvent(wParam);
+ break;
////////////////////////////////////////////////////////////////////////
// Mouse events, processed
////////////////////////////////////////////////////////////////////////
@@ -1450,8 +1457,6 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
* change such as when the window is moved to a monitor with a different DPI.
*/
{
- WORD newYAxisDPI = HIWORD(wParam);
- WORD newXAxisDPI = LOWORD(wParam);
// The suggested new size and position of the window.
RECT* const suggestedWindowRect = (RECT*)lParam;
diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp
index 7027c2af368..da23baa3111 100644
--- a/intern/ghost/intern/GHOST_WindowWin32.cpp
+++ b/intern/ghost/intern/GHOST_WindowWin32.cpp
@@ -51,7 +51,9 @@
#include <string.h>
#include <assert.h>
-
+#ifndef GET_POINTERID_WPARAM
+#define GET_POINTERID_WPARAM(wParam) (LOWORD(wParam))
+#endif // GET_POINTERID_WPARAM
const wchar_t *GHOST_WindowWin32::s_windowClassName = L"GHOST_WindowClass";
const int GHOST_WindowWin32::s_maxTitleLength = 128;
@@ -89,6 +91,8 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
m_wantAlphaBackground(alphaBackground),
m_normal_state(GHOST_kWindowStateNormal),
m_user32(NULL),
+ m_fpGetPointerInfo(NULL),
+ m_fpGetPointerPenInfo(NULL),
m_parentWindowHwnd(parentwindowhwnd),
m_debug_context(is_debug)
{
@@ -284,6 +288,12 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
RegisterRawInputDevices(&device, 1, sizeof(device));
}
+ // Initialize Windows Ink
+ if (m_user32) {
+ m_fpGetPointerInfo = (GHOST_WIN32_GetPointerInfo) ::GetProcAddress(m_user32, "GetPointerInfo");
+ m_fpGetPointerPenInfo = (GHOST_WIN32_GetPointerPenInfo) ::GetProcAddress(m_user32, "GetPointerPenInfo");
+ }
+
// Initialize Wintab
m_wintab.handle = ::LoadLibrary("Wintab32.dll");
if (m_wintab.handle) {
@@ -353,6 +363,7 @@ GHOST_WindowWin32::~GHOST_WindowWin32()
if (m_Bar) {
m_Bar->SetProgressState(m_hWnd, TBPF_NOPROGRESS);
m_Bar->Release();
+ m_Bar = NULL;
}
if (m_wintab.handle) {
@@ -364,6 +375,13 @@ GHOST_WindowWin32::~GHOST_WindowWin32()
memset(&m_wintab, 0, sizeof(m_wintab));
}
+ if (m_user32) {
+ FreeLibrary(m_user32);
+ m_user32 = NULL;
+ m_fpGetPointerInfo = NULL;
+ m_fpGetPointerPenInfo = NULL;
+ }
+
if (m_customCursor) {
DestroyCursor(m_customCursor);
m_customCursor = NULL;
@@ -371,6 +389,7 @@ GHOST_WindowWin32::~GHOST_WindowWin32()
if (m_hWnd != NULL && m_hDC != NULL && releaseNativeHandles()) {
::ReleaseDC(m_hWnd, m_hDC);
+ m_hDC = NULL;
}
if (m_hWnd) {
@@ -379,6 +398,7 @@ GHOST_WindowWin32::~GHOST_WindowWin32()
RevokeDragDrop(m_hWnd);
// Release our reference of the DropTarget and it will delete itself eventually.
m_dropTarget->Release();
+ m_dropTarget = NULL;
}
::SetWindowLongPtr(m_hWnd, GWLP_USERDATA, NULL);
::DestroyWindow(m_hWnd);
@@ -866,8 +886,62 @@ GHOST_TSuccess GHOST_WindowWin32::setWindowCursorShape(GHOST_TStandardCursor cur
return GHOST_kSuccess;
}
+void GHOST_WindowWin32::processWin32PointerEvent(WPARAM wParam)
+{
+ if (!m_system->useTabletAPI(GHOST_kTabletNative)) {
+ return; // Other tablet API specified by user
+ }
+
+ if (!m_fpGetPointerInfo || !m_fpGetPointerPenInfo) {
+ return; // OS version does not support pointer API
+ }
+
+ UINT32 pointerId = GET_POINTERID_WPARAM(wParam);
+ POINTER_INFO pointerInfo;
+ if (!m_fpGetPointerInfo(pointerId, &pointerInfo)) {
+ return; // Invalid pointer info
+ }
+
+ m_tabletData.Active = GHOST_kTabletModeNone;
+ m_tabletData.Pressure = 1.0f;
+ m_tabletData.Xtilt = 0.0f;
+ m_tabletData.Ytilt = 0.0f;
+
+ if (pointerInfo.pointerType & PT_POINTER) {
+ POINTER_PEN_INFO pointerPenInfo;
+ if (!m_fpGetPointerPenInfo(pointerId, &pointerPenInfo)) {
+ return;
+ }
+
+ // With the Microsoft Surface Pen if you hover the within 1cm of the screen the WM_POINTERUPDATE
+ // event will fire with PEN_MASK_PRESSURE mask set and zero pressure. In this case we disable
+ // tablet mode until the pen is physically touching. This enables the user to switch to the
+ // mouse and draw at full pressure.
+ if (pointerPenInfo.penMask & PEN_MASK_PRESSURE && pointerPenInfo.pressure > 0) {
+ m_tabletData.Active = GHOST_kTabletModeStylus;
+ m_tabletData.Pressure = pointerPenInfo.pressure / 1024.0f;
+ }
+
+ if (pointerPenInfo.penFlags & PEN_FLAG_ERASER) {
+ m_tabletData.Active = GHOST_kTabletModeEraser;
+ }
+
+ if (pointerPenInfo.penFlags & PEN_MASK_TILT_X) {
+ m_tabletData.Xtilt = fmin(fabs(pointerPenInfo.tiltX / 90), 1.0f);
+ }
+
+ if (pointerPenInfo.penFlags & PEN_MASK_TILT_Y) {
+ m_tabletData.Ytilt = fmin(fabs(pointerPenInfo.tiltY / 90), 1.0f);
+ }
+ }
+}
+
void GHOST_WindowWin32::processWin32TabletActivateEvent(WORD state)
{
+ if (!m_system->useTabletAPI(GHOST_kTabletWintab)) {
+ return;
+ }
+
if (m_wintab.enable && m_wintab.tablet) {
m_wintab.enable(m_wintab.tablet, state);
@@ -879,6 +953,10 @@ void GHOST_WindowWin32::processWin32TabletActivateEvent(WORD state)
void GHOST_WindowWin32::processWin32TabletInitEvent()
{
+ if (!m_system->useTabletAPI(GHOST_kTabletWintab)) {
+ return;
+ }
+
// Let's see if we can initialize tablet here
if (m_wintab.info && m_wintab.tablet) {
AXIS Pressure, Orientation[3]; /* The maximum tablet size */
@@ -903,10 +981,16 @@ void GHOST_WindowWin32::processWin32TabletInitEvent()
m_tabletData.Active = GHOST_kTabletModeNone;
}
+
+ m_tabletData.Active = GHOST_kTabletModeNone;
}
void GHOST_WindowWin32::processWin32TabletEvent(WPARAM wParam, LPARAM lParam)
{
+ if (!m_system->useTabletAPI(GHOST_kTabletWintab)) {
+ return;
+ }
+
if (m_wintab.packet && m_wintab.tablet) {
PACKET pkt;
if (m_wintab.packet((HCTX)lParam, wParam, &pkt)) {
@@ -972,6 +1056,10 @@ void GHOST_WindowWin32::processWin32TabletEvent(WPARAM wParam, LPARAM lParam)
void GHOST_WindowWin32::bringTabletContextToFront()
{
+ if (!m_system->useTabletAPI(GHOST_kTabletWintab)) {
+ return;
+ }
+
if (m_wintab.overlap && m_wintab.tablet) {
m_wintab.overlap(m_wintab.tablet, TRUE);
}
diff --git a/intern/ghost/intern/GHOST_WindowWin32.h b/intern/ghost/intern/GHOST_WindowWin32.h
index d790d5c3d24..4efcac53c85 100644
--- a/intern/ghost/intern/GHOST_WindowWin32.h
+++ b/intern/ghost/intern/GHOST_WindowWin32.h
@@ -68,6 +68,79 @@ typedef UINT(API * GHOST_WIN32_GetDpiForWindow)(HWND);
#define USER_DEFAULT_SCREEN_DPI 96
#endif // USER_DEFAULT_SCREEN_DPI
+// typedefs for user32 functions to allow pointer functions
+enum tagPOINTER_INPUT_TYPE {
+ PT_POINTER = 1, // Generic pointer
+ PT_TOUCH = 2, // Touch
+ PT_PEN = 3, // Pen
+ PT_MOUSE = 4, // Mouse
+#if(WINVER >= 0x0603)
+ PT_TOUCHPAD = 5, // Touchpad
+#endif /* WINVER >= 0x0603 */
+};
+
+typedef enum tagPOINTER_BUTTON_CHANGE_TYPE {
+ POINTER_CHANGE_NONE,
+ POINTER_CHANGE_FIRSTBUTTON_DOWN,
+ POINTER_CHANGE_FIRSTBUTTON_UP,
+ POINTER_CHANGE_SECONDBUTTON_DOWN,
+ POINTER_CHANGE_SECONDBUTTON_UP,
+ POINTER_CHANGE_THIRDBUTTON_DOWN,
+ POINTER_CHANGE_THIRDBUTTON_UP,
+ POINTER_CHANGE_FOURTHBUTTON_DOWN,
+ POINTER_CHANGE_FOURTHBUTTON_UP,
+ POINTER_CHANGE_FIFTHBUTTON_DOWN,
+ POINTER_CHANGE_FIFTHBUTTON_UP,
+} POINTER_BUTTON_CHANGE_TYPE;
+
+typedef DWORD POINTER_INPUT_TYPE;
+typedef UINT32 POINTER_FLAGS;
+
+typedef struct tagPOINTER_INFO {
+ POINTER_INPUT_TYPE pointerType;
+ UINT32 pointerId;
+ UINT32 frameId;
+ POINTER_FLAGS pointerFlags;
+ HANDLE sourceDevice;
+ HWND hwndTarget;
+ POINT ptPixelLocation;
+ POINT ptHimetricLocation;
+ POINT ptPixelLocationRaw;
+ POINT ptHimetricLocationRaw;
+ DWORD dwTime;
+ UINT32 historyCount;
+ INT32 InputData;
+ DWORD dwKeyStates;
+ UINT64 PerformanceCount;
+ POINTER_BUTTON_CHANGE_TYPE ButtonChangeType;
+} POINTER_INFO;
+
+typedef UINT32 PEN_FLAGS;
+#define PEN_FLAG_NONE 0x00000000 // Default
+#define PEN_FLAG_BARREL 0x00000001 // The barrel button is pressed
+#define PEN_FLAG_INVERTED 0x00000002 // The pen is inverted
+#define PEN_FLAG_ERASER 0x00000004 // The eraser button is pressed
+
+typedef UINT32 PEN_MASK;
+#define PEN_MASK_NONE 0x00000000 // Default - none of the optional fields are valid
+#define PEN_MASK_PRESSURE 0x00000001 // The pressure field is valid
+#define PEN_MASK_ROTATION 0x00000002 // The rotation field is valid
+#define PEN_MASK_TILT_X 0x00000004 // The tiltX field is valid
+#define PEN_MASK_TILT_Y 0x00000008 // The tiltY field is valid
+
+typedef struct tagPOINTER_PEN_INFO {
+ POINTER_INFO pointerInfo;
+ PEN_FLAGS penFlags;
+ PEN_MASK penMask;
+ UINT32 pressure;
+ UINT32 rotation;
+ INT32 tiltX;
+ INT32 tiltY;
+} POINTER_PEN_INFO;
+
+typedef BOOL (API * GHOST_WIN32_GetPointerInfo)(UINT32 pointerId, POINTER_INFO *pointerInfo);
+typedef BOOL (API * GHOST_WIN32_GetPointerPenInfo)(UINT32 pointerId, POINTER_PEN_INFO *penInfo);
+
/**
* GHOST window on M$ Windows OSs.
* \author Maarten Gribnau
@@ -253,6 +326,7 @@ public:
return &m_tabletData;
}
+ void processWin32PointerEvent(WPARAM wParam);
void processWin32TabletActivateEvent(WORD state);
void processWin32TabletInitEvent();
void processWin32TabletEvent(WPARAM wParam, LPARAM lParam);
@@ -377,6 +451,8 @@ private:
/** user32 dll handle*/
HMODULE m_user32;
+ GHOST_WIN32_GetPointerInfo m_fpGetPointerInfo;
+ GHOST_WIN32_GetPointerPenInfo m_fpGetPointerPenInfo;
/** Hwnd to parent window */
GHOST_TEmbedderWindowID m_parentWindowHwnd;