From 7c80ac6df7001ba8ad37c6cf3ab0dc70b57011dd Mon Sep 17 00:00:00 2001
From: Christoph Reichenbach <[EMAIL REDACTED]>
Date: Sun, 27 Aug 2023 06:20:29 +0000
Subject: [PATCH] API for pressure-sensitive pens + XInput2/Wayland
This patch adds an API for querying pressure-
sensitive pens, cf. SDL_pen.h:
- Enumerate all pens
- Get pen capabilities, names, GUIDs
- Distinguishes pens and erasers
- Distinguish attached and detached pens
- Pressure and tilt support
- Rotation, distance, throttle wheel support
(throttle wheel untested)
- Pen type and meta-information reporting
(partially tested)
Pen event reporting:
- Three new event structures: PenTip, PenMotion, and
PenButton
- Report location with sub-pixel precision
- Include axis and button status, is-eraser flag
Internal pen tracker, intended to be independent
of platform APIs, cf. SDL_pen_c.h:
- Track known pens
- Handle pen hotplugging
Automatic test:
- testautomation_pen.c
Other features:
- XInput2 implementation, incl. hotplugging
- Wayland implementation, incl. hotplugging
- Backward compatibility: pen events default to
emulating pens with mouse ID SDL_PEN_MOUSEID
- Can be toggled via SDL_HINT_PEN_NOT_MOUSE
- Test/demo program (testpen)
- Wacom pen feature identification by pen ID
Acknowledgements:
- Ping Cheng (Wacom) provided extensive feedback
on Wacom pen features and detection so that
hopefully untested Wacom devices have a
realistic chance of working out of the box.
---
VisualC-GDK/SDL/SDL.vcxproj | 2 +
VisualC-GDK/SDL/SDL.vcxproj.filters | 6 +
VisualC-WinRT/SDL-UWP.vcxproj | 2 +
VisualC-WinRT/SDL-UWP.vcxproj.filters | 6 +
VisualC/SDL/SDL.vcxproj | 2 +
VisualC/SDL/SDL.vcxproj.filters | 6 +
.../testautomation/testautomation.vcxproj | 6 +
Xcode/SDL/SDL.xcodeproj/project.pbxproj | 33 +
include/SDL3/SDL_events.h | 76 +-
include/SDL3/SDL_hints.h | 34 +
include/SDL3/SDL_pen.h | 285 +++
src/dynapi/SDL_dynapi.sym | 16 +
src/dynapi/SDL_dynapi_overrides.h | 16 +
src/dynapi/SDL_dynapi_procs.h | 8 +
src/dynapi/gendynapi.py | 2 +-
src/events/SDL_events.c | 54 +-
src/events/SDL_mouse.c | 24 +-
src/events/SDL_mouse_c.h | 3 +
src/events/SDL_pen.c | 1083 ++++++++++
src/events/SDL_pen_c.h | 336 +++
src/video/wayland/SDL_waylandevents.c | 463 +++-
src/video/wayland/SDL_waylandevents_c.h | 38 +-
src/video/x11/SDL_x11events.c | 185 +-
src/video/x11/SDL_x11events.h | 4 +
src/video/x11/SDL_x11pen.c | 694 ++++++
src/video/x11/SDL_x11pen.h | 54 +
src/video/x11/SDL_x11sym.h | 1 +
src/video/x11/SDL_x11video.c | 13 +-
src/video/x11/SDL_x11window.c | 38 +-
src/video/x11/SDL_x11xinput2.c | 180 +-
src/video/x11/SDL_x11xinput2.h | 3 +-
test/CMakeLists.txt | 1 +
test/testautomation.c | 1 +
test/testautomation_pen.c | 1908 +++++++++++++++++
test/testautomation_suites.h | 1 +
test/testpen.c | 496 +++++
36 files changed, 5841 insertions(+), 239 deletions(-)
create mode 100644 include/SDL3/SDL_pen.h
create mode 100644 src/events/SDL_pen.c
create mode 100644 src/events/SDL_pen_c.h
create mode 100644 src/video/x11/SDL_x11pen.c
create mode 100644 src/video/x11/SDL_x11pen.h
create mode 100644 test/testautomation_pen.c
create mode 100644 test/testpen.c
diff --git a/VisualC-GDK/SDL/SDL.vcxproj b/VisualC-GDK/SDL/SDL.vcxproj
index a2c5aa6db280..3d6451e2fbe6 100644
--- a/VisualC-GDK/SDL/SDL.vcxproj
+++ b/VisualC-GDK/SDL/SDL.vcxproj
@@ -330,6 +330,7 @@
<ClInclude Include="..\..\include\SDL3\SDL_opengles2_gl2ext.h" />
<ClInclude Include="..\..\include\SDL3\SDL_opengles2_gl2platform.h" />
<ClInclude Include="..\..\include\SDL3\SDL_opengles2_khrplatform.h" />
+ <ClInclude Include="..\..\include\SDL3\SDL_pen.h" />
<ClInclude Include="..\..\include\SDL3\SDL_pixels.h" />
<ClInclude Include="..\..\include\SDL3\SDL_platform.h" />
<ClInclude Include="..\..\include\SDL3\SDL_platform_defines.h" />
@@ -586,6 +587,7 @@
<ClCompile Include="..\..\src\events\SDL_events.c" />
<ClCompile Include="..\..\src\events\SDL_keyboard.c" />
<ClCompile Include="..\..\src\events\SDL_mouse.c" />
+ <ClCompile Include="..\..\src\events\SDL_pen.c" />
<ClCompile Include="..\..\src\events\SDL_quit.c" />
<ClCompile Include="..\..\src\events\SDL_touch.c" />
<ClCompile Include="..\..\src\events\SDL_windowevents.c" />
diff --git a/VisualC-GDK/SDL/SDL.vcxproj.filters b/VisualC-GDK/SDL/SDL.vcxproj.filters
index a5772a745340..2396f04b6ee4 100644
--- a/VisualC-GDK/SDL/SDL.vcxproj.filters
+++ b/VisualC-GDK/SDL/SDL.vcxproj.filters
@@ -300,6 +300,9 @@
<ClInclude Include="..\..\include\SDL3\SDL_opengles2_khrplatform.h">
<Filter>API Headers</Filter>
</ClInclude>
+ <ClInclude Include="..\..\include\SDL3\SDL_pen.h">
+ <Filter>API Headers</Filter>
+ </ClInclude>
<ClInclude Include="..\..\include\SDL3\SDL_pixels.h">
<Filter>API Headers</Filter>
</ClInclude>
@@ -940,6 +943,9 @@
<ClCompile Include="..\..\src\events\SDL_mouse.c">
<Filter>events</Filter>
</ClCompile>
+ <ClCompile Include="..\..\src\events\SDL_pen.c">
+ <Filter>events</Filter>
+ </ClCompile>
<ClCompile Include="..\..\src\events\SDL_quit.c">
<Filter>events</Filter>
</ClCompile>
diff --git a/VisualC-WinRT/SDL-UWP.vcxproj b/VisualC-WinRT/SDL-UWP.vcxproj
index f7eca1ef796b..2fad76fd31f8 100644
--- a/VisualC-WinRT/SDL-UWP.vcxproj
+++ b/VisualC-WinRT/SDL-UWP.vcxproj
@@ -67,6 +67,7 @@
<ClInclude Include="..\include\SDL3\SDL_mouse.h" />
<ClInclude Include="..\include\SDL3\SDL_mutex.h" />
<ClInclude Include="..\include\SDL3\SDL_opengles2.h" />
+ <ClInclude Include="..\include\SDL3\SDL_pen.h" />
<ClInclude Include="..\include\SDL3\SDL_pixels.h" />
<ClInclude Include="..\include\SDL3\SDL_platform.h" />
<ClInclude Include="..\include\SDL3\SDL_platform_defines.h" />
@@ -306,6 +307,7 @@
<ClCompile Include="..\src\events\SDL_events.c" />
<ClCompile Include="..\src\events\SDL_keyboard.c" />
<ClCompile Include="..\src\events\SDL_mouse.c" />
+ <ClCompile Include="..\src\events\SDL_pen.c" />
<ClCompile Include="..\src\events\SDL_quit.c" />
<ClCompile Include="..\src\events\SDL_touch.c" />
<ClCompile Include="..\src\events\SDL_windowevents.c" />
diff --git a/VisualC-WinRT/SDL-UWP.vcxproj.filters b/VisualC-WinRT/SDL-UWP.vcxproj.filters
index fc905ac9e852..747841ba1b48 100644
--- a/VisualC-WinRT/SDL-UWP.vcxproj.filters
+++ b/VisualC-WinRT/SDL-UWP.vcxproj.filters
@@ -105,6 +105,9 @@
<ClInclude Include="..\include\SDL3\SDL_opengles2.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\include\SDL3\SDL_pen.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="..\include\SDL3\SDL_pixels.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -552,6 +555,9 @@
<ClCompile Include="..\src\events\SDL_mouse.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\src\events\SDL_pen.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\src\events\SDL_quit.c">
<Filter>Source Files</Filter>
</ClCompile>
diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj
index fc53003e73fb..0dee2d50069f 100644
--- a/VisualC/SDL/SDL.vcxproj
+++ b/VisualC/SDL/SDL.vcxproj
@@ -280,6 +280,7 @@
<ClInclude Include="..\..\include\SDL3\SDL_opengles2_gl2ext.h" />
<ClInclude Include="..\..\include\SDL3\SDL_opengles2_gl2platform.h" />
<ClInclude Include="..\..\include\SDL3\SDL_opengles2_khrplatform.h" />
+ <ClInclude Include="..\..\include\SDL3\SDL_pen.h" />
<ClInclude Include="..\..\include\SDL3\SDL_pixels.h" />
<ClInclude Include="..\..\include\SDL3\SDL_platform.h" />
<ClInclude Include="..\..\include\SDL3\SDL_platform_defines.h" />
@@ -507,6 +508,7 @@
<ClCompile Include="..\..\src\events\SDL_events.c" />
<ClCompile Include="..\..\src\events\SDL_keyboard.c" />
<ClCompile Include="..\..\src\events\SDL_mouse.c" />
+ <ClCompile Include="..\..\src\events\SDL_pen.c" />
<ClCompile Include="..\..\src\events\SDL_quit.c" />
<ClCompile Include="..\..\src\events\SDL_touch.c" />
<ClCompile Include="..\..\src\events\SDL_windowevents.c" />
diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters
index c9d6f36e5d47..fbaadb802a5c 100644
--- a/VisualC/SDL/SDL.vcxproj.filters
+++ b/VisualC/SDL/SDL.vcxproj.filters
@@ -294,6 +294,9 @@
<ClInclude Include="..\..\include\SDL3\SDL_opengles2_khrplatform.h">
<Filter>API Headers</Filter>
</ClInclude>
+ <ClInclude Include="..\..\include\SDL3\SDL_pen.h">
+ <Filter>API Headers</Filter>
+ </ClInclude>
<ClInclude Include="..\..\include\SDL3\SDL_pixels.h">
<Filter>API Headers</Filter>
</ClInclude>
@@ -921,6 +924,9 @@
<ClCompile Include="..\..\src\events\SDL_mouse.c">
<Filter>events</Filter>
</ClCompile>
+ <ClCompile Include="..\..\src\events\SDL_pen.c">
+ <Filter>events</Filter>
+ </ClCompile>
<ClCompile Include="..\..\src\events\SDL_quit.c">
<Filter>events</Filter>
</ClCompile>
diff --git a/VisualC/tests/testautomation/testautomation.vcxproj b/VisualC/tests/testautomation/testautomation.vcxproj
index 260617b4a872..dbe1f3d8c2e1 100644
--- a/VisualC/tests/testautomation/testautomation.vcxproj
+++ b/VisualC/tests/testautomation/testautomation.vcxproj
@@ -209,6 +209,12 @@
<ClCompile Include="..\..\..\test\testautomation_main.c" />
<ClCompile Include="..\..\..\test\testautomation_math.c" />
<ClCompile Include="..\..\..\test\testautomation_mouse.c" />
+ <ClCompile Include="..\..\..\test\testautomation_pen.c">
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(ProjectDir)\..\..\..\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(ProjectDir)\..\..\..\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectDir)\..\..\..\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(ProjectDir)\..\..\..\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ClCompile>
<ClCompile Include="..\..\..\test\testautomation_pixels.c" />
<ClCompile Include="..\..\..\test\testautomation_platform.c" />
<ClCompile Include="..\..\..\test\testautomation_properties.c" />
diff --git a/Xcode/SDL/SDL.xcodeproj/project.pbxproj b/Xcode/SDL/SDL.xcodeproj/project.pbxproj
index 32f2baf0f119..66ead99a18f7 100644
--- a/Xcode/SDL/SDL.xcodeproj/project.pbxproj
+++ b/Xcode/SDL/SDL.xcodeproj/project.pbxproj
@@ -43,6 +43,27 @@
00D0D0D810675E46004B05EF /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 007317C10858E15000B2BC32 /* Carbon.framework */; platformFilters = (macos, ); };
557D0CFA254586CA003913E3 /* CoreHaptics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F37DC5F225350EBC0002E6F7 /* CoreHaptics.framework */; platformFilters = (ios, maccatalyst, macos, tvos, ); };
557D0CFB254586D7003913E3 /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A75FDABD23E28B6200529352 /* GameController.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
+ 63125C002A790B12008EF011 /* SDL_pen.h in Headers */ = {isa = PBXBuildFile; fileRef = 63125BFF2A790B12008EF011 /* SDL_pen.h */; };
+ 63125C012A790B12008EF011 /* SDL_pen.h in Headers */ = {isa = PBXBuildFile; fileRef = 63125BFF2A790B12008EF011 /* SDL_pen.h */; };
+ 63125C022A790B12008EF011 /* SDL_pen.h in Headers */ = {isa = PBXBuildFile; fileRef = 63125BFF2A790B12008EF011 /* SDL_pen.h */; };
+ 63125C0A2A790B69008EF011 /* SDL_pen.c in Sources */ = {isa = PBXBuildFile; fileRef = 63125C092A790B69008EF011 /* SDL_pen.c */; };
+ 63125C0B2A790B69008EF011 /* SDL_pen.c in Sources */ = {isa = PBXBuildFile; fileRef = 63125C092A790B69008EF011 /* SDL_pen.c */; };
+ 63125C0C2A790B69008EF011 /* SDL_pen.c in Sources */ = {isa = PBXBuildFile; fileRef = 63125C092A790B69008EF011 /* SDL_pen.c */; };
+ 63125C0D2A790B69008EF011 /* SDL_pen.c in Sources */ = {isa = PBXBuildFile; fileRef = 63125C092A790B69008EF011 /* SDL_pen.c */; };
+ 63125C0E2A790B69008EF011 /* SDL_pen.c in Sources */ = {isa = PBXBuildFile; fileRef = 63125C092A790B69008EF011 /* SDL_pen.c */; };
+ 63125C0F2A790B69008EF011 /* SDL_pen.c in Sources */ = {isa = PBXBuildFile; fileRef = 63125C092A790B69008EF011 /* SDL_pen.c */; };
+ 63125C102A790B69008EF011 /* SDL_pen.c in Sources */ = {isa = PBXBuildFile; fileRef = 63125C092A790B69008EF011 /* SDL_pen.c */; };
+ 63125C112A790B69008EF011 /* SDL_pen.c in Sources */ = {isa = PBXBuildFile; fileRef = 63125C092A790B69008EF011 /* SDL_pen.c */; };
+ 63125C122A790B69008EF011 /* SDL_pen.c in Sources */ = {isa = PBXBuildFile; fileRef = 63125C092A790B69008EF011 /* SDL_pen.c */; };
+ 63125C142A790B9A008EF011 /* SDL_pen_c.h in Headers */ = {isa = PBXBuildFile; fileRef = 63125C132A790B9A008EF011 /* SDL_pen_c.h */; };
+ 63125C152A790B9A008EF011 /* SDL_pen_c.h in Headers */ = {isa = PBXBuildFile; fileRef = 63125C132A790B9A008EF011 /* SDL_pen_c.h */; };
+ 63125C162A790B9A008EF011 /* SDL_pen_c.h in Headers */ = {isa = PBXBuildFile; fileRef = 63125C132A790B9A008EF011 /* SDL_pen_c.h */; };
+ 63125C172A790B9A008EF011 /* SDL_pen_c.h in Headers */ = {isa = PBXBuildFile; fileRef = 63125C132A790B9A008EF011 /* SDL_pen_c.h */; };
+ 63125C182A790B9A008EF011 /* SDL_pen_c.h in Headers */ = {isa = PBXBuildFile; fileRef = 63125C132A790B9A008EF011 /* SDL_pen_c.h */; };
+ 63125C192A790B9A008EF011 /* SDL_pen_c.h in Headers */ = {isa = PBXBuildFile; fileRef = 63125C132A790B9A008EF011 /* SDL_pen_c.h */; };
+ 63125C1A2A790B9A008EF011 /* SDL_pen_c.h in Headers */ = {isa = PBXBuildFile; fileRef = 63125C132A790B9A008EF011 /* SDL_pen_c.h */; };
+ 63125C1B2A790B9A008EF011 /* SDL_pen_c.h in Headers */ = {isa = PBXBuildFile; fileRef = 63125C132A790B9A008EF011 /* SDL_pen_c.h */; };
+ 63125C1C2A790B9A008EF011 /* SDL_pen_c.h in Headers */ = {isa = PBXBuildFile; fileRef = 63125C132A790B9A008EF011 /* SDL_pen_c.h */; };
5616CA4C252BB2A6005D5928 /* SDL_url.c in Sources */ = {isa = PBXBuildFile; fileRef = 5616CA49252BB2A5005D5928 /* SDL_url.c */; };
5616CA4D252BB2A6005D5928 /* SDL_sysurl.h in Headers */ = {isa = PBXBuildFile; fileRef = 5616CA4A252BB2A6005D5928 /* SDL_sysurl.h */; };
5616CA4E252BB2A6005D5928 /* SDL_sysurl.m in Sources */ = {isa = PBXBuildFile; fileRef = 5616CA4B252BB2A6005D5928 /* SDL_sysurl.m */; };
@@ -52,6 +73,9 @@
566E26D8246274CC00718109 /* SDL_locale.c in Sources */ = {isa = PBXBuildFile; fileRef = 566E26CD246274CB00718109 /* SDL_locale.c */; };
566E26E1246274CC00718109 /* SDL_syslocale.h in Headers */ = {isa = PBXBuildFile; fileRef = 566E26CE246274CC00718109 /* SDL_syslocale.h */; };
56A2373329F9C113003CCA5F /* SDL_sysrwlock.c in Sources */ = {isa = PBXBuildFile; fileRef = 56A2373229F9C113003CCA5F /* SDL_sysrwlock.c */; };
+ 63134A222A7902CF0021E9A6 /* SDL_pen.h in Headers */ = {isa = PBXBuildFile; fileRef = 63134A212A7902CF0021E9A6 /* SDL_pen.h */; };
+ 63134A252A7902FD0021E9A6 /* SDL_pen_c.h in Headers */ = {isa = PBXBuildFile; fileRef = 63134A232A7902FD0021E9A6 /* SDL_pen_c.h */; };
+ 63134A262A7902FD0021E9A6 /* SDL_pen.c in Sources */ = {isa = PBXBuildFile; fileRef = 63134A242A7902FD0021E9A6 /* SDL_pen.c */; };
75E0915A241EA924004729E1 /* SDL_virtualjoystick.c in Sources */ = {isa = PBXBuildFile; fileRef = 75E09158241EA924004729E1 /* SDL_virtualjoystick.c */; };
75E09163241EA924004729E1 /* SDL_virtualjoystick_c.h in Headers */ = {isa = PBXBuildFile; fileRef = 75E09159241EA924004729E1 /* SDL_virtualjoystick_c.h */; };
9846B07C287A9020000C35C8 /* SDL_hidapi_shield.c in Sources */ = {isa = PBXBuildFile; fileRef = 9846B07B287A9020000C35C8 /* SDL_hidapi_shield.c */; };
@@ -534,6 +558,9 @@
566E26CD246274CB00718109 /* SDL_locale.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_locale.c; path = locale/SDL_locale.c; sourceTree = "<group>"; };
566E26CE246274CC00718109 /* SDL_syslocale.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_syslocale.h; path = locale/SDL_syslocale.h; sourceTree = "<group>"; };
56A2373229F9C113003CCA5F /* SDL_sysrwlock.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_sysrwlock.c; sourceTree = "<group>"; };
+ 63134A212A7902CF0021E9A6 /* SDL_pen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_pen.h; path = SDL3/SDL_pen.h; sourceTree = "<group>"; };
+ 63134A232A7902FD0021E9A6 /* SDL_pen_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_pen_c.h; sourceTree = "<group>"; };
+ 63134A242A7902FD0021E9A6 /* SDL_pen.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_pen.c; sourceTree = "<group>"; };
75E09158241EA924004729E1 /* SDL_virtualjoystick.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_virtualjoystick.c; sourceTree = "<group>"; };
75E09159241EA924004729E1 /* SDL_virtualjoystick_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_virtualjoystick_c.h; sourceTree = "<group>"; };
9846B07B287A9020000C35C8 /* SDL_hidapi_shield.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_shield.c; sourceTree = "<group>"; };
@@ -1093,6 +1120,7 @@
F3F7D8C92933074B00816151 /* SDL_opengles2_gl2platform.h */,
F3F7D8B12933074900816151 /* SDL_opengles2_khrplatform.h */,
F3F7D8C72933074B00816151 /* SDL_opengles2.h */,
+ 63134A212A7902CF0021E9A6 /* SDL_pen.h */,
F3F7D8B52933074A00816151 /* SDL_pixels.h */,
F3B38CCB296E2E52005DA6D3 /* SDL_platform_defines.h */,
F3F7D8AB2933074900816151 /* SDL_platform.h */,
@@ -2059,6 +2087,8 @@
A7D8A93823E2514000DCD162 /* SDL_keyboard.c */,
A7D8A92B23E2514000DCD162 /* SDL_mouse_c.h */,
A7D8A92A23E2514000DCD162 /* SDL_mouse.c */,
+ 63134A232A7902FD0021E9A6 /* SDL_pen_c.h */,
+ 63134A242A7902FD0021E9A6 /* SDL_pen.c */,
A7D8A93C23E2514000DCD162 /* SDL_quit.c */,
A7D8A93723E2514000DCD162 /* SDL_touch_c.h */,
A7D8A93E23E2514000DCD162 /* SDL_touch.c */,
@@ -2365,6 +2395,8 @@
A7D8B3D423E2514300DCD162 /* yuv_rgb.h in Headers */,
A7D8B3C823E2514200DCD162 /* yuv_rgb_sse_func.h in Headers */,
A7D8B3CE23E2514300DCD162 /* yuv_rgb_std_func.h in Headers */,
+ 63134A222A7902CF0021E9A6 /* SDL_pen.h in Headers */,
+ 63134A252A7902FD0021E9A6 /* SDL_pen_c.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -2685,6 +2717,7 @@
A7D8AEA023E2514100DCD162 /* SDL_cocoavulkan.m in Sources */,
A7D8AB6123E2514100DCD162 /* SDL_offscreenwindow.c in Sources */,
566E26D8246274CC00718109 /* SDL_locale.c in Sources */,
+ 63134A262A7902FD0021E9A6 /* SDL_pen.c in Sources */,
000040E76FDC6AE48CBF0000 /* SDL_hashtable.c in Sources */,
0000A4DA2F45A31DC4F00000 /* SDL_sysmain_callbacks.m in Sources */,
000028F8113A53F4333E0000 /* SDL_main_callbacks.c in Sources */,
diff --git a/include/SDL3/SDL_events.h b/include/SDL3/SDL_events.h
index dcc9ece04a72..186f9f40787a 100644
--- a/include/SDL3/SDL_events.h
+++ b/include/SDL3/SDL_events.h
@@ -34,6 +34,7 @@
#include <SDL3/SDL_joystick.h>
#include <SDL3/SDL_keyboard.h>
#include <SDL3/SDL_mouse.h>
+#include <SDL3/SDL_pen.h>
#include <SDL3/SDL_quit.h>
#include <SDL3/SDL_stdinc.h>
#include <SDL3/SDL_touch.h>
@@ -113,6 +114,8 @@ typedef enum
SDL_EVENT_WINDOW_RESTORED, /**< Window has been restored to normal size and position */
SDL_EVENT_WINDOW_MOUSE_ENTER, /**< Window has gained mouse focus */
SDL_EVENT_WINDOW_MOUSE_LEAVE, /**< Window has lost mouse focus */
+ SDL_EVENT_WINDOW_PEN_ENTER, /**< Window has gained focus of the pressure-sensitive pen with ID "data1" */
+ SDL_EVENT_WINDOW_PEN_LEAVE, /**< Window has lost focus of the pressure-sensitive pen with ID "data1" */
SDL_EVENT_WINDOW_FOCUS_GAINED, /**< Window has gained keyboard focus */
SDL_EVENT_WINDOW_FOCUS_LOST, /**< Window has lost keyboard focus */
SDL_EVENT_WINDOW_CLOSE_REQUESTED, /**< The window manager requests that the window be closed */
@@ -191,6 +194,13 @@ typedef enum
/* Sensor events */
SDL_EVENT_SENSOR_UPDATE = 0x1200, /**< A sensor was updated */
+ /* Pressure-sensitive pen events */
+ SDL_EVENT_PEN_DOWN = 0x1300, /**< Pressure-sensitive pen touched drawing surface */
+ SDL_EVENT_PEN_UP, /**< Pressure-sensitive pen stopped touching drawing surface */
+ SDL_EVENT_PEN_MOTION, /**< Pressure-sensitive pen moved, or angle/pressure changed */
+ SDL_EVENT_PEN_BUTTON_DOWN, /**< Pressure-sensitive pen button pressed */
+ SDL_EVENT_PEN_BUTTON_UP, /**< Pressure-sensitive pen button released */
+
/* Render events */
SDL_EVENT_RENDER_TARGETS_RESET = 0x2000, /**< The render targets have been reset and their contents need to be updated */
SDL_EVENT_RENDER_DEVICE_RESET, /**< The device has been reset and all textures need to be recreated */
@@ -296,7 +306,7 @@ typedef struct SDL_MouseMotionEvent
Uint32 type; /**< ::SDL_EVENT_MOUSE_MOTION */
Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */
SDL_WindowID windowID; /**< The window with mouse focus, if any */
- SDL_MouseID which; /**< The mouse instance id, or SDL_TOUCH_MOUSEID */
+ SDL_MouseID which; /**< The mouse instance id, SDL_TOUCH_MOUSEID, or SDL_PEN_MOUSEID */
Uint32 state; /**< The current button state */
float x; /**< X coordinate, relative to window */
float y; /**< Y coordinate, relative to window */
@@ -312,7 +322,7 @@ typedef struct SDL_MouseButtonEvent
Uint32 type; /**< ::SDL_EVENT_MOUSE_BUTTON_DOWN or ::SDL_EVENT_MOUSE_BUTTON_UP */
Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */
SDL_WindowID windowID; /**< The window with mouse focus, if any */
- SDL_MouseID which; /**< The mouse instance id, or SDL_TOUCH_MOUSEID */
+ SDL_MouseID which; /**< The mouse instance id, SDL_TOUCH_MOUSEID, or SDL_PEN_MOUSEID */
Uint8 button; /**< The mouse button index */
Uint8 state; /**< ::SDL_PRESSED or ::SDL_RELEASED */
Uint8 clicks; /**< 1 for single-click, 2 for double-click, etc. */
@@ -329,7 +339,7 @@ typedef struct SDL_MouseWheelEvent
Uint32 type; /**< ::SDL_EVENT_MOUSE_WHEEL */
Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */
SDL_WindowID windowID; /**< The window with mouse focus, if any */
- SDL_MouseID which; /**< The mouse instance id, or SDL_TOUCH_MOUSEID */
+ SDL_MouseID which; /**< The mouse instance id, SDL_TOUCH_MOUSEID, or SDL_PEN_MOUSEID */
float x; /**< The amount scrolled horizontally, positive to the right and negative to the left */
float y; /**< The amount scrolled vertically, positive away from the user and negative toward the user */
Uint32 direction; /**< Set to one of the SDL_MOUSEWHEEL_* defines. When FLIPPED the values in X and Y will be opposite. Multiply by -1 to change them back */
@@ -512,6 +522,63 @@ typedef struct SDL_TouchFingerEvent
#define SDL_DROPEVENT_DATA_SIZE 64
+/**
+ * Pressure-sensitive pen touched or stopped touching surface (event.ptip.*)
+ */
+typedef struct SDL_PenTipEvent
+{
+ Uint32 type; /**< ::SDL_EVENT_PEN_DOWN or ::SDL_EVENT_PEN_UP */
+ Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */
+ Uint32 windowID; /**< The window with pen focus, if any */
+ SDL_PenID which; /**< The pen instance id */
+ Uint8 tip; /**< ::SDL_PEN_TIP_INK when using a regular pen tip, or ::SDL_PEN_TIP_ERASER if the pen is being used as an eraser (e.g., flipped to use the eraser tip) */
+ Uint8 state; /**< ::SDL_PRESSED on ::SDL_EVENT_PEN_DOWN and ::SDL_RELEASED on ::SDL_EVENT_PEN_UP */
+ Uint16 pen_state; /**< Pen button masks (where SDL_BUTTON(1) is the first button, SDL_BUTTON(2) is the second button etc.),
+ ::SDL_PEN_DOWN_MASK is set if the pen is touching the surface, and
+ ::SDL_PEN_ERASER_MASK is set if the pen is (used as) an eraser. */
+ float x; /**< X coordinate, relative to window */
+ float y; /**< Y coordinate, relative to window */
+ float axes[SDL_PEN_NUM_AXES]; /**< Pen axes such as pressure and tilt (ordered as per ::SDL_PenAxis) */
+} SDL_PenTipEvent;
+
+/**
+ * Pressure-sensitive pen motion / pressure / angle event structure (event.pmotion.*)
+ */
+typedef struct SDL_PenMotionEvent
+{
+ Uint32 type; /**< ::SDL_EVENT_PEN_MOTION */
+ Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */
+ Uint32 windowID; /**< The window with pen focus, if any */
+ SDL_PenID which; /**< The pen instance id */
+ Uint8 padding1;
+ Uint8 padding2;
+ Uint16 pen_state; /**< Pen button masks (where SDL_BUTTON(1) is the first button, SDL_BUTTON(2) is the second button etc.),
+ ::SDL_PEN_DOWN_MASK is set if the pen is touching the surface, and
+ ::SDL_PEN_ERASER_MASK is set if the pen is (used as) an eraser. */
+ float x; /**< X coordinate, relative to window */
+ float y; /**< Y coordinate, relative to window */
+ float axes[SDL_PEN_NUM_AXES]; /**< Pen axes such as pressure and tilt (ordered as per ::SDL_PenAxis) */
+} SDL_PenMotionEvent;
+
+/**
+ * Pressure-sensitive pen button event structure (event.pbutton.*)
+ */
+typedef struct SDL_PenButtonEvent
+{
+ Uint32 type; /**< ::SDL_EVENT_PEN_BUTTON_DOWN or ::SDL_EVENT_PEN_BUTTON_UP */
+ Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */
+ Uint32 windowID; /**< The window with pen focus, if any */
+ SDL_PenID which; /**< The pen instance id */
+ Uint8 button; /**< The pen button index (1 represents the pen tip for compatibility with mouse events) */
+ Uint8 state; /**< ::SDL_PRESSED or ::SDL_RELEASED */
+ Uint16 pen_state; /**< Pen button masks (where SDL_BUTTON(1) is the first button, SDL_BUTTON(2) is the second button etc.),
+ ::SDL_PEN_DOWN_MASK is set if the pen is touching the surface, and
+ ::SDL_PEN_ERASER_MASK is set if the pen is (used as) an eraser. */
+ float x; /**< X coordinate, relative to window */
+ float y; /**< Y coordinate, relative to window */
+ float axes[SDL_PEN_NUM_AXES]; /**< Pen axes such as pressure and tilt (ordered as per ::SDL_PenAxis) */
+} SDL_PenButtonEvent;
+
/**
* An event used to drop text or request a file open by the system (event.drop.*)
*
@@ -603,6 +670,9 @@ typedef union SDL_Event
SDL_QuitEvent quit; /**< Quit request event data */
SDL_UserEvent user; /**< Custom event data */
SDL_TouchFingerEvent tfinger; /**< Touch finger event data */
+ SDL_PenTipEvent ptip; /**< Pen tip touching or leaving drawing surface */
+ SDL_PenMotionEvent pmotion; /**< Pen change in position, pressure, or angle */
+ SDL_PenButtonEvent pbutton; /**< Pen button press */
SDL_DropEvent drop; /**< Drag and drop event data */
SDL_ClipboardEvent clipboard; /**< Clipboard event data */
diff --git a/include/SDL3/SDL_hints.h b/include/SDL3/SDL_hints.h
index 0e4bf7565231..91d7f0670ca5 100644
--- a/include/SDL3/SDL_hints.h
+++ b/include/SDL3/SDL_hints.h
@@ -1220,6 +1220,40 @@ extern "C" {
*/
#define SDL_HINT_MOUSE_AUTO_CAPTURE "SDL_MOUSE_AUTO_CAPTURE"
+/**
+ * Treat pen movement as separate from mouse movement
+ *
+ * By default, pens report both ::SDL_MouseMotionEvent and ::SDL_PenMotionEvent updates
+ * (analogously for button presses). This hint allows decoupling mouse and pen updates.
+ *
+ * This variable toggles between the following behaviour:
+ * "0" - (Default) Pen acts as a mouse with mouse ID ::SDL_PEN_MOUSEID.
+ * Use case: client application is not pen aware, user wants to
+ * use pen instead of mouse to interact.
+ * "1" - Pen reports mouse clicks and movement events but does not update
+ * SDL-internal mouse state (buttons pressed, current mouse location).
+ * Use case: client application is not pen aware, user frequently
+ * alternates between pen and "real" mouse.
+ * "2" - Pen reports no mouse events.
+ * Use case: pen-aware client application uses this hint to allow user to
+ * toggle between pen+mouse mode ("2") and pen-only mode ("1" or "0").
+ */
+#define SDL_HINT_PEN_NOT_MOUSE "SDL_HINT_PEN_NOT_MOUSE"
+
+/**
+ * Pen mouse button emulation triggers only when the pen touches the tablet surface
+ *
+ * "0" - The pen reports mouse button press/release immediately when the pen
+ * button is pressed/released, and the pen tip touching the surface counts
+ * as left mouse button press.
+ * "1" - (Default) Mouse button presses are sent when the pen first touches
+ * the tablet (analogously for releases). Not pressing a pen button
+ * simulates mouse button 1, pressing the first pen button simulates
+ * mouse button 2 etc.; it is not possible to report multiple buttons
+ * as pressed at the same time.
+ */
+#define SDL_HINT_PEN_DELAY_MOUSE_BUTTON "SDL_HINT_PEN_DELAY_MOUSE_BUTTON"
+
/**
* Tell SDL not to catch the SIGINT or SIGTERM signals.
*
diff --git a/include/SDL3/SDL_pen.h b/include/SDL3/SDL_pen.h
new file mode 100644
index 000000000000..0f48da90abac
--- /dev/null
+++ b/include/SDL3/SDL_pen.h
@@ -0,0 +1,285 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim tha
(Patch may be truncated, please check the link at the top of this post.)