summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/video/uikit/SDL_uikitviewcontroller.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_uikitviewcontroller.m
Initial commit
Diffstat (limited to 'contrib/SDL-3.2.8/src/video/uikit/SDL_uikitviewcontroller.m')
-rw-r--r--contrib/SDL-3.2.8/src/video/uikit/SDL_uikitviewcontroller.m736
1 files changed, 736 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/video/uikit/SDL_uikitviewcontroller.m b/contrib/SDL-3.2.8/src/video/uikit/SDL_uikitviewcontroller.m
new file mode 100644
index 0000000..45f2d64
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/uikit/SDL_uikitviewcontroller.m
@@ -0,0 +1,736 @@
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_sysvideo.h"
26#include "../../events/SDL_events_c.h"
27
28#include "SDL_uikitviewcontroller.h"
29#include "SDL_uikitmessagebox.h"
30#include "SDL_uikitevents.h"
31#include "SDL_uikitvideo.h"
32#include "SDL_uikitmodes.h"
33#include "SDL_uikitwindow.h"
34#include "SDL_uikitopengles.h"
35
36#ifdef SDL_PLATFORM_TVOS
37static void SDLCALL SDL_AppleTVControllerUIHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
38{
39 @autoreleasepool {
40 SDL_uikitviewcontroller *viewcontroller = (__bridge SDL_uikitviewcontroller *)userdata;
41 viewcontroller.controllerUserInteractionEnabled = hint && (*hint != '0');
42 }
43}
44#endif
45
46#ifndef SDL_PLATFORM_TVOS
47static void SDLCALL SDL_HideHomeIndicatorHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
48{
49 @autoreleasepool {
50 SDL_uikitviewcontroller *viewcontroller = (__bridge SDL_uikitviewcontroller *)userdata;
51 viewcontroller.homeIndicatorHidden = (hint && *hint) ? SDL_atoi(hint) : -1;
52 [viewcontroller setNeedsUpdateOfHomeIndicatorAutoHidden];
53 [viewcontroller setNeedsUpdateOfScreenEdgesDeferringSystemGestures];
54 }
55}
56#endif
57
58@implementation SDLUITextField : UITextField
59- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
60{
61 if (action == @selector(paste:)) {
62 return NO;
63 }
64
65 return [super canPerformAction:action withSender:sender];
66}
67@end
68
69@implementation SDL_uikitviewcontroller
70{
71 CADisplayLink *displayLink;
72 int animationInterval;
73 void (*animationCallback)(void *);
74 void *animationCallbackParam;
75
76#ifdef SDL_IPHONE_KEYBOARD
77 SDLUITextField *textField;
78 BOOL hidingKeyboard;
79 BOOL rotatingOrientation;
80 NSString *committedText;
81 NSString *obligateForBackspace;
82#endif
83}
84
85@synthesize window;
86
87- (instancetype)initWithSDLWindow:(SDL_Window *)_window
88{
89 if (self = [super initWithNibName:nil bundle:nil]) {
90 self.window = _window;
91
92#ifdef SDL_IPHONE_KEYBOARD
93 [self initKeyboard];
94 hidingKeyboard = NO;
95 rotatingOrientation = NO;
96#endif
97
98#ifdef SDL_PLATFORM_TVOS
99 SDL_AddHintCallback(SDL_HINT_APPLE_TV_CONTROLLER_UI_EVENTS,
100 SDL_AppleTVControllerUIHintChanged,
101 (__bridge void *)self);
102#endif
103
104#ifndef SDL_PLATFORM_TVOS
105 SDL_AddHintCallback(SDL_HINT_IOS_HIDE_HOME_INDICATOR,
106 SDL_HideHomeIndicatorHintChanged,
107 (__bridge void *)self);
108#endif
109
110 // Enable high refresh rates on iOS
111 // To enable this on phones, you should add the following line to Info.plist:
112 // <key>CADisableMinimumFrameDurationOnPhone</key> <true/>
113 if (@available(iOS 15.0, tvOS 15.0, *)) {
114 const SDL_DisplayMode *mode = SDL_GetDesktopDisplayMode(SDL_GetPrimaryDisplay());
115 if (mode && mode->refresh_rate > 60.0f) {
116 int frame_rate = (int)mode->refresh_rate;
117 displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(doLoop:)];
118 displayLink.preferredFrameRateRange = CAFrameRateRangeMake((frame_rate * 2) / 3, frame_rate, frame_rate);
119 [displayLink addToRunLoop:NSRunLoop.currentRunLoop forMode:NSDefaultRunLoopMode];
120 }
121 }
122 }
123 return self;
124}
125
126- (void)dealloc
127{
128#ifdef SDL_IPHONE_KEYBOARD
129 [self deinitKeyboard];
130#endif
131
132#ifdef SDL_PLATFORM_TVOS
133 SDL_RemoveHintCallback(SDL_HINT_APPLE_TV_CONTROLLER_UI_EVENTS,
134 SDL_AppleTVControllerUIHintChanged,
135 (__bridge void *)self);
136#endif
137
138#ifndef SDL_PLATFORM_TVOS
139 SDL_RemoveHintCallback(SDL_HINT_IOS_HIDE_HOME_INDICATOR,
140 SDL_HideHomeIndicatorHintChanged,
141 (__bridge void *)self);
142#endif
143}
144
145- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
146{
147 SDL_SetSystemTheme(UIKit_GetSystemTheme());
148}
149
150- (void)setAnimationCallback:(int)interval
151 callback:(void (*)(void *))callback
152 callbackParam:(void *)callbackParam
153{
154 [self stopAnimation];
155
156 if (interval <= 0) {
157 interval = 1;
158 }
159 animationInterval = interval;
160 animationCallback = callback;
161 animationCallbackParam = callbackParam;
162
163 if (animationCallback) {
164 [self startAnimation];
165 }
166}
167
168- (void)startAnimation
169{
170 displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(doLoop:)];
171
172#ifdef SDL_PLATFORM_VISIONOS
173 displayLink.preferredFramesPerSecond = 90 / animationInterval; //TODO: Get frame max frame rate on visionOS
174#else
175 SDL_UIKitWindowData *data = (__bridge SDL_UIKitWindowData *)window->internal;
176
177 displayLink.preferredFramesPerSecond = data.uiwindow.screen.maximumFramesPerSecond / animationInterval;
178#endif
179
180 [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
181}
182
183- (void)stopAnimation
184{
185 [displayLink invalidate];
186 displayLink = nil;
187}
188
189- (void)doLoop:(CADisplayLink *)sender
190{
191 // Don't run the game loop while a messagebox is up
192 if (animationCallback && !UIKit_ShowingMessageBox()) {
193 // See the comment in the function definition.
194#if defined(SDL_VIDEO_OPENGL_ES) || defined(SDL_VIDEO_OPENGL_ES2)
195 UIKit_GL_RestoreCurrentContext();
196#endif
197
198 animationCallback(animationCallbackParam);
199 }
200}
201
202- (void)loadView
203{
204 // Do nothing.
205}
206
207- (void)viewDidLayoutSubviews
208{
209 const CGSize size = self.view.bounds.size;
210 int w = (int)size.width;
211 int h = (int)size.height;
212
213 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, w, h);
214}
215
216#ifndef SDL_PLATFORM_TVOS
217- (NSUInteger)supportedInterfaceOrientations
218{
219 return UIKit_GetSupportedOrientations(window);
220}
221
222- (BOOL)prefersStatusBarHidden
223{
224 BOOL hidden = (window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_BORDERLESS)) != 0;
225 return hidden;
226}
227
228- (BOOL)prefersHomeIndicatorAutoHidden
229{
230 BOOL hidden = NO;
231 if (self.homeIndicatorHidden == 1) {
232 hidden = YES;
233 }
234 return hidden;
235}
236
237- (UIRectEdge)preferredScreenEdgesDeferringSystemGestures
238{
239 if (self.homeIndicatorHidden >= 0) {
240 if (self.homeIndicatorHidden == 2) {
241 return UIRectEdgeAll;
242 } else {
243 return UIRectEdgeNone;
244 }
245 }
246
247 // By default, fullscreen and borderless windows get all screen gestures
248 if ((window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_BORDERLESS)) != 0) {
249 return UIRectEdgeAll;
250 } else {
251 return UIRectEdgeNone;
252 }
253}
254
255- (BOOL)prefersPointerLocked
256{
257 return SDL_GCMouseRelativeMode() ? YES : NO;
258}
259
260#endif // !SDL_PLATFORM_TVOS
261
262/*
263 ---- Keyboard related functionality below this line ----
264 */
265#ifdef SDL_IPHONE_KEYBOARD
266
267@synthesize textInputRect;
268@synthesize keyboardHeight;
269@synthesize textFieldFocused;
270
271// Set ourselves up as a UITextFieldDelegate
272- (void)initKeyboard
273{
274 obligateForBackspace = @" "; // 64 space
275 textField = [[SDLUITextField alloc] initWithFrame:CGRectZero];
276 textField.delegate = self;
277 // placeholder so there is something to delete!
278 textField.text = obligateForBackspace;
279 committedText = textField.text;
280
281 textField.hidden = YES;
282 textFieldFocused = NO;
283
284 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
285#ifndef SDL_PLATFORM_TVOS
286 [center addObserver:self
287 selector:@selector(keyboardWillShow:)
288 name:UIKeyboardWillShowNotification
289 object:nil];
290 [center addObserver:self
291 selector:@selector(keyboardWillHide:)
292 name:UIKeyboardWillHideNotification
293 object:nil];
294 [center addObserver:self
295 selector:@selector(keyboardDidHide:)
296 name:UIKeyboardDidHideNotification
297 object:nil];
298#endif
299 [center addObserver:self
300 selector:@selector(textFieldTextDidChange:)
301 name:UITextFieldTextDidChangeNotification
302 object:nil];
303}
304
305- (NSArray *)keyCommands
306{
307 NSMutableArray *commands = [[NSMutableArray alloc] init];
308 [commands addObject:[UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow modifierFlags:kNilOptions action:@selector(handleCommand:)]];
309 [commands addObject:[UIKeyCommand keyCommandWithInput:UIKeyInputDownArrow modifierFlags:kNilOptions action:@selector(handleCommand:)]];
310 [commands addObject:[UIKeyCommand keyCommandWithInput:UIKeyInputLeftArrow modifierFlags:kNilOptions action:@selector(handleCommand:)]];
311 [commands addObject:[UIKeyCommand keyCommandWithInput:UIKeyInputRightArrow modifierFlags:kNilOptions action:@selector(handleCommand:)]];
312 [commands addObject:[UIKeyCommand keyCommandWithInput:UIKeyInputEscape modifierFlags:kNilOptions action:@selector(handleCommand:)]];
313 return [NSArray arrayWithArray:commands];
314}
315
316- (void)handleCommand:(UIKeyCommand *)keyCommand
317{
318 SDL_Scancode scancode = SDL_SCANCODE_UNKNOWN;
319 NSString *input = keyCommand.input;
320
321 if (input == UIKeyInputUpArrow) {
322 scancode = SDL_SCANCODE_UP;
323 } else if (input == UIKeyInputDownArrow) {
324 scancode = SDL_SCANCODE_DOWN;
325 } else if (input == UIKeyInputLeftArrow) {
326 scancode = SDL_SCANCODE_LEFT;
327 } else if (input == UIKeyInputRightArrow) {
328 scancode = SDL_SCANCODE_RIGHT;
329 } else if (input == UIKeyInputEscape) {
330 scancode = SDL_SCANCODE_ESCAPE;
331 }
332
333 if (scancode != SDL_SCANCODE_UNKNOWN) {
334 SDL_SendKeyboardKeyAutoRelease(0, scancode);
335 }
336}
337
338- (void)setView:(UIView *)view
339{
340 [super setView:view];
341
342 [view addSubview:textField];
343
344 if (textFieldFocused) {
345 /* startTextInput has been called before the text field was added to the view,
346 * call it again for the text field to actually become first responder. */
347 [self startTextInput];
348 }
349}
350
351- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
352{
353 [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
354 rotatingOrientation = YES;
355 [coordinator
356 animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
357 }
358 completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
359 self->rotatingOrientation = NO;
360 }];
361}
362
363- (void)deinitKeyboard
364{
365 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
366#ifndef SDL_PLATFORM_TVOS
367 [center removeObserver:self
368 name:UIKeyboardWillShowNotification
369 object:nil];
370 [center removeObserver:self
371 name:UIKeyboardWillHideNotification
372 object:nil];
373 [center removeObserver:self
374 name:UIKeyboardDidHideNotification
375 object:nil];
376#endif
377 [center removeObserver:self
378 name:UITextFieldTextDidChangeNotification
379 object:nil];
380}
381
382- (void)setTextFieldProperties:(SDL_PropertiesID) props
383{
384 textField.secureTextEntry = NO;
385
386 switch (SDL_GetTextInputType(props)) {
387 default:
388 case SDL_TEXTINPUT_TYPE_TEXT:
389 textField.keyboardType = UIKeyboardTypeDefault;
390 textField.textContentType = nil;
391 break;
392 case SDL_TEXTINPUT_TYPE_TEXT_NAME:
393 textField.keyboardType = UIKeyboardTypeDefault;
394 textField.textContentType = UITextContentTypeName;
395 break;
396 case SDL_TEXTINPUT_TYPE_TEXT_EMAIL:
397 textField.keyboardType = UIKeyboardTypeEmailAddress;
398 textField.textContentType = UITextContentTypeEmailAddress;
399 break;
400 case SDL_TEXTINPUT_TYPE_TEXT_USERNAME:
401 textField.keyboardType = UIKeyboardTypeDefault;
402 textField.textContentType = UITextContentTypeUsername;
403 break;
404 case SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_HIDDEN:
405 textField.keyboardType = UIKeyboardTypeDefault;
406 textField.textContentType = UITextContentTypePassword;
407 textField.secureTextEntry = YES;
408 break;
409 case SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_VISIBLE:
410 textField.keyboardType = UIKeyboardTypeDefault;
411 textField.textContentType = UITextContentTypePassword;
412 break;
413 case SDL_TEXTINPUT_TYPE_NUMBER:
414 textField.keyboardType = UIKeyboardTypeDecimalPad;
415 textField.textContentType = nil;
416 break;
417 case SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_HIDDEN:
418 textField.keyboardType = UIKeyboardTypeNumberPad;
419 if (@available(iOS 12.0, tvOS 12.0, *)) {
420 textField.textContentType = UITextContentTypeOneTimeCode;
421 } else {
422 textField.textContentType = nil;
423 }
424 textField.secureTextEntry = YES;
425 break;
426 case SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_VISIBLE:
427 textField.keyboardType = UIKeyboardTypeNumberPad;
428 if (@available(iOS 12.0, tvOS 12.0, *)) {
429 textField.textContentType = UITextContentTypeOneTimeCode;
430 } else {
431 textField.textContentType = nil;
432 }
433 break;
434 }
435
436 switch (SDL_GetTextInputCapitalization(props)) {
437 default:
438 case SDL_CAPITALIZE_NONE:
439 textField.autocapitalizationType = UITextAutocapitalizationTypeNone;
440 break;
441 case SDL_CAPITALIZE_LETTERS:
442 textField.autocapitalizationType = UITextAutocapitalizationTypeAllCharacters;
443 break;
444 case SDL_CAPITALIZE_WORDS:
445 textField.autocapitalizationType = UITextAutocapitalizationTypeWords;
446 break;
447 case SDL_CAPITALIZE_SENTENCES:
448 textField.autocapitalizationType = UITextAutocapitalizationTypeSentences;
449 break;
450 }
451
452 if (SDL_GetTextInputAutocorrect(props)) {
453 textField.autocorrectionType = UITextAutocorrectionTypeYes;
454 textField.spellCheckingType = UITextSpellCheckingTypeYes;
455 } else {
456 textField.autocorrectionType = UITextAutocorrectionTypeNo;
457 textField.spellCheckingType = UITextSpellCheckingTypeNo;
458 }
459
460 if (SDL_GetTextInputMultiline(props)) {
461 textField.enablesReturnKeyAutomatically = YES;
462 } else {
463 textField.enablesReturnKeyAutomatically = NO;
464 }
465
466 if (!textField.window) {
467 /* textField has not been added to the view yet,
468 we don't have to do anything. */
469 return;
470 }
471
472 // the text field needs to be re-added to the view in order to update correctly.
473 UIView *superview = textField.superview;
474 [textField removeFromSuperview];
475 [superview addSubview:textField];
476
477 if (SDL_TextInputActive(window)) {
478 [textField becomeFirstResponder];
479 }
480}
481
482/* requests the SDL text field to become focused and accept text input.
483 * also shows the onscreen virtual keyboard if no hardware keyboard is attached. */
484- (bool)startTextInput
485{
486 textFieldFocused = YES;
487 if (!textField.window) {
488 /* textField has not been added to the view yet,
489 * we will try again when that happens. */
490 return true;
491 }
492
493 return [textField becomeFirstResponder];
494}
495
496/* requests the SDL text field to lose focus and stop accepting text input.
497 * also hides the onscreen virtual keyboard if no hardware keyboard is attached. */
498- (bool)stopTextInput
499{
500 textFieldFocused = NO;
501 if (!textField.window) {
502 /* textField has not been added to the view yet,
503 * we will try again when that happens. */
504 return true;
505 }
506
507 [self resetTextState];
508 return [textField resignFirstResponder];
509}
510
511- (void)keyboardWillShow:(NSNotification *)notification
512{
513#ifndef SDL_PLATFORM_TVOS
514 CGRect kbrect = [[notification userInfo][UIKeyboardFrameEndUserInfoKey] CGRectValue];
515
516 /* The keyboard rect is in the coordinate space of the screen/window, but we
517 * want its height in the coordinate space of the view. */
518 kbrect = [self.view convertRect:kbrect fromView:nil];
519
520 [self setKeyboardHeight:(int)kbrect.size.height];
521#endif
522
523 /* A keyboard hide transition has been interrupted with a show (keyboardWillHide has been called but keyboardDidHide didn't).
524 * since text input was stopped by the hide, we have to start it again. */
525 if (hidingKeyboard) {
526 SDL_StartTextInput(window);
527 hidingKeyboard = NO;
528 }
529}
530
531- (void)keyboardWillHide:(NSNotification *)notification
532{
533 hidingKeyboard = YES;
534 [self setKeyboardHeight:0];
535
536 /* When the user dismisses the software keyboard by the "hide" button in the bottom right corner,
537 * we want to reflect that on SDL_TextInputActive by calling SDL_StopTextInput...on certain conditions */
538 if (SDL_TextInputActive(window)
539 /* keyboardWillHide gets called when a hardware keyboard is attached,
540 * keep text input state active if hiding while there is a hardware keyboard.
541 * if the hardware keyboard gets detached, the software keyboard will appear anyway. */
542 && !SDL_HasKeyboard()
543 /* When the device changes orientation, a sequence of hide and show transitions are triggered.
544 * keep text input state active in this case. */
545 && !rotatingOrientation) {
546 SDL_StopTextInput(window);
547 }
548}
549
550- (void)keyboardDidHide:(NSNotification *)notification
551{
552 hidingKeyboard = NO;
553}
554
555- (void)textFieldTextDidChange:(NSNotification *)notification
556{
557 if (textField.markedTextRange == nil) {
558 NSUInteger compareLength = SDL_min(textField.text.length, committedText.length);
559 NSUInteger matchLength;
560
561 // Backspace over characters that are no longer in the string
562 for (matchLength = 0; matchLength < compareLength; ++matchLength) {
563 if ([committedText characterAtIndex:matchLength] != [textField.text characterAtIndex:matchLength]) {
564 break;
565 }
566 }
567 if (matchLength < committedText.length) {
568 size_t deleteLength = SDL_utf8strlen([[committedText substringFromIndex:matchLength] UTF8String]);
569 while (deleteLength > 0) {
570 // Send distinct down and up events for each backspace action
571 SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, 0, SDL_SCANCODE_BACKSPACE, true);
572 SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, 0, SDL_SCANCODE_BACKSPACE, false);
573 --deleteLength;
574 }
575 }
576
577 if (matchLength < textField.text.length) {
578 NSString *pendingText = [textField.text substringFromIndex:matchLength];
579 if (!SDL_HardwareKeyboardKeyPressed()) {
580 /* Go through all the characters in the string we've been sent and
581 * convert them to key presses */
582 NSUInteger i;
583 for (i = 0; i < pendingText.length; i++) {
584 SDL_SendKeyboardUnicodeKey(0, [pendingText characterAtIndex:i]);
585 }
586 }
587 SDL_SendKeyboardText([pendingText UTF8String]);
588 }
589 committedText = textField.text;
590 }
591}
592
593- (void)updateKeyboard
594{
595 SDL_UIKitWindowData *data = (__bridge SDL_UIKitWindowData *) window->internal;
596
597 CGAffineTransform t = self.view.transform;
598 CGPoint offset = CGPointMake(0.0, 0.0);
599#ifdef SDL_PLATFORM_VISIONOS
600 CGRect frame = UIKit_ComputeViewFrame(window);
601#else
602 CGRect frame = UIKit_ComputeViewFrame(window, data.uiwindow.screen);
603#endif
604
605 if (self.keyboardHeight && self.textInputRect.h) {
606 int rectbottom = (int)(self.textInputRect.y + self.textInputRect.h);
607 int keybottom = (int)(self.view.bounds.size.height - self.keyboardHeight);
608 if (keybottom < rectbottom) {
609 offset.y = keybottom - rectbottom;
610 }
611 }
612
613 /* Apply this view's transform (except any translation) to the offset, in
614 * order to orient it correctly relative to the frame's coordinate space. */
615 t.tx = 0.0;
616 t.ty = 0.0;
617 offset = CGPointApplyAffineTransform(offset, t);
618
619 // Apply the updated offset to the view's frame.
620 frame.origin.x += offset.x;
621 frame.origin.y += offset.y;
622
623 self.view.frame = frame;
624}
625
626- (void)setKeyboardHeight:(int)height
627{
628 keyboardHeight = height;
629 [self updateKeyboard];
630}
631
632// UITextFieldDelegate method. Invoked when user types something.
633- (BOOL)textField:(UITextField *)_textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
634{
635 if (textField.markedTextRange == nil) {
636 if (textField.text.length < 16) {
637 [self resetTextState];
638 }
639 }
640 return YES;
641}
642
643// Terminates the editing session
644- (BOOL)textFieldShouldReturn:(UITextField *)_textField
645{
646 SDL_SendKeyboardKeyAutoRelease(0, SDL_SCANCODE_RETURN);
647 if (textFieldFocused &&
648 SDL_GetHintBoolean(SDL_HINT_RETURN_KEY_HIDES_IME, false)) {
649 SDL_StopTextInput(window);
650 }
651 return YES;
652}
653
654- (void)resetTextState
655{
656 textField.text = obligateForBackspace;
657 committedText = textField.text;
658}
659
660#endif
661
662@end
663
664// iPhone keyboard addition functions
665#ifdef SDL_IPHONE_KEYBOARD
666
667static SDL_uikitviewcontroller *GetWindowViewController(SDL_Window *window)
668{
669 if (!window || !window->internal) {
670 SDL_SetError("Invalid window");
671 return nil;
672 }
673
674 SDL_UIKitWindowData *data = (__bridge SDL_UIKitWindowData *)window->internal;
675
676 return data.viewcontroller;
677}
678
679bool UIKit_HasScreenKeyboardSupport(SDL_VideoDevice *_this)
680{
681 return true;
682}
683
684bool UIKit_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props)
685{
686 @autoreleasepool {
687 SDL_uikitviewcontroller *vc = GetWindowViewController(window);
688 return [vc startTextInput];
689 }
690}
691
692bool UIKit_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window)
693{
694 @autoreleasepool {
695 SDL_uikitviewcontroller *vc = GetWindowViewController(window);
696 return [vc stopTextInput];
697 }
698}
699
700void UIKit_SetTextInputProperties(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props)
701{
702 @autoreleasepool {
703 SDL_uikitviewcontroller *vc = GetWindowViewController(window);
704 [vc setTextFieldProperties:props];
705 }
706}
707
708bool UIKit_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window)
709{
710 @autoreleasepool {
711 SDL_uikitviewcontroller *vc = GetWindowViewController(window);
712 if (vc != nil) {
713 return vc.textFieldFocused;
714 }
715 return false;
716 }
717}
718
719bool UIKit_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window)
720{
721 @autoreleasepool {
722 SDL_uikitviewcontroller *vc = GetWindowViewController(window);
723 if (vc != nil) {
724 vc.textInputRect = window->text_input_rect;
725
726 if (vc.textFieldFocused) {
727 [vc updateKeyboard];
728 }
729 }
730 }
731 return true;
732}
733
734#endif // SDL_IPHONE_KEYBOARD
735
736#endif // SDL_VIDEO_DRIVER_UIKIT