diff options
author | chai <chaifix@163.com> | 2019-05-11 22:54:56 +0800 |
---|---|---|
committer | chai <chaifix@163.com> | 2019-05-11 22:54:56 +0800 |
commit | 9645be0af1b1d5cb0ad5892d5464e1b23c51b550 (patch) | |
tree | 129c716bed8e93312421c3adb2f8e7c4f811602d /source/3rd-party/SDL2/src/video/uikit/SDL_uikitviewcontroller.m |
Diffstat (limited to 'source/3rd-party/SDL2/src/video/uikit/SDL_uikitviewcontroller.m')
-rw-r--r-- | source/3rd-party/SDL2/src/video/uikit/SDL_uikitviewcontroller.m | 553 |
1 files changed, 553 insertions, 0 deletions
diff --git a/source/3rd-party/SDL2/src/video/uikit/SDL_uikitviewcontroller.m b/source/3rd-party/SDL2/src/video/uikit/SDL_uikitviewcontroller.m new file mode 100644 index 0000000..49a39b6 --- /dev/null +++ b/source/3rd-party/SDL2/src/video/uikit/SDL_uikitviewcontroller.m @@ -0,0 +1,553 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 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 that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#if SDL_VIDEO_DRIVER_UIKIT + +#include "SDL_video.h" +#include "SDL_assert.h" +#include "SDL_hints.h" +#include "../SDL_sysvideo.h" +#include "../../events/SDL_events_c.h" + +#import "SDL_uikitviewcontroller.h" +#import "SDL_uikitmessagebox.h" +#include "SDL_uikitvideo.h" +#include "SDL_uikitmodes.h" +#include "SDL_uikitwindow.h" +#include "SDL_uikitopengles.h" + +#if SDL_IPHONE_KEYBOARD +#include "keyinfotable.h" +#endif + +#if TARGET_OS_TV +static void SDLCALL +SDL_AppleTVControllerUIHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint) +{ + @autoreleasepool { + SDL_uikitviewcontroller *viewcontroller = (__bridge SDL_uikitviewcontroller *) userdata; + viewcontroller.controllerUserInteractionEnabled = hint && (*hint != '0'); + } +} +#endif + +#if !TARGET_OS_TV +static void SDLCALL +SDL_HideHomeIndicatorHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint) +{ + @autoreleasepool { + SDL_uikitviewcontroller *viewcontroller = (__bridge SDL_uikitviewcontroller *) userdata; + viewcontroller.homeIndicatorHidden = (hint && *hint) ? SDL_atoi(hint) : -1; + if (@available(iOS 11.0, *)) { + [viewcontroller setNeedsUpdateOfHomeIndicatorAutoHidden]; + [viewcontroller setNeedsUpdateOfScreenEdgesDeferringSystemGestures]; + } + } +} +#endif + +@implementation SDL_uikitviewcontroller { + CADisplayLink *displayLink; + int animationInterval; + void (*animationCallback)(void*); + void *animationCallbackParam; + +#if SDL_IPHONE_KEYBOARD + UITextField *textField; + BOOL rotatingOrientation; + NSString *changeText; + NSString *obligateForBackspace; +#endif +} + +@synthesize window; + +- (instancetype)initWithSDLWindow:(SDL_Window *)_window +{ + if (self = [super initWithNibName:nil bundle:nil]) { + self.window = _window; + +#if SDL_IPHONE_KEYBOARD + [self initKeyboard]; + rotatingOrientation = FALSE; +#endif + +#if TARGET_OS_TV + SDL_AddHintCallback(SDL_HINT_APPLE_TV_CONTROLLER_UI_EVENTS, + SDL_AppleTVControllerUIHintChanged, + (__bridge void *) self); +#endif + +#if !TARGET_OS_TV + SDL_AddHintCallback(SDL_HINT_IOS_HIDE_HOME_INDICATOR, + SDL_HideHomeIndicatorHintChanged, + (__bridge void *) self); +#endif + } + return self; +} + +- (void)dealloc +{ +#if SDL_IPHONE_KEYBOARD + [self deinitKeyboard]; +#endif + +#if TARGET_OS_TV + SDL_DelHintCallback(SDL_HINT_APPLE_TV_CONTROLLER_UI_EVENTS, + SDL_AppleTVControllerUIHintChanged, + (__bridge void *) self); +#endif + +#if !TARGET_OS_TV + SDL_DelHintCallback(SDL_HINT_IOS_HIDE_HOME_INDICATOR, + SDL_HideHomeIndicatorHintChanged, + (__bridge void *) self); +#endif +} + +- (void)setAnimationCallback:(int)interval + callback:(void (*)(void*))callback + callbackParam:(void*)callbackParam +{ + [self stopAnimation]; + + animationInterval = interval; + animationCallback = callback; + animationCallbackParam = callbackParam; + + if (animationCallback) { + [self startAnimation]; + } +} + +- (void)startAnimation +{ + displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(doLoop:)]; + +#ifdef __IPHONE_10_3 + SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata; + + if ([displayLink respondsToSelector:@selector(preferredFramesPerSecond)] + && data != nil && data.uiwindow != nil + && [data.uiwindow.screen respondsToSelector:@selector(maximumFramesPerSecond)]) { + displayLink.preferredFramesPerSecond = data.uiwindow.screen.maximumFramesPerSecond / animationInterval; + } else +#endif + { +#if __IPHONE_OS_VERSION_MIN_REQUIRED < 100300 + [displayLink setFrameInterval:animationInterval]; +#endif + } + + [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; +} + +- (void)stopAnimation +{ + [displayLink invalidate]; + displayLink = nil; +} + +- (void)doLoop:(CADisplayLink*)sender +{ + /* Don't run the game loop while a messagebox is up */ + if (!UIKit_ShowingMessageBox()) { + /* See the comment in the function definition. */ + UIKit_GL_RestoreCurrentContext(); + + animationCallback(animationCallbackParam); + } +} + +- (void)loadView +{ + /* Do nothing. */ +} + +- (void)viewDidLayoutSubviews +{ + const CGSize size = self.view.bounds.size; + int w = (int) size.width; + int h = (int) size.height; + + SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, w, h); +} + +#if !TARGET_OS_TV +- (NSUInteger)supportedInterfaceOrientations +{ + return UIKit_GetSupportedOrientations(window); +} + +#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_7_0 +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orient +{ + return ([self supportedInterfaceOrientations] & (1 << orient)) != 0; +} +#endif + +- (BOOL)prefersStatusBarHidden +{ + BOOL hidden = (window->flags & (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_BORDERLESS)) != 0; + return hidden; +} + +- (BOOL)prefersHomeIndicatorAutoHidden +{ + BOOL hidden = NO; + if (self.homeIndicatorHidden == 1) { + hidden = YES; + } + return hidden; +} + +- (UIRectEdge)preferredScreenEdgesDeferringSystemGestures +{ + if (self.homeIndicatorHidden >= 0) { + if (self.homeIndicatorHidden == 2) { + return UIRectEdgeAll; + } else { + return UIRectEdgeNone; + } + } + + /* By default, fullscreen and borderless windows get all screen gestures */ + if ((window->flags & (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_BORDERLESS)) != 0) { + return UIRectEdgeAll; + } else { + return UIRectEdgeNone; + } +} +#endif + +/* + ---- Keyboard related functionality below this line ---- + */ +#if SDL_IPHONE_KEYBOARD + +@synthesize textInputRect; +@synthesize keyboardHeight; +@synthesize keyboardVisible; + +/* Set ourselves up as a UITextFieldDelegate */ +- (void)initKeyboard +{ + changeText = nil; + obligateForBackspace = @" "; /* 64 space */ + textField = [[UITextField alloc] initWithFrame:CGRectZero]; + textField.delegate = self; + /* placeholder so there is something to delete! */ + textField.text = obligateForBackspace; + + /* set UITextInputTrait properties, mostly to defaults */ + textField.autocapitalizationType = UITextAutocapitalizationTypeNone; + textField.autocorrectionType = UITextAutocorrectionTypeNo; + textField.enablesReturnKeyAutomatically = NO; + textField.keyboardAppearance = UIKeyboardAppearanceDefault; + textField.keyboardType = UIKeyboardTypeDefault; + textField.returnKeyType = UIReturnKeyDefault; + textField.secureTextEntry = NO; + + textField.hidden = YES; + keyboardVisible = NO; + + NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; +#if !TARGET_OS_TV + [center addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil]; + [center addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; +#endif + [center addObserver:self selector:@selector(textFieldTextDidChange:) name:UITextFieldTextDidChangeNotification object:nil]; +} + +- (void)setView:(UIView *)view +{ + [super setView:view]; + + [view addSubview:textField]; + + if (keyboardVisible) { + [self showKeyboard]; + } +} + +/* willRotateToInterfaceOrientation and didRotateFromInterfaceOrientation are deprecated in iOS 8+ in favor of viewWillTransitionToSize */ +#if TARGET_OS_TV || __IPHONE_OS_VERSION_MIN_REQUIRED >= 80000 +- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator +{ + [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; + rotatingOrientation = TRUE; + [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {} + completion:^(id<UIViewControllerTransitionCoordinatorContext> context) { + rotatingOrientation = FALSE; + }]; +} +#else +- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { + [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration]; + rotatingOrientation = TRUE; +} + +- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation { + [super didRotateFromInterfaceOrientation:fromInterfaceOrientation]; + rotatingOrientation = FALSE; +} +#endif /* TARGET_OS_TV || __IPHONE_OS_VERSION_MIN_REQUIRED >= 80000 */ + +- (void)deinitKeyboard +{ + NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; +#if !TARGET_OS_TV + [center removeObserver:self name:UIKeyboardWillShowNotification object:nil]; + [center removeObserver:self name:UIKeyboardWillHideNotification object:nil]; +#endif + [center removeObserver:self name:UITextFieldTextDidChangeNotification object:nil]; +} + +/* reveal onscreen virtual keyboard */ +- (void)showKeyboard +{ + keyboardVisible = YES; + if (textField.window) { + [textField becomeFirstResponder]; + } +} + +/* hide onscreen virtual keyboard */ +- (void)hideKeyboard +{ + keyboardVisible = NO; + [textField resignFirstResponder]; +} + +- (void)keyboardWillShow:(NSNotification *)notification +{ +#if !TARGET_OS_TV + CGRect kbrect = [[notification userInfo][UIKeyboardFrameEndUserInfoKey] CGRectValue]; + + /* The keyboard rect is in the coordinate space of the screen/window, but we + * want its height in the coordinate space of the view. */ + kbrect = [self.view convertRect:kbrect fromView:nil]; + + [self setKeyboardHeight:(int)kbrect.size.height]; +#endif +} + +- (void)keyboardWillHide:(NSNotification *)notification +{ + if (!rotatingOrientation) { + SDL_StopTextInput(); + } + [self setKeyboardHeight:0]; +} + +- (void)textFieldTextDidChange:(NSNotification *)notification +{ + if (changeText!=nil && textField.markedTextRange == nil) + { + NSUInteger len = changeText.length; + if (len > 0) { + /* Go through all the characters in the string we've been sent and + * convert them to key presses */ + int i; + for (i = 0; i < len; i++) { + unichar c = [changeText characterAtIndex:i]; + SDL_Scancode code; + Uint16 mod; + + if (c < 127) { + /* Figure out the SDL_Scancode and SDL_keymod for this unichar */ + code = unicharToUIKeyInfoTable[c].code; + mod = unicharToUIKeyInfoTable[c].mod; + } else { + /* We only deal with ASCII right now */ + code = SDL_SCANCODE_UNKNOWN; + mod = 0; + } + + if (mod & KMOD_SHIFT) { + /* If character uses shift, press shift down */ + SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_LSHIFT); + } + + /* send a keydown and keyup even for the character */ + SDL_SendKeyboardKey(SDL_PRESSED, code); + SDL_SendKeyboardKey(SDL_RELEASED, code); + + if (mod & KMOD_SHIFT) { + /* If character uses shift, press shift back up */ + SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_LSHIFT); + } + } + SDL_SendKeyboardText([changeText UTF8String]); + } + changeText = nil; + } +} + +- (void)updateKeyboard +{ + CGAffineTransform t = self.view.transform; + CGPoint offset = CGPointMake(0.0, 0.0); + CGRect frame = UIKit_ComputeViewFrame(window, self.view.window.screen); + + if (self.keyboardHeight) { + int rectbottom = self.textInputRect.y + self.textInputRect.h; + int keybottom = self.view.bounds.size.height - self.keyboardHeight; + if (keybottom < rectbottom) { + offset.y = keybottom - rectbottom; + } + } + + /* Apply this view's transform (except any translation) to the offset, in + * order to orient it correctly relative to the frame's coordinate space. */ + t.tx = 0.0; + t.ty = 0.0; + offset = CGPointApplyAffineTransform(offset, t); + + /* Apply the updated offset to the view's frame. */ + frame.origin.x += offset.x; + frame.origin.y += offset.y; + + self.view.frame = frame; +} + +- (void)setKeyboardHeight:(int)height +{ + keyboardVisible = height > 0; + keyboardHeight = height; + [self updateKeyboard]; +} + +/* UITextFieldDelegate method. Invoked when user types something. */ +- (BOOL)textField:(UITextField *)_textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string +{ + NSUInteger len = string.length; + if (len == 0) { + changeText = nil; + if (textField.markedTextRange == nil) { + /* it wants to replace text with nothing, ie a delete */ + SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_BACKSPACE); + SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_BACKSPACE); + } + if (textField.text.length < 16) { + textField.text = obligateForBackspace; + } + } else { + changeText = string; + } + return YES; +} + +/* Terminates the editing session */ +- (BOOL)textFieldShouldReturn:(UITextField*)_textField +{ + SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_RETURN); + SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_RETURN); + if (SDL_GetHintBoolean(SDL_HINT_RETURN_KEY_HIDES_IME, SDL_FALSE)) { + SDL_StopTextInput(); + } + return YES; +} + +#endif + +@end + +/* iPhone keyboard addition functions */ +#if SDL_IPHONE_KEYBOARD + +static SDL_uikitviewcontroller * +GetWindowViewController(SDL_Window * window) +{ + if (!window || !window->driverdata) { + SDL_SetError("Invalid window"); + return nil; + } + + SDL_WindowData *data = (__bridge SDL_WindowData *)window->driverdata; + + return data.viewcontroller; +} + +SDL_bool +UIKit_HasScreenKeyboardSupport(_THIS) +{ + return SDL_TRUE; +} + +void +UIKit_ShowScreenKeyboard(_THIS, SDL_Window *window) +{ + @autoreleasepool { + SDL_uikitviewcontroller *vc = GetWindowViewController(window); + [vc showKeyboard]; + } +} + +void +UIKit_HideScreenKeyboard(_THIS, SDL_Window *window) +{ + @autoreleasepool { + SDL_uikitviewcontroller *vc = GetWindowViewController(window); + [vc hideKeyboard]; + } +} + +SDL_bool +UIKit_IsScreenKeyboardShown(_THIS, SDL_Window *window) +{ + @autoreleasepool { + SDL_uikitviewcontroller *vc = GetWindowViewController(window); + if (vc != nil) { + return vc.keyboardVisible; + } + return SDL_FALSE; + } +} + +void +UIKit_SetTextInputRect(_THIS, SDL_Rect *rect) +{ + if (!rect) { + SDL_InvalidParamError("rect"); + return; + } + + @autoreleasepool { + SDL_uikitviewcontroller *vc = GetWindowViewController(SDL_GetFocusWindow()); + if (vc != nil) { + vc.textInputRect = *rect; + + if (vc.keyboardVisible) { + [vc updateKeyboard]; + } + } + } +} + + +#endif /* SDL_IPHONE_KEYBOARD */ + +#endif /* SDL_VIDEO_DRIVER_UIKIT */ + +/* vi: set ts=4 sw=4 expandtab: */ |