summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/video/uikit/SDL_uikitpen.m
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
committer3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
commit5a079a2d114f96d4847d1ee305d5b7c16eeec50e (patch)
tree8926ab44f168acf787d8e19608857b3af0f82758 /contrib/SDL-3.2.8/src/video/uikit/SDL_uikitpen.m
Initial commit
Diffstat (limited to 'contrib/SDL-3.2.8/src/video/uikit/SDL_uikitpen.m')
-rw-r--r--contrib/SDL-3.2.8/src/video/uikit/SDL_uikitpen.m214
1 files changed, 214 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/video/uikit/SDL_uikitpen.m b/contrib/SDL-3.2.8/src/video/uikit/SDL_uikitpen.m
new file mode 100644
index 0000000..5209681
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/uikit/SDL_uikitpen.m
@@ -0,0 +1,214 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifdef SDL_VIDEO_DRIVER_UIKIT
24
25#include "SDL_uikitevents.h"
26#include "SDL_uikitpen.h"
27#include "SDL_uikitwindow.h"
28
29#include "../../events/SDL_pen_c.h"
30
31// Fix build errors when using an older SDK by defining these selectors
32#if !defined(SDL_PLATFORM_TVOS)
33
34@interface UITouch (SDL)
35#if !(__IPHONE_OS_VERSION_MAX_ALLOWED >= 170500)
36@property (nonatomic, readonly) CGFloat rollAngle;
37#endif
38@end
39
40@interface UIHoverGestureRecognizer (SDL)
41#if !(__IPHONE_OS_VERSION_MAX_ALLOWED >= 160100)
42@property (nonatomic, readonly) CGFloat zOffset;
43#endif
44#if !(__IPHONE_OS_VERSION_MAX_ALLOWED >= 160400)
45- (CGFloat) azimuthAngleInView:(UIView *) view;
46
47@property (nonatomic, readonly) CGFloat altitudeAngle;
48#endif
49#if !(__IPHONE_OS_VERSION_MAX_ALLOWED >= 170500)
50@property (nonatomic, readonly) CGFloat rollAngle;
51#endif
52@end
53
54#endif // !SDL_PLATFORM_TVOS
55
56static SDL_PenID apple_pencil_id = 0;
57
58bool UIKit_InitPen(SDL_VideoDevice *_this)
59{
60 return true;
61}
62
63// we only have one Apple Pencil at a time, and it must be paired to the iOS device.
64// We only know about its existence when it first sends an event, so add an single SDL pen
65// device here if we haven't already.
66static SDL_PenID UIKit_AddPenIfNecesary()
67{
68 if (!apple_pencil_id) {
69 SDL_PenInfo info;
70 SDL_zero(info);
71 info.capabilities = SDL_PEN_CAPABILITY_PRESSURE | SDL_PEN_CAPABILITY_XTILT | SDL_PEN_CAPABILITY_YTILT;
72 info.max_tilt = 90.0f;
73 info.num_buttons = 0;
74 info.subtype = SDL_PEN_TYPE_PENCIL;
75
76 if (@available(iOS 17.5, *)) { // need rollAngle method.
77 info.capabilities |= SDL_PEN_CAPABILITY_ROTATION;
78 }
79
80 if (@available(ios 16.1, *)) { // need zOffset method.
81 info.capabilities |= SDL_PEN_CAPABILITY_DISTANCE;
82 }
83
84 // Apple Pencil and iOS can report when the pencil is being "squeezed" but it's a boolean thing,
85 // so we can't use it for tangential pressure.
86
87 // There's only ever one Apple Pencil at most, so we just pass a non-zero value for the handle.
88 apple_pencil_id = SDL_AddPenDevice(0, "Apple Pencil", &info, (void *) (size_t) 0x1);
89 }
90
91 return apple_pencil_id;
92}
93
94static void UIKit_HandlePenAxes(SDL_Window *window, NSTimeInterval nstimestamp, float zOffset, const CGPoint *point, float force,
95 float maximumPossibleForce, float azimuthAngleInView, float altitudeAngle, float rollAngle)
96{
97 const SDL_PenID penId = UIKit_AddPenIfNecesary();
98 if (penId) {
99 const Uint64 timestamp = UIKit_GetEventTimestamp(nstimestamp);
100 const float radians_to_degrees = 180.0f / SDL_PI_F;
101
102 // Normalize force to 0.0f ... 1.0f range.
103 const float pressure = force / maximumPossibleForce;
104
105 // azimuthAngleInView is in radians, with 0 being the pen's back end pointing due east on the screen when the
106 // tip is touching the screen, and negative when heading north from there, positive to the south.
107 // So convert to degrees, 0 being due east, etc.
108 const float azimuth_angle = azimuthAngleInView * radians_to_degrees;
109
110 // altitudeAngle is in radians, with 0 being the pen laying flat on (parallel to) the device
111 // screen and PI/2 being it pointing straight up from (perpendicular to) the device screen.
112 // So convert to degrees, 0 being flat and 90 being straight up.
113 const float altitude_angle = altitudeAngle * radians_to_degrees;
114
115 // the azimuth_angle goes from -180 to 180 (with abs(angle) moving from 180 to 0, left to right), but SDL wants
116 // it from -90 (back facing west) to 90 (back facing east).
117 const float xtilt = (180.0f - SDL_fabsf(azimuth_angle)) - 90.0f;
118
119 // the altitude_angle goes from 0 to 90 regardless of which direction the pen is lifting off the device, but SDL wants
120 // it from -90 (flat facing north) to 90 (flat facing south).
121 const float ytilt = (azimuth_angle < 0.0f) ? -(90.0f - altitude_angle) : (90.0f - altitude_angle);
122
123 // rotation is in radians, and only available on a later iOS.
124 const float rotation = rollAngle * radians_to_degrees; // !!! FIXME: this might need adjustment, I don't have a pencil that supports it.
125
126 SDL_SendPenMotion(timestamp, penId, window, point->x, point->y);
127 SDL_SendPenAxis(timestamp, penId, window, SDL_PEN_AXIS_PRESSURE, pressure);
128 SDL_SendPenAxis(timestamp, penId, window, SDL_PEN_AXIS_XTILT, xtilt);
129 SDL_SendPenAxis(timestamp, penId, window, SDL_PEN_AXIS_YTILT, ytilt);
130 SDL_SendPenAxis(timestamp, penId, window, SDL_PEN_AXIS_ROTATION, rotation);
131 SDL_SendPenAxis(timestamp, penId, window, SDL_PEN_AXIS_DISTANCE, zOffset);
132 }
133}
134
135#if !defined(SDL_PLATFORM_TVOS)
136extern void UIKit_HandlePenHover(SDL_uikitview *view, UIHoverGestureRecognizer *recognizer)
137{
138 float zOffset = 0.0f;
139 if (@available(iOS 16.1, *)) {
140 zOffset = (float) [recognizer zOffset];
141 }
142
143 float azimuthAngleInView = 0.0f;
144 if (@available(iOS 16.4, *)) {
145 azimuthAngleInView = (float) [recognizer azimuthAngleInView:view];
146 }
147
148 float altitudeAngle = 0.0f;
149 if (@available(iOS 16.4, *)) {
150 altitudeAngle = (float) [recognizer altitudeAngle];
151 }
152
153 float rollAngle = 0.0f;
154 if (@available(iOS 17.5, *)) {
155 rollAngle = (float) [recognizer rollAngle];
156 }
157
158 SDL_Window *window = [view getSDLWindow];
159 const CGPoint point = [recognizer locationInView:view];
160
161 // force is zero here; if you're here, you're not touching.
162 // !!! FIXME: no timestamp on these...?
163 UIKit_HandlePenAxes(window, 0, zOffset, &point, 0.0f, 1.0f, azimuthAngleInView, altitudeAngle, rollAngle);
164}
165#endif
166
167static void UIKit_HandlePenAxesFromUITouch(SDL_uikitview *view, UITouch *pencil)
168{
169 float rollAngle = 0.0f;
170#if !defined(SDL_PLATFORM_TVOS)
171 if (@available(iOS 17.5, *)) {
172 rollAngle = (float) [pencil rollAngle];
173 }
174#endif
175
176 SDL_Window *window = [view getSDLWindow];
177 const CGPoint point = [pencil locationInView:view];
178
179 // zOffset is zero here; if you're here, you're touching.
180 UIKit_HandlePenAxes(window, [pencil timestamp], 0.0f, &point, [pencil force], [pencil maximumPossibleForce], [pencil azimuthAngleInView:view], [pencil altitudeAngle], rollAngle);
181}
182
183void UIKit_HandlePenMotion(SDL_uikitview *view, UITouch *pencil)
184{
185 UIKit_HandlePenAxesFromUITouch(view, pencil);
186}
187
188void UIKit_HandlePenPress(SDL_uikitview *view, UITouch *pencil)
189{
190 const SDL_PenID penId = UIKit_AddPenIfNecesary();
191 if (penId) {
192 UIKit_HandlePenAxesFromUITouch(view, pencil);
193 SDL_SendPenTouch(UIKit_GetEventTimestamp([pencil timestamp]), penId, [view getSDLWindow], false, true);
194 }
195}
196
197void UIKit_HandlePenRelease(SDL_uikitview *view, UITouch *pencil)
198{
199 const SDL_PenID penId = UIKit_AddPenIfNecesary();
200 if (penId) {
201 SDL_SendPenTouch(UIKit_GetEventTimestamp([pencil timestamp]), penId, [view getSDLWindow], false, false);
202 UIKit_HandlePenAxesFromUITouch(view, pencil);
203 }
204}
205
206void UIKit_QuitPen(SDL_VideoDevice *_this)
207{
208 if (apple_pencil_id) {
209 SDL_RemovePenDevice(0, apple_pencil_id);
210 apple_pencil_id = 0;
211 }
212}
213
214#endif // SDL_VIDEO_DRIVER_UIKIT