Atlas - SDL_uikitpen.m

Home / ext / SDL / src / video / uikit Lines: 1 | Size: 8680 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2026 Sam Lantinga <[email protected]> 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(SDL_Window *window) 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 info.device_type = SDL_PEN_DEVICE_TYPE_DIRECT; // Apple Pencil on iOS is always a direct device; it works on the tablet's screen. 76 77 if (@available(iOS 17.5, *)) { // need rollAngle method. 78 info.capabilities |= SDL_PEN_CAPABILITY_ROTATION; 79 } 80 81 if (@available(ios 16.1, *)) { // need zOffset method. 82 info.capabilities |= SDL_PEN_CAPABILITY_DISTANCE; 83 } 84 85 // Apple Pencil and iOS can report when the pencil is being "squeezed" but it's a boolean thing, 86 // so we can't use it for tangential pressure. 87 88 // There's only ever one Apple Pencil at most, so we just pass a non-zero value for the handle. 89 apple_pencil_id = SDL_AddPenDevice(0, "Apple Pencil", window, &info, (void *) (size_t) 0x1, true); 90 } 91 92 return apple_pencil_id; 93} 94 95static void UIKit_HandlePenAxes(SDL_Window *window, NSTimeInterval nstimestamp, float zOffset, const CGPoint *point, float force, 96 float maximumPossibleForce, float azimuthAngleInView, float altitudeAngle, float rollAngle) 97{ 98 const SDL_PenID penId = UIKit_AddPenIfNecesary(window); 99 if (penId) { 100 const Uint64 timestamp = UIKit_GetEventTimestamp(nstimestamp); 101 const float radians_to_degrees = 180.0f / SDL_PI_F; 102 103 // Normalize force to 0.0f ... 1.0f range. 104 const float pressure = force / maximumPossibleForce; 105 106 // azimuthAngleInView is in radians, with 0 being the pen's back end pointing due east on the screen when the 107 // tip is touching the screen, and negative when heading north from there, positive to the south. 108 // So convert to degrees, 0 being due east, etc. 109 const float azimuth_angle = azimuthAngleInView * radians_to_degrees; 110 111 // altitudeAngle is in radians, with 0 being the pen laying flat on (parallel to) the device 112 // screen and PI/2 being it pointing straight up from (perpendicular to) the device screen. 113 // So convert to degrees, 0 being flat and 90 being straight up. 114 const float altitude_angle = altitudeAngle * radians_to_degrees; 115 116 // the azimuth_angle goes from -180 to 180 (with abs(angle) moving from 180 to 0, left to right), but SDL wants 117 // it from -90 (back facing west) to 90 (back facing east). 118 const float xtilt = (180.0f - SDL_fabsf(azimuth_angle)) - 90.0f; 119 120 // the altitude_angle goes from 0 to 90 regardless of which direction the pen is lifting off the device, but SDL wants 121 // it from -90 (flat facing north) to 90 (flat facing south). 122 const float ytilt = (azimuth_angle < 0.0f) ? -(90.0f - altitude_angle) : (90.0f - altitude_angle); 123 124 // rotation is in radians, and only available on a later iOS. 125 const float rotation = rollAngle * radians_to_degrees; // !!! FIXME: this might need adjustment, I don't have a pencil that supports it. 126 127 if (force == 0.0f && (SDL_GetPenStatus(penId, NULL, 0) & SDL_PEN_INPUT_DOWN)) { 128 // The first hover as the pen is being released has a stale position, so ignore it 129 return; 130 } 131 132 SDL_SendPenMotion(timestamp, penId, window, point->x, point->y); 133 SDL_SendPenAxis(timestamp, penId, window, SDL_PEN_AXIS_PRESSURE, pressure); 134 SDL_SendPenAxis(timestamp, penId, window, SDL_PEN_AXIS_XTILT, xtilt); 135 SDL_SendPenAxis(timestamp, penId, window, SDL_PEN_AXIS_YTILT, ytilt); 136 SDL_SendPenAxis(timestamp, penId, window, SDL_PEN_AXIS_ROTATION, rotation); 137 SDL_SendPenAxis(timestamp, penId, window, SDL_PEN_AXIS_DISTANCE, zOffset); 138 } 139} 140 141#if !defined(SDL_PLATFORM_TVOS) 142void UIKit_HandlePenHover(SDL_uikitview *view, UIHoverGestureRecognizer *recognizer) 143{ 144 float zOffset = 0.0f; 145 if (@available(iOS 16.1, *)) { 146 zOffset = (float) [recognizer zOffset]; 147 } 148 149 float azimuthAngleInView = 0.0f; 150 if (@available(iOS 16.4, *)) { 151 azimuthAngleInView = (float) [recognizer azimuthAngleInView:view]; 152 } 153 154 float altitudeAngle = 0.0f; 155 if (@available(iOS 16.4, *)) { 156 altitudeAngle = (float) [recognizer altitudeAngle]; 157 } 158 159 float rollAngle = 0.0f; 160 if (@available(iOS 17.5, *)) { 161 rollAngle = (float) [recognizer rollAngle]; 162 } 163 164 SDL_Window *window = [view getSDLWindow]; 165 const CGPoint point = [recognizer locationInView:view]; 166 167 // force is zero here; if you're here, you're not touching. 168 // !!! FIXME: no timestamp on these...? 169 UIKit_HandlePenAxes(window, 0, zOffset, &point, 0.0f, 1.0f, azimuthAngleInView, altitudeAngle, rollAngle); 170} 171#endif 172 173static void UIKit_HandlePenAxesFromUITouch(SDL_uikitview *view, UITouch *pencil) 174{ 175 float rollAngle = 0.0f; 176#if !defined(SDL_PLATFORM_TVOS) 177 if (@available(iOS 17.5, *)) { 178 rollAngle = (float) [pencil rollAngle]; 179 } 180#endif 181 182 SDL_Window *window = [view getSDLWindow]; 183 const CGPoint point = [pencil locationInView:view]; 184 185 // zOffset is zero here; if you're here, you're touching. 186 UIKit_HandlePenAxes(window, [pencil timestamp], 0.0f, &point, [pencil force], [pencil maximumPossibleForce], [pencil azimuthAngleInView:view], [pencil altitudeAngle], rollAngle); 187} 188 189void UIKit_HandlePenMotion(SDL_uikitview *view, UITouch *pencil) 190{ 191 UIKit_HandlePenAxesFromUITouch(view, pencil); 192} 193 194void UIKit_HandlePenPress(SDL_uikitview *view, UITouch *pencil) 195{ 196 SDL_Window *window = [view getSDLWindow]; 197 const SDL_PenID penId = UIKit_AddPenIfNecesary(window); 198 if (penId) { 199 UIKit_HandlePenAxesFromUITouch(view, pencil); 200 SDL_SendPenTouch(UIKit_GetEventTimestamp([pencil timestamp]), penId, window, false, true); 201 } 202} 203 204void UIKit_HandlePenRelease(SDL_uikitview *view, UITouch *pencil) 205{ 206 SDL_Window *window = [view getSDLWindow]; 207 const SDL_PenID penId = UIKit_AddPenIfNecesary(window); 208 if (penId) { 209 SDL_SendPenTouch(UIKit_GetEventTimestamp([pencil timestamp]), penId, window, false, false); 210 UIKit_HandlePenAxesFromUITouch(view, pencil); 211 } 212} 213 214void UIKit_QuitPen(SDL_VideoDevice *_this) 215{ 216 if (apple_pencil_id) { 217 SDL_RemovePenDevice(0, NULL, apple_pencil_id); 218 apple_pencil_id = 0; 219 } 220} 221 222#endif // SDL_VIDEO_DRIVER_UIKIT 223
[FILE END]
(C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.