Windows: add support for Windows Ink.
authorChristopher Peerman <chris_82>
Mon, 14 Jan 2019 16:46:49 +0000 (17:46 +0100)
committerBrecht Van Lommel <brechtvanlommel@gmail.com>
Mon, 14 Jan 2019 19:48:11 +0000 (20:48 +0100)
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

14 files changed:
intern/ghost/GHOST_C-api.h
intern/ghost/GHOST_ISystem.h
intern/ghost/GHOST_Types.h
intern/ghost/intern/GHOST_C-api.cpp
intern/ghost/intern/GHOST_System.cpp
intern/ghost/intern/GHOST_System.h
intern/ghost/intern/GHOST_SystemWin32.cpp
intern/ghost/intern/GHOST_WindowWin32.cpp
intern/ghost/intern/GHOST_WindowWin32.h
release/scripts/startup/bl_ui/space_userpref.py
source/blender/makesdna/DNA_userdef_types.h
source/blender/makesrna/intern/rna_userdef.c
source/blender/windowmanager/WM_api.h
source/blender/windowmanager/intern/wm_window.c

index 1ce051d26606ba3f7a24f66402bc6236fa733518..45ec41f63a0eeec1188cd083b290f5625263e912 100644 (file)
@@ -742,6 +742,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
index 42ae750e20a92eb46425769acfb5bfc144f2e906..39686c567190e9d06221cb3305d21a85d61f7f33 100644 (file)
@@ -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
index 1fc1d1a3e567fde1722e9701c6e88fc456abfb0f..f786c99342b4b4bb3941e8702be6f97d19c37d06 100644 (file)
@@ -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) */
index ab0e7b724b8eacbaff0075efc2c36ed342454f38..d3fb7d6f0431a5091ecdc21f0108d1af5a841621 100644 (file)
@@ -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();
 }
index fc69900acdf6a3f94c4c7daad6b6b89782c2a8ea..149649f11d17e4094b8c88bc3da2787b0e3929fa 100644 (file)
@@ -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)
 {
index ee3c30c35b460109f92c61890cc2b30054bb69d3..7660ddc947ea574761ce31f1c70eece702437253 100644 (file)
@@ -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
index 00852c1ad05c1fb1b526d3b41bc98b01ac5568ee..70010f8b558e7c234c7d8ce032e4f3bc859ad07c 100644 (file)
 #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;
 
index 7027c2af368a67b1437f076f07246d813f889e92..da23baa31115d7da8898987bcc8343baa016c46c 100644 (file)
@@ -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);
        }
index d790d5c3d2422da27c4a3229a2b57a2bb934d4c5..4efcac53c85fc519f9bc8357a75f251f49f8a8c5 100644 (file)
@@ -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;
index 6abf2e92da887ee81abef36fae11224fa1197f5f..a4feabf72544556e78f930e7cf405fed0c9baace 100644 (file)
@@ -1504,6 +1504,11 @@ class USERPREF_PT_input_devices_tablet(PreferencePanel):
         prefs = context.preferences
         inputs = prefs.inputs
 
+        import sys
+        if sys.platform[:3] == "win":
+            layout.prop(inputs, "tablet_api")
+            layout.separator()
+
         flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
 
         flow.prop(inputs, "pressure_threshold_max")
index a749fba502692903560d7aba35c5a72ba5e91c02..a653aaec6715590f2482dd1cb4b7b15b97bf0925 100644 (file)
@@ -694,13 +694,16 @@ typedef struct UserDef {
        /** Seconds to zoom around current frame. */
        float view_frame_seconds;
 
-       char _pad1[4];
+       char _pad1[2];
 
        /** Private, defaults to 20 for 72 DPI setting. */
        short widget_unit;
        short anisotropic_filter;
        short use_16bit_textures, use_gpu_mipmap;
 
+       /** Tablet API to use (Windows only). */
+       short tablet_api;
+
        /** Raw tablet pressure that maps to 100%. */
        float pressure_threshold_max;
        /** Curve non-linearity parameter. */
@@ -915,6 +918,13 @@ typedef enum eUserpref_UI_Flag2 {
        USER_TRACKPAD_NATURAL           = (1 << 2),
 } eUserpref_UI_Flag2;
 
+/* UserDef.tablet_api */
+typedef enum eUserpref_TableAPI {
+       USER_TABLET_AUTOMATIC = 0,
+       USER_TABLET_NATIVE = 1,
+       USER_TABLET_WINTAB = 2,
+} eUserpref_TabletAPI;
+
 /* UserDef.app_flag */
 typedef enum eUserpref_APP_Flag {
        USER_APP_LOCK_UI_LAYOUT = (1 << 0),
index 15f20a6aba7d636e6c042cf44041a1422c80e211..eea2595d2a789c05a94c45550b895a2b4364fa0c 100644 (file)
@@ -236,6 +236,11 @@ static void rna_userdef_autokeymode_set(PointerRNA *ptr, int value)
        }
 }
 
+static void rna_userdef_tablet_api_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *UNUSED(ptr))
+{
+       WM_init_tablet_api();
+}
+
 #ifdef WITH_INPUT_NDOF
 static void rna_userdef_ndof_deadzone_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
 {
@@ -4608,6 +4613,13 @@ static void rna_def_userdef_input(BlenderRNA *brna)
        };
 #endif /* WITH_INPUT_NDOF */
 
+       static const EnumPropertyItem tablet_api[] = {
+               {USER_TABLET_AUTOMATIC, "AUTOMATIC", 0, "Automatic", "Automatically choose Wintab or Windows Ink depending on the device"},
+               {USER_TABLET_NATIVE, "WINDOWS_INK", 0, "Windows Ink", "Use native Windows Ink API, for modern tablet and pen devices. Requires Windows 8 or newer"},
+               {USER_TABLET_WINTAB, "WINTAB", 0, "Wintab", "Use Wintab driver for older tablets and Windows versions"},
+               {0, NULL, 0, NULL, NULL}
+       };
+
        static const EnumPropertyItem view_zoom_styles[] = {
                {USER_ZOOM_CONT, "CONTINUE", 0, "Continue", "Old style zoom, continues while moving mouse up or down"},
                {USER_ZOOM_DOLLY, "DOLLY", 0, "Dolly", "Zoom in and out based on vertical mouse movement"},
@@ -4733,6 +4745,11 @@ static void rna_def_userdef_input(BlenderRNA *brna)
        RNA_def_property_ui_text(prop, "Softness",
                                 "Adjusts softness of the low pressure response onset using a gamma curve");
 
+       prop = RNA_def_property(srna, "tablet_api", PROP_ENUM, PROP_NONE);
+       RNA_def_property_enum_items(prop, tablet_api);
+       RNA_def_property_ui_text(prop, "Tablet API", "Select the tablet API to use for pressure sensitivity");
+       RNA_def_property_update(prop, 0, "rna_userdef_tablet_api_update");
+
 #ifdef WITH_INPUT_NDOF
        /* 3D mouse settings */
        /* global options */
index adf082982b786e880e78367be3c5d2af223fa889..9daed32ee33f7d79a1c4088b12a7959298761dec 100644 (file)
@@ -91,6 +91,7 @@ void          WM_init_state_fullscreen_set(void);
 void           WM_init_state_normal_set(void);
 void           WM_init_window_focus_set(bool do_it);
 void           WM_init_native_pixels(bool do_it);
+void           WM_init_tablet_api(void);
 
 void           WM_init                         (struct bContext *C, int argc, const char **argv);
 void           WM_exit_ext                     (struct bContext *C, const bool do_python);
index 281a556a62d41e68bea39f7dfa773ec8c7b88e76..8b76b2a5d32ca185a61953a43cf9cb4d6fca1309 100644 (file)
@@ -1690,6 +1690,8 @@ void wm_ghost_init(bContext *C)
                }
 
                GHOST_UseWindowFocus(wm_init_state.window_focus);
+
+               WM_init_tablet_api();
        }
 }
 
@@ -1979,6 +1981,24 @@ void WM_init_native_pixels(bool do_it)
        wm_init_state.native_pixels = do_it;
 }
 
+void WM_init_tablet_api(void)
+{
+       if (g_system) {
+               switch(U.tablet_api) {
+                       case USER_TABLET_NATIVE:
+                               GHOST_SetTabletAPI(g_system, GHOST_kTabletNative);
+                               break;
+                       case USER_TABLET_WINTAB:
+                               GHOST_SetTabletAPI(g_system, GHOST_kTabletWintab);
+                               break;
+                       case USER_TABLET_AUTOMATIC:
+                       default:
+                               GHOST_SetTabletAPI(g_system, GHOST_kTabletAutomatic);
+                               break;
+               }
+       }
+}
+
 /* This function requires access to the GHOST_SystemHandle (g_system) */
 void WM_cursor_warp(wmWindow *win, int x, int y)
 {