Hi Graham, and first — thank you. XWinTab turned out to be the only working
path to get real pen pressure into Adobe Flash Professional CS6 running on
Wine, on a non-Wacom Huion Kamvas Pro 22 display tablet. Wine's built-in
wintab gets nothing under XInput2/libinput, so your XCB/XInput2 approach is the
whole game here.
While getting Flash to drive it, I had to make 7 changes. Several are generic
(including two outright crash fixes in the current code), a couple are more
Flash-specific and probably deserve discussion. I'd be glad to open a PR — as
one branch or split however you prefer. Listing them here first so you can tell
me what you'd actually want upstream.
Environment: Wine 11.0 (stable), win32 prefix, Ubuntu/Pop!_OS 24.04, hid_uclogic
(DIGImend) in proprietary mode. Flash resolves wintab entry points by
ordinal, which is what surfaced most of this.
1. Crash: self-cast in the packet-peek iterator (bug)
In the WTPacketsPeek iteration path, the user-data pointer is cast from the wrong
variable (effectively (PktPeekIterData *) data instead of the passed
userData). The result is an access violation (0xc0000005, read near offset
0x8) the instant the pen moves. One-line fix.
2. Crash: WTPacketsPeek called with a NULL buffer (bug)
Flash calls WTPacketsPeek with a NULL destination buffer to count available
packets before fetching them. The copy path writes into address 0 → 0xc0000005
(write to 0x0). Guarding the copy with a NULL check on the destination fixes it
and matches the WinTab contract (NULL buffer = query count).
3. Functions resolved by ordinal
Flash imports wintab by ordinal, not by name, so with the default build it finds
nothing at all. I built the DLL with an explicit .def assigning the standard
WinTab ordinals (WTInfoW=1020, WTOpenW=1021, WTGetW=1061, WTClose=22,
WTPacketsGet=23, WTPacketsPeek=80, WTEnable=40, WTOverlap=41, WTQueueSizeGet=84,
WTQueueSizeSet=85) instead of relying on --kill-at. This is what lets ordinal-
linking apps (a lot of older Windows creative software) load it.
4. WTInfo(0, 0) presence probe
Flash probes for a tablet by calling WTInfo with category 0 / index 0, expecting
the size of the default context back. That was unhandled, so Flash concluded
"no tablet". Returning sizeof(LOGCONTEXTW) when a device is selected makes the
probe succeed.
5. WTI_DEVICES capability queries
Before showing its pressure controls, Flash queries device capabilities
(WTI_DEVICES): the WTPKT data mask and the X/Y/NPRESSURE AXIS structures. Those
went unanswered. I added a WTI_DEVICES branch returning the mask and AXIS data,
with the pressure max read from the device (falling back to 2047 if unavailable).
6. Accept a pen that reports zero buttons
The device-acceptance check required at least one button. The Huion pen exposes
valuators (X/Y/pressure) but 0 buttons under libinput, so it was rejected. I
relaxed the check to accept a device that has valuators and >= 3 axes. This is
really a "non-Wacom tablet" generalization.
7. Re-binding a context without WTClose (Flash-specific, for discussion)
Flash opens a new wintab context for every document but never calls WTClose when
you close the last one — then opens again on the next document. The existing
guard rejected the second WTOpen, so pressure died until Flash was restarted. My
fix: if a context handle already exists, re-bind it under the lock (refresh
hwnd / logContext / enabled / status), re-send the CTXOPEN message, and return
the existing handle. This one is the most app-shaped of the set — happy to gate
it or rework it however you'd like.
A couple of these (the 2047 fallback, the re-bind) are pragmatic and I expect
you'll want them tightened for upstream — totally fine, just tell me how you'd
prefer them. Full write-up with the exact crash signatures for #1 and #2 is here:
https://github.com/gurppt/flashcs6linux_deploy/blob/main/xwintab/PATCHES.md
Thanks again for the project.
Hi Graham, and first — thank you. XWinTab turned out to be the only working
path to get real pen pressure into Adobe Flash Professional CS6 running on
Wine, on a non-Wacom Huion Kamvas Pro 22 display tablet. Wine's built-in
wintab gets nothing under XInput2/libinput, so your XCB/XInput2 approach is the
whole game here.
While getting Flash to drive it, I had to make 7 changes. Several are generic
(including two outright crash fixes in the current code), a couple are more
Flash-specific and probably deserve discussion. I'd be glad to open a PR — as
one branch or split however you prefer. Listing them here first so you can tell
me what you'd actually want upstream.
Environment: Wine 11.0 (stable), win32 prefix, Ubuntu/Pop!_OS 24.04, hid_uclogic
(DIGImend) in proprietary mode. Flash resolves wintab entry points by
ordinal, which is what surfaced most of this.
1. Crash: self-cast in the packet-peek iterator (bug)
In the WTPacketsPeek iteration path, the user-data pointer is cast from the wrong
variable (effectively
(PktPeekIterData *) datainstead of the passeduserData). The result is an access violation (0xc0000005, read near offset0x8) the instant the pen moves. One-line fix.
2. Crash: WTPacketsPeek called with a NULL buffer (bug)
Flash calls WTPacketsPeek with a NULL destination buffer to count available
packets before fetching them. The copy path writes into address 0 → 0xc0000005
(write to 0x0). Guarding the copy with a NULL check on the destination fixes it
and matches the WinTab contract (NULL buffer = query count).
3. Functions resolved by ordinal
Flash imports wintab by ordinal, not by name, so with the default build it finds
nothing at all. I built the DLL with an explicit
.defassigning the standardWinTab ordinals (WTInfoW=1020, WTOpenW=1021, WTGetW=1061, WTClose=22,
WTPacketsGet=23, WTPacketsPeek=80, WTEnable=40, WTOverlap=41, WTQueueSizeGet=84,
WTQueueSizeSet=85) instead of relying on
--kill-at. This is what lets ordinal-linking apps (a lot of older Windows creative software) load it.
4. WTInfo(0, 0) presence probe
Flash probes for a tablet by calling WTInfo with category 0 / index 0, expecting
the size of the default context back. That was unhandled, so Flash concluded
"no tablet". Returning
sizeof(LOGCONTEXTW)when a device is selected makes theprobe succeed.
5. WTI_DEVICES capability queries
Before showing its pressure controls, Flash queries device capabilities
(WTI_DEVICES): the WTPKT data mask and the X/Y/NPRESSURE AXIS structures. Those
went unanswered. I added a WTI_DEVICES branch returning the mask and AXIS data,
with the pressure max read from the device (falling back to 2047 if unavailable).
6. Accept a pen that reports zero buttons
The device-acceptance check required at least one button. The Huion pen exposes
valuators (X/Y/pressure) but 0 buttons under libinput, so it was rejected. I
relaxed the check to accept a device that has valuators and >= 3 axes. This is
really a "non-Wacom tablet" generalization.
7. Re-binding a context without WTClose (Flash-specific, for discussion)
Flash opens a new wintab context for every document but never calls WTClose when
you close the last one — then opens again on the next document. The existing
guard rejected the second WTOpen, so pressure died until Flash was restarted. My
fix: if a context handle already exists, re-bind it under the lock (refresh
hwnd / logContext / enabled / status), re-send the CTXOPEN message, and return
the existing handle. This one is the most app-shaped of the set — happy to gate
it or rework it however you'd like.
A couple of these (the 2047 fallback, the re-bind) are pragmatic and I expect
you'll want them tightened for upstream — totally fine, just tell me how you'd
prefer them. Full write-up with the exact crash signatures for #1 and #2 is here:
https://github.com/gurppt/flashcs6linux_deploy/blob/main/xwintab/PATCHES.md
Thanks again for the project.