[PATCH v2 05/12] wintab32: Conform behavior to real wintab driver.

Elaine Lefler elaineclefler at gmail.com
Thu Mar 24 23:46:19 CDT 2022


Signed-off-by: Elaine Lefler <elaineclefler at gmail.com>
---

v2: Splitting into smaller patches as per feedback.

Notes:
- Updated documentation URL, the old one was broken.
- Categories support negative indices.
- Scaling uses Org+abs(Ext)-1, not Org+abs(Ext).
- WT_PROXIMITY does not generate packets.
- No data should be copied when WTInfoT returns 0.
- Removing packets from the queue should only remove packets that
actually exist, not the entire length of the buffer.
- Packets with the same serial should be ignored.
- There are a number of edge cases where the mapping requested by the
application does not match the mapping of the system. Wine can't remap
the tablet, but we can remap the coordinates so that the application
works correctly.
---
 dlls/wintab32/context.c         | 169 ++++++++++++++++++++++++++------
 dlls/wintab32/wintab32.c        |  14 +--
 dlls/wintab32/wintab_internal.h |   1 +
 include/wintab.h                |  26 +++++
 4 files changed, 171 insertions(+), 39 deletions(-)

diff --git a/dlls/wintab32/context.c b/dlls/wintab32/context.c
index da465d180c7..5302fadfdca 100644
--- a/dlls/wintab32/context.c
+++ b/dlls/wintab32/context.c
@@ -38,7 +38,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(wintab32);
 
 /*
  * Documentation found at
- * http://www.csl.sony.co.jp/projects/ar/restricted/wintabl.html
+ * https://developer-docs.wacom.com/intuos-cintiq-business-tablets/docs/wintab-overview
  */
 
 static LPOPENCONTEXT gOpenContexts;
@@ -62,7 +62,8 @@ static void LOGCONTEXTWtoA(const LOGCONTEXTW *in, LOGCONTEXTA *out)
 
 static BOOL is_logcontext_category(UINT wCategory)
 {
-    return wCategory == WTI_DEFSYSCTX || wCategory == WTI_DEFCONTEXT || wCategory == WTI_DDCTXS;
+    return wCategory == WTI_DEFSYSCTX || wCategory == WTI_DEFCONTEXT
+            || IS_WTI_DDCTXS_TYPE(wCategory) || IS_WTI_DSCTXS_TYPE(wCategory);
 }
 
 static BOOL is_string_field(UINT wCategory, UINT nIndex)
@@ -71,10 +72,11 @@ static BOOL is_string_field(UINT wCategory, UINT nIndex)
         return TRUE;
     if (is_logcontext_category(wCategory) && nIndex == CTX_NAME)
         return TRUE;
-    if ((wCategory >= WTI_CURSORS && wCategory <= WTI_CURSORS + 9) &&
-            (nIndex == CSR_NAME || nIndex == CSR_BTNNAMES))
+    if (IS_WTI_CURSORS_TYPE(wCategory)
+            && (nIndex == CSR_NAME || nIndex == CSR_BTNNAMES))
         return TRUE;
-    if (wCategory == WTI_DEVICES && (nIndex == DVC_NAME || nIndex == DVC_PNPID))
+    if (IS_WTI_DEVICES_TYPE(wCategory)
+            && (nIndex == DVC_NAME || nIndex == DVC_PNPID))
         return TRUE;
     return FALSE;
 }
@@ -175,12 +177,44 @@ int TABLET_PostTabletMessage(LPOPENCONTEXT newcontext, UINT msg, WPARAM wParam,
     return 0;
 }
 
+LPOPENCONTEXT FindContextForHwnd(HWND hwnd)
+{
+    LPOPENCONTEXT ptr = gOpenContexts;
+
+    EnterCriticalSection(&csTablet);
+    while (ptr)
+    {
+        if (ptr->hwndOwner == hwnd && ptr->enabled)
+            break;
+        ptr = ptr->next;
+    }
+    LeaveCriticalSection(&csTablet);
+    return ptr;
+}
+
+/* Scale point for tablet area. Org is offset. Ext is length. Negative extent
+ * indicates flipped coordinate system, but coordinates remain positive.
+ * (Org <= x < Org + abs(Ext)) */
 static inline DWORD ScaleForContext(DWORD In, LONG InOrg, LONG InExt, LONG OutOrg, LONG OutExt)
 {
-    if (((InExt > 0 )&&(OutExt > 0)) || ((InExt<0) && (OutExt < 0)))
-        return MulDiv(In - InOrg, abs(OutExt), abs(InExt)) + OutOrg;
+    LONG AbsInExt = abs(InExt);
+    LONG AbsOutExt = abs(OutExt);
+
+    if (AbsInExt <= 1 || AbsOutExt <= 1)
+        /* No dimensions */
+        return 0;
+
+    /* Largest representable value is 1 less than extent, we need to scale by
+     * that. This is the same way wintab does it. */
+    AbsInExt--;
+    AbsOutExt--;
+
+    if ((InExt < 0) == (OutExt < 0))
+        /* Same sign, maintain direction */
+        return MulDiv(In - InOrg, AbsOutExt, AbsInExt) + OutOrg;
     else
-        return MulDiv(abs(InExt) - (In - InOrg), abs(OutExt), abs(InExt)) + OutOrg;
+        /* Opposite signs, invert direction */
+        return MulDiv(AbsInExt - (In - InOrg), AbsOutExt, AbsInExt) + OutOrg;
 }
 
 LPOPENCONTEXT AddPacketToContextQueue(LPPACKET packet, HWND hwnd)
@@ -204,20 +238,36 @@ LPOPENCONTEXT AddPacketToContextQueue(LPPACKET packet, HWND hwnd)
             }
 
             tgt = ptr->PacketsQueued;
+            if (tgt > 0 && ptr->PacketQueue[tgt - 1].pkSerialNumber == packet->pkSerialNumber)
+            {
+                ptr = ptr->next;
+                continue;
+
+            }
 
             packet->pkContext = ptr->handle;
 
-            /* translate packet data to the context */
+            /* Translate packet data to the context */
             packet->pkChanged = packet->pkChanged & ptr->context.lcPktData;
 
-            /* Scale  as per documentation */
-            packet->pkY = ScaleForContext(packet->pkY, ptr->context.lcInOrgY,
-                                ptr->context.lcInExtY, ptr->context.lcOutOrgY,
-                                ptr->context.lcOutExtY);
+            packet->pkX = ScaleForContext(packet->pkX,
+                            ptr->context.lcInOrgX, ptr->context.lcInExtX,
+                            ptr->context.lcOutOrgX, ptr->context.lcOutExtX);
+
+            packet->pkY = ScaleForContext(packet->pkY,
+                            ptr->context.lcInOrgY, ptr->context.lcInExtY,
+                            ptr->context.lcOutOrgY, ptr->context.lcOutExtY);
 
-            packet->pkX = ScaleForContext(packet->pkX, ptr->context.lcInOrgX,
-                                ptr->context.lcInExtX, ptr->context.lcOutOrgX,
-                                ptr->context.lcOutExtX);
+            /* Clip values if outside of reporting range */
+            if ((LONG)packet->pkX < ptr->context.lcOutOrgX)
+                packet->pkX = ptr->context.lcOutOrgX;
+            else if((LONG)packet->pkX >= ptr->context.lcOutOrgX + abs(ptr->context.lcOutExtX))
+                packet->pkX = ptr->context.lcOutOrgX + abs(ptr->context.lcOutExtX) - 1;
+
+            if ((LONG)packet->pkY < ptr->context.lcOutOrgY)
+                packet->pkY = ptr->context.lcOutOrgY;
+            else if((LONG)packet->pkY >= ptr->context.lcOutOrgY + abs(ptr->context.lcOutExtY))
+                packet->pkY = ptr->context.lcOutOrgY + abs(ptr->context.lcOutExtY) - 1;
 
             DUMPPACKET(*packet);
 
@@ -369,21 +419,27 @@ static UINT WTInfoT(UINT wCategory, UINT nIndex, LPVOID lpOutput, BOOL bUnicode)
     if (!LoadTablet()) return 0;
 
     TRACE("(%d, %d, %p, %d)\n", wCategory, nIndex, lpOutput, bUnicode);
-
     if (is_logcontext_category(wCategory) && nIndex == 0)
     {
-        if (lpOutput)
+        if (pWTInfoW(wCategory, nIndex, NULL) == 0)
         {
-            LOGCONTEXTW buf;
-            pWTInfoW(wCategory, nIndex, &buf);
-
-            if (bUnicode)
-                memcpy(lpOutput, &buf, sizeof(buf));
-            else
-                LOGCONTEXTWtoA(&buf, lpOutput);
+            result = 0;
         }
+        else
+        {
+            if (lpOutput)
+            {
+                LOGCONTEXTW buf;
+                pWTInfoW(wCategory, nIndex, &buf);
+
+                if (bUnicode)
+                    memcpy(lpOutput, &buf, sizeof(buf));
+                else
+                    LOGCONTEXTWtoA(&buf, lpOutput);
+            }
 
-        result = bUnicode ? sizeof(LOGCONTEXTW) : sizeof(LOGCONTEXTA);
+            result = bUnicode ? sizeof(LOGCONTEXTW) : sizeof(LOGCONTEXTA);
+        }
     }
     else if (is_string_field(wCategory, nIndex) && !bUnicode)
     {
@@ -423,6 +479,9 @@ UINT WINAPI WTInfoW(UINT wCategory, UINT nIndex, LPVOID lpOutput)
 HCTX WINAPI WTOpenW(HWND hWnd, LPLOGCONTEXTW lpLogCtx, BOOL fEnable)
 {
     LPOPENCONTEXT newcontext;
+    LONG inOrgX, inOrgY, inExtX, inExtY;
+    int sysOrgX, sysOrgY, sysExtX, sysExtY;
+    AXIS devAxX, devAxY;
 
     if (!LoadTablet()) return 0;
 
@@ -433,9 +492,61 @@ HCTX WINAPI WTOpenW(HWND hWnd, LPLOGCONTEXTW lpLogCtx, BOOL fEnable)
     newcontext->context = *lpLogCtx;
     newcontext->hwndOwner = hWnd;
     newcontext->ActiveCursor = -1;
-    newcontext->QueueSize = 10;
+    newcontext->QueueSize = 25;
     newcontext->PacketsQueued = 0;
-    newcontext->PacketQueue=HeapAlloc(GetProcessHeap(),0,sizeof(PACKET)*10);
+    newcontext->PacketQueue=HeapAlloc(GetProcessHeap(),0,sizeof(PACKET)*newcontext->QueueSize);
+
+    /* Tablet area remapping is not supported. Ignore the incoming lcIn* values
+     * and use the system-provided values instead. */
+    pWTInfoW(WTI_DEFSYSCTX, CTX_INORGX, &inOrgX);
+    pWTInfoW(WTI_DEFSYSCTX, CTX_INORGY, &inOrgY);
+    pWTInfoW(WTI_DEFSYSCTX, CTX_INEXTX, &inExtX);
+    pWTInfoW(WTI_DEFSYSCTX, CTX_INEXTY, &inExtY);
+
+    /* According to Wacom's ScribbleDemo, the way to request raw tablet
+     * coordinates is to request an extent larger than the axis. */
+    if (pWTInfoW(WTI_DEVICES + newcontext->context.lcDevice, DVC_X, &devAxX) > 0
+            && pWTInfoW(WTI_DEVICES + newcontext->context.lcDevice, DVC_Y, &devAxY) > 0)
+    {
+        if (abs(newcontext->context.lcOutExtX) > devAxX.axMax - devAxX.axMin + 1
+            || abs(newcontext->context.lcOutExtY) > devAxY.axMax - devAxY.axMin + 1)
+        {
+            newcontext->context.lcOutOrgX = inOrgX;
+            newcontext->context.lcOutExtX = (newcontext->context.lcOutExtX >= 0
+                    ? inExtX : -inExtX);
+
+            newcontext->context.lcOutOrgY = inOrgY;
+            newcontext->context.lcOutExtY = (newcontext->context.lcOutExtY >= 0
+                    ? inExtY : -inExtY);
+        }
+    }
+
+    /* wintab allows developers to customize the area of the screen that the
+     * tablet points to. Wine does NOT allow this, so fudge the input area as
+     * necessary to make it line up with the actual screen. */
+    pWTInfoW(WTI_DEFSYSCTX, CTX_SYSORGX, &sysOrgX);
+    pWTInfoW(WTI_DEFSYSCTX, CTX_SYSORGY, &sysOrgY);
+    pWTInfoW(WTI_DEFSYSCTX, CTX_SYSEXTX, &sysExtX);
+    pWTInfoW(WTI_DEFSYSCTX, CTX_SYSEXTY, &sysExtY);
+
+    newcontext->context.lcInOrgX = ScaleForContext(newcontext->context.lcSysOrgX,
+            sysOrgX, sysExtX, inOrgX, inExtX);
+
+    /* Scale Y region from the bottom because the tablet is upside down */
+    newcontext->context.lcInOrgY = ScaleForContext(newcontext->context.lcSysOrgY + abs(newcontext->context.lcSysExtY),
+            sysOrgY, -sysExtY, inOrgY, inExtY);
+
+    newcontext->context.lcInExtX = MulDiv(abs(newcontext->context.lcSysExtX),
+            inExtX, sysExtX);
+
+    newcontext->context.lcInExtY = MulDiv(abs(newcontext->context.lcSysExtY),
+            inExtY, sysExtY);
+
+    if (memcmp(&newcontext->context, lpLogCtx, sizeof(newcontext->context)) != 0)
+    {
+        TRACE("Incoming context has been modified. Result:\n");
+        DUMPCONTEXT(newcontext->context);
+    }
 
     EnterCriticalSection(&csTablet);
     newcontext->handle = gTopContext++;
@@ -603,7 +714,7 @@ BOOL WINAPI WTPacket(HCTX hCtx, UINT wSerial, LPVOID lpPkt)
         if (lpPkt)
            TABLET_CopyPacketData(context ,lpPkt, wtp);
 
-        if ((rc+1) < context->QueueSize)
+        if ((rc+1) < context->PacketsQueued)
         {
             memmove(context->PacketQueue, &context->PacketQueue[rc+1],
                 (context->PacketsQueued - (rc+1))*sizeof(PACKET));
diff --git a/dlls/wintab32/wintab32.c b/dlls/wintab32/wintab32.c
index 416398a2633..61a1917701a 100644
--- a/dlls/wintab32/wintab32.c
+++ b/dlls/wintab32/wintab32.c
@@ -158,16 +158,10 @@ static LRESULT WINAPI TABLET_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
             }
         case WT_PROXIMITY:
             {
-                PACKET packet;
-                LPOPENCONTEXT handler;
-                if (pGetCurrentPacket)
-                {
-                    pGetCurrentPacket(&packet);
-                    handler = AddPacketToContextQueue(&packet,(HWND)wParam);
-                    if (handler)
-                        TABLET_PostTabletMessage(handler, WT_PROXIMITY,
-                                                (WPARAM)handler->handle, lParam, TRUE);
-                }
+                LPOPENCONTEXT handler = FindContextForHwnd((HWND)wParam);
+                if (handler)
+                    TABLET_PostTabletMessage(handler, WT_PROXIMITY,
+                                            (WPARAM)handler->handle, lParam, TRUE);
                 break;
             }
     }
diff --git a/dlls/wintab32/wintab_internal.h b/dlls/wintab32/wintab_internal.h
index 2ee59941faf..3f5302bf13c 100644
--- a/dlls/wintab32/wintab_internal.h
+++ b/dlls/wintab32/wintab_internal.h
@@ -131,6 +131,7 @@ typedef struct tagOPENCONTEXT
 
 int TABLET_PostTabletMessage(LPOPENCONTEXT newcontext, UINT msg, WPARAM wParam,
                              LPARAM lParam, BOOL send_always) DECLSPEC_HIDDEN;
+LPOPENCONTEXT FindContextForHwnd(HWND hwnd) DECLSPEC_HIDDEN;
 LPOPENCONTEXT AddPacketToContextQueue(LPPACKET packet, HWND hwnd) DECLSPEC_HIDDEN;
 
 /* Display driver functions */
diff --git a/include/wintab.h b/include/wintab.h
index e758a52a174..8d95d1a97b8 100644
--- a/include/wintab.h
+++ b/include/wintab.h
@@ -838,6 +838,32 @@ static inline int TABLET_CopyData(LPVOID target, LPCVOID src, INT size)
     return(size);
 }
 
+/* Helpers for category. Though undocumented, negative indices are valid, and
+ * represent iterating the categories in reverse order. */
+#ifndef NOWTDEVICES
+static inline BOOL IS_WTI_DEVICES_TYPE(UINT wCategory){
+    return wCategory > WTI_DEVICES - 50 && wCategory <= WTI_DEVICES + 50;
+}
+#endif /* !defined(NOWTDEVICES) */
+#ifndef NOWTCURSORS
+static inline BOOL IS_WTI_CURSORS_TYPE(UINT wCategory){
+    return wCategory > WTI_CURSORS - 50 && wCategory <= WTI_CURSORS + 50;
+}
+#endif /* !defined(NOWTCURSORS) */
+#ifndef NOWTEXTENSIONS
+static inline BOOL IS_WTI_EXTENSIONS_TYPE(UINT wCategory){
+    return wCategory > WTI_EXTENSIONS - 50 && wCategory <= WTI_EXTENSIONS + 50;
+}
+#endif /* !defined(NOWTEXTENSIONS) */
+#ifndef NOWTDEFCONTEXT
+static inline BOOL IS_WTI_DDCTXS_TYPE(UINT wCategory){
+    return wCategory > WTI_DDCTXS - 50 && wCategory <= WTI_DDCTXS + 50;
+}
+static inline BOOL IS_WTI_DSCTXS_TYPE(UINT wCategory){
+    return wCategory > WTI_DSCTXS - 50 && wCategory <= WTI_DSCTXS + 50;
+}
+#endif /* !defined(NOWTDEFCONTEXT) */
+
 #endif /* !defined(NOWTFUNCTIONS) */
 
 #ifdef __cplusplus
-- 
2.32.0 (Apple Git-132)




More information about the wine-devel mailing list