Atlas - SDL_uikitpen.m
Home / ext / SDL / src / video / uikit Lines: 1 | Size: 8473 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 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 SDL_SendPenMotion(timestamp, penId, window, point->x, point->y); 128 SDL_SendPenAxis(timestamp, penId, window, SDL_PEN_AXIS_PRESSURE, pressure); 129 SDL_SendPenAxis(timestamp, penId, window, SDL_PEN_AXIS_XTILT, xtilt); 130 SDL_SendPenAxis(timestamp, penId, window, SDL_PEN_AXIS_YTILT, ytilt); 131 SDL_SendPenAxis(timestamp, penId, window, SDL_PEN_AXIS_ROTATION, rotation); 132 SDL_SendPenAxis(timestamp, penId, window, SDL_PEN_AXIS_DISTANCE, zOffset); 133 } 134} 135 136#if !defined(SDL_PLATFORM_TVOS) 137extern void UIKit_HandlePenHover(SDL_uikitview *view, UIHoverGestureRecognizer *recognizer) 138{ 139 float zOffset = 0.0f; 140 if (@available(iOS 16.1, *)) { 141 zOffset = (float) [recognizer zOffset]; 142 } 143 144 float azimuthAngleInView = 0.0f; 145 if (@available(iOS 16.4, *)) { 146 azimuthAngleInView = (float) [recognizer azimuthAngleInView:view]; 147 } 148 149 float altitudeAngle = 0.0f; 150 if (@available(iOS 16.4, *)) { 151 altitudeAngle = (float) [recognizer altitudeAngle]; 152 } 153 154 float rollAngle = 0.0f; 155 if (@available(iOS 17.5, *)) { 156 rollAngle = (float) [recognizer rollAngle]; 157 } 158 159 SDL_Window *window = [view getSDLWindow]; 160 const CGPoint point = [recognizer locationInView:view]; 161 162 // force is zero here; if you're here, you're not touching. 163 // !!! FIXME: no timestamp on these...? 164 UIKit_HandlePenAxes(window, 0, zOffset, &point, 0.0f, 1.0f, azimuthAngleInView, altitudeAngle, rollAngle); 165} 166#endif 167 168static void UIKit_HandlePenAxesFromUITouch(SDL_uikitview *view, UITouch *pencil) 169{ 170 float rollAngle = 0.0f; 171#if !defined(SDL_PLATFORM_TVOS) 172 if (@available(iOS 17.5, *)) { 173 rollAngle = (float) [pencil rollAngle]; 174 } 175#endif 176 177 SDL_Window *window = [view getSDLWindow]; 178 const CGPoint point = [pencil locationInView:view]; 179 180 // zOffset is zero here; if you're here, you're touching. 181 UIKit_HandlePenAxes(window, [pencil timestamp], 0.0f, &point, [pencil force], [pencil maximumPossibleForce], [pencil azimuthAngleInView:view], [pencil altitudeAngle], rollAngle); 182} 183 184void UIKit_HandlePenMotion(SDL_uikitview *view, UITouch *pencil) 185{ 186 UIKit_HandlePenAxesFromUITouch(view, pencil); 187} 188 189void UIKit_HandlePenPress(SDL_uikitview *view, UITouch *pencil) 190{ 191 SDL_Window *window = [view getSDLWindow]; 192 const SDL_PenID penId = UIKit_AddPenIfNecesary(window); 193 if (penId) { 194 UIKit_HandlePenAxesFromUITouch(view, pencil); 195 SDL_SendPenTouch(UIKit_GetEventTimestamp([pencil timestamp]), penId, window, false, true); 196 } 197} 198 199void UIKit_HandlePenRelease(SDL_uikitview *view, UITouch *pencil) 200{ 201 SDL_Window *window = [view getSDLWindow]; 202 const SDL_PenID penId = UIKit_AddPenIfNecesary(window); 203 if (penId) { 204 SDL_SendPenTouch(UIKit_GetEventTimestamp([pencil timestamp]), penId, window, false, false); 205 UIKit_HandlePenAxesFromUITouch(view, pencil); 206 } 207} 208 209void UIKit_QuitPen(SDL_VideoDevice *_this) 210{ 211 if (apple_pencil_id) { 212 SDL_RemovePenDevice(0, NULL, apple_pencil_id); 213 apple_pencil_id = 0; 214 } 215} 216 217#endif // SDL_VIDEO_DRIVER_UIKIT 218[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.