Description
Describe the project you are working on
Lorien (https://github.com/mbrlabs/Lorien), which is an infinite-canvas drawing program that benefits from pen/wacom/stylus input method. This project uses Godot 3.4/3.x
Describe the problem or limitation you are having in your project
I am currently working to resolve a feature request to map the eraser tool to the eraser-end of the pen (mbrlabs/Lorien#118). Implementing this feature requires checking whether the pen is inverted or not. However, Godot engine treats both ends the same and does not currently provide enough information to determine which end of the pen is used.
Describe the feature / enhancement and how it helps to overcome the problem or limitation
Add pen_inverted boolean property and getter/setter functions (under InputEventMouseMotion) similar to how pen pressure is polled.
- pen_inverted is true when eraser-end is used and false otherwise.
void set_pen_inverted(bool p_inverted)
: requires platform-specific code to get status and then set event property.bool get_pen_inverted(void)
: used to poll inversion status
With this in place, my problem can be solved by polling inversion status and if-else statements to map a different tool on both ends of the pen.
Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams
My proposal is based off my initial prototyping work: hansemro/godot#1
Development branches:
- 3.x : https://github.com/hansemro/godot/commits/eraser-detect-3.x
- 4 (PR for master) : https://github.com/hansemro/godot/commits/eraser-detect-4
On a mouse-motion event, we leverage existing platform-specific APIs to determine and set the pen's inversion status. Once set, the event can be polled for pen_inverted property with get_pen_inverted
. We do not expect game/project developers to use set_pen_inverted
.
- Relevant API update commit: hansemro/godot@a2b5742
Most of the non-trivial work comes in handling platform-specific APIs. Below, I will cover some ideas that I have successfully tested in Godot 3.x
tree. For unhandled platforms, we can assume the pen to not be inverted.
Windows
Both wintab and winink APIs have accessible properties to poll inversion status. Note that only wintab is expected to work under wine.
wintab API
Check for TPS_INVERT flag in WTPacket.pkStatus.
- TPS_INVERT: https://github.com/Wacom-Developer/wacom-device-kit-windows/blob/d2bc78fe79d442a3d398f750357e46effbca1daa/Wintab%20CAD%20Test/SampleCode/WINTAB.H#L513
- pkStatus: https://github.com/Wacom-Developer/wacom-device-kit-windows/blob/d2bc78fe79d442a3d398f750357e46effbca1daa/Wintab%20CAD%20Test/SampleCode/PKTDEF.H#L170
Relevant prototype commit: hansemro/godot@2af8a58
winink API
Check for PEN_FLAG_INVERTED or PEN_FLAG_ERASER flags in POINTER_PEN_INFO.penFlags.
- pen flags: https://github.com/mingw-w64/mingw-w64/blob/35fa6bdbc14383c9078520492974ff5ab31ba796/mingw-w64-headers/include/winuser.h#L2679-L2680
- pen flags: https://docs.microsoft.com/en-us/windows/win32/inputmsg/pen-flags-constants
- POINTER_PEN_INFO: https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-pointer_pen_info
Relevant prototype commit: hansemro/godot@75ca2cc
Linux/X11
When input devices are polled, check if the word "eraser" is in the device's name. If it is present, mark the device in a device map as inverted. The strstr implementation is based off gtk/gdk's method for distinguishing PEN/ERASER input source: https://gitlab.com/gnome/gtk/-/blob/master/gdk/x11/gdkdevicemanager-xi.c#L235. Personally, I don't like this method and would prefer to handle BTN_TOOL_PEN/BTN_TOOL_RUBBER events.
Relevant prototype commits:
- hansemro/godot@a7f43e5
- this is needed to get slave pointer id and name instead of
Virtual core pointer
with non-unique ids. Without this, all devices will have the same device id and name which will break this implementation. - when processing events, we can use sourceid to get slave pointer id to retrieve pen_inverted status for that device from a map.
- this is needed to get slave pointer id and name instead of
- hansemro/godot@2dff406
macOS
Check if pointingDeviceType is NSPointingDeviceTypeEraser only on NSEventSubtypeTabletProximity subtype event.
- https://developer.apple.com/documentation/appkit/nspointingdevicetype/nspointingdevicetypeeraser?language=objc
- https://chromium.googlesource.com/chromium/src/+/a07e14909ad9a17cc721f42b67a6e5aa56d27bc7/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm#772
Relevant prototype commit: hansemro/godot@bc53003
Other Platforms
Android
- Godot's Android input handler needs some work to properly support stylus input in general.
Check if event.getToolType(0) == MotionEvent.TOOL_TYPE_ERASER.
https://source.android.com/devices/accessories/stylus
https://developer.android.com/reference/android/view/MotionEvent.html#TOOL_TYPE_ERASER
iOS
Cannot be implemented.
HTML5
TODO
Testing
Godot demo project to test functionality: https://github.com/hansemro/InvertedPenGodotTest
If this enhancement will not be used often, can it be worked around with a few lines of script?
Although this feature may not be used often, it cannot easily be worked around as different platforms handle pen inversion differently.
Is there a reason why this should be core and not an add-on in the asset library?
This feature will fill a hole in the already-existing pen/tablet/wacom support. This feature could also help distinguish Godot from other engines.
Activity