forked from Green-Sky/tomato
Merge commit 'dec0d4ec4153bf9fc2b78ae6c2df45b6ea8dde7a' as 'external/sdl/SDL'
This commit is contained in:
45
external/sdl/SDL/src/video/uikit/SDL_uikitappdelegate.h
vendored
Normal file
45
external/sdl/SDL/src/video/uikit/SDL_uikitappdelegate.h
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface SDLLaunchScreenController : UIViewController
|
||||
|
||||
- (instancetype)init;
|
||||
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil;
|
||||
- (void)loadView;
|
||||
|
||||
@end
|
||||
|
||||
@interface SDLUIKitDelegate : NSObject <UIApplicationDelegate>
|
||||
|
||||
+ (id)sharedAppDelegate;
|
||||
+ (NSString *)getAppDelegateClassName;
|
||||
|
||||
- (void)hideLaunchScreen;
|
||||
|
||||
/* This property is marked as optional, and is only intended to be used when
|
||||
* the app's UI is storyboard-based. SDL is not storyboard-based, however
|
||||
* several major third-party ad APIs (e.g. Google admob) incorrectly assume this
|
||||
* property always exists, and will crash if it doesn't. */
|
||||
@property(nonatomic) UIWindow *window;
|
||||
|
||||
@end
|
521
external/sdl/SDL/src/video/uikit/SDL_uikitappdelegate.m
vendored
Normal file
521
external/sdl/SDL/src/video/uikit/SDL_uikitappdelegate.m
vendored
Normal file
@ -0,0 +1,521 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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"
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_UIKIT
|
||||
|
||||
#include "../SDL_sysvideo.h"
|
||||
|
||||
#import "SDL_uikitappdelegate.h"
|
||||
#import "SDL_uikitmodes.h"
|
||||
#import "SDL_uikitwindow.h"
|
||||
|
||||
#include "../../events/SDL_events_c.h"
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
#include <AvailabilityVersions.h>
|
||||
|
||||
#ifndef __IPHONE_13_0
|
||||
#define __IPHONE_13_0 130000
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef main
|
||||
#undef main
|
||||
#endif
|
||||
|
||||
static SDL_main_func forward_main;
|
||||
static int forward_argc;
|
||||
static char **forward_argv;
|
||||
static int exit_status;
|
||||
|
||||
int SDL_RunApp(int argc, char* argv[], SDL_main_func mainFunction, void * reserved)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* store arguments */
|
||||
/* Note that we need to be careful about how we allocate/free memory here.
|
||||
* If the application calls SDL_SetMemoryFunctions(), we can't rely on
|
||||
* SDL_free() to use the same allocator after SDL_main() returns.
|
||||
*/
|
||||
forward_main = mainFunction;
|
||||
forward_argc = argc;
|
||||
forward_argv = (char **)malloc((argc + 1) * sizeof(char *)); /* This should NOT be SDL_malloc() */
|
||||
for (i = 0; i < argc; i++) {
|
||||
forward_argv[i] = malloc((strlen(argv[i]) + 1) * sizeof(char)); /* This should NOT be SDL_malloc() */
|
||||
strcpy(forward_argv[i], argv[i]);
|
||||
}
|
||||
forward_argv[i] = NULL;
|
||||
|
||||
/* Give over control to run loop, SDLUIKitDelegate will handle most things from here */
|
||||
@autoreleasepool {
|
||||
UIApplicationMain(argc, argv, nil, [SDLUIKitDelegate getAppDelegateClassName]);
|
||||
}
|
||||
|
||||
/* free the memory we used to hold copies of argc and argv */
|
||||
for (i = 0; i < forward_argc; i++) {
|
||||
free(forward_argv[i]); /* This should NOT be SDL_free() */
|
||||
}
|
||||
free(forward_argv); /* This should NOT be SDL_free() */
|
||||
|
||||
return exit_status;
|
||||
}
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
/* Load a launch image using the old UILaunchImageFile-era naming rules. */
|
||||
static UIImage *SDL_LoadLaunchImageNamed(NSString *name, int screenh)
|
||||
{
|
||||
UIInterfaceOrientation curorient = [UIApplication sharedApplication].statusBarOrientation;
|
||||
UIUserInterfaceIdiom idiom = [UIDevice currentDevice].userInterfaceIdiom;
|
||||
UIImage *image = nil;
|
||||
|
||||
if (idiom == UIUserInterfaceIdiomPhone && screenh == 568) {
|
||||
/* The image name for the iPhone 5 uses its height as a suffix. */
|
||||
image = [UIImage imageNamed:[NSString stringWithFormat:@"%@-568h", name]];
|
||||
} else if (idiom == UIUserInterfaceIdiomPad) {
|
||||
/* iPad apps can launch in any orientation. */
|
||||
if (UIInterfaceOrientationIsLandscape(curorient)) {
|
||||
if (curorient == UIInterfaceOrientationLandscapeLeft) {
|
||||
image = [UIImage imageNamed:[NSString stringWithFormat:@"%@-LandscapeLeft", name]];
|
||||
} else {
|
||||
image = [UIImage imageNamed:[NSString stringWithFormat:@"%@-LandscapeRight", name]];
|
||||
}
|
||||
if (!image) {
|
||||
image = [UIImage imageNamed:[NSString stringWithFormat:@"%@-Landscape", name]];
|
||||
}
|
||||
} else {
|
||||
if (curorient == UIInterfaceOrientationPortraitUpsideDown) {
|
||||
image = [UIImage imageNamed:[NSString stringWithFormat:@"%@-PortraitUpsideDown", name]];
|
||||
}
|
||||
if (!image) {
|
||||
image = [UIImage imageNamed:[NSString stringWithFormat:@"%@-Portrait", name]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!image) {
|
||||
image = [UIImage imageNamed:name];
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
@interface SDLLaunchStoryboardViewController : UIViewController
|
||||
@property(nonatomic, strong) UIViewController *storyboardViewController;
|
||||
- (instancetype)initWithStoryboardViewController:(UIViewController *)storyboardViewController;
|
||||
@end
|
||||
|
||||
@implementation SDLLaunchStoryboardViewController
|
||||
|
||||
- (instancetype)initWithStoryboardViewController:(UIViewController *)storyboardViewController
|
||||
{
|
||||
self = [super init];
|
||||
self.storyboardViewController = storyboardViewController;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
[self addChildViewController:self.storyboardViewController];
|
||||
[self.view addSubview:self.storyboardViewController.view];
|
||||
self.storyboardViewController.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
self.storyboardViewController.view.frame = self.view.bounds;
|
||||
[self.storyboardViewController didMoveToParentViewController:self];
|
||||
|
||||
UIApplication.sharedApplication.statusBarHidden = self.prefersStatusBarHidden;
|
||||
UIApplication.sharedApplication.statusBarStyle = self.preferredStatusBarStyle;
|
||||
}
|
||||
|
||||
- (BOOL)prefersStatusBarHidden
|
||||
{
|
||||
return [[NSBundle.mainBundle objectForInfoDictionaryKey:@"UIStatusBarHidden"] boolValue];
|
||||
}
|
||||
|
||||
- (UIStatusBarStyle)preferredStatusBarStyle
|
||||
{
|
||||
NSString *statusBarStyle = [NSBundle.mainBundle objectForInfoDictionaryKey:@"UIStatusBarStyle"];
|
||||
if ([statusBarStyle isEqualToString:@"UIStatusBarStyleLightContent"]) {
|
||||
return UIStatusBarStyleLightContent;
|
||||
}
|
||||
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0
|
||||
if (@available(iOS 13.0, *)) {
|
||||
if ([statusBarStyle isEqualToString:@"UIStatusBarStyleDarkContent"]) {
|
||||
return UIStatusBarStyleDarkContent;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return UIStatusBarStyleDefault;
|
||||
}
|
||||
|
||||
@end
|
||||
#endif /* !TARGET_OS_TV */
|
||||
|
||||
@interface SDLLaunchScreenController ()
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
- (NSUInteger)supportedInterfaceOrientations;
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
@implementation SDLLaunchScreenController
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
return [self initWithNibName:nil bundle:[NSBundle mainBundle]];
|
||||
}
|
||||
|
||||
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
|
||||
{
|
||||
if (!(self = [super initWithNibName:nil bundle:nil])) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSString *screenname = nibNameOrNil;
|
||||
NSBundle *bundle = nibBundleOrNil;
|
||||
|
||||
/* A launch screen may not exist. Fall back to launch images in that case. */
|
||||
if (screenname) {
|
||||
@try {
|
||||
self.view = [bundle loadNibNamed:screenname owner:self options:nil][0];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
/* If a launch screen name is specified but it fails to load, iOS
|
||||
* displays a blank screen rather than falling back to an image. */
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
if (!self.view) {
|
||||
NSArray *launchimages = [bundle objectForInfoDictionaryKey:@"UILaunchImages"];
|
||||
NSString *imagename = nil;
|
||||
UIImage *image = nil;
|
||||
|
||||
int screenw = (int)([UIScreen mainScreen].bounds.size.width + 0.5);
|
||||
int screenh = (int)([UIScreen mainScreen].bounds.size.height + 0.5);
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
UIInterfaceOrientation curorient = [UIApplication sharedApplication].statusBarOrientation;
|
||||
|
||||
/* We always want portrait-oriented size, to match UILaunchImageSize. */
|
||||
if (screenw > screenh) {
|
||||
int width = screenw;
|
||||
screenw = screenh;
|
||||
screenh = width;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Xcode 5 introduced a dictionary of launch images in Info.plist. */
|
||||
if (launchimages) {
|
||||
for (NSDictionary *dict in launchimages) {
|
||||
NSString *minversion = dict[@"UILaunchImageMinimumOSVersion"];
|
||||
NSString *sizestring = dict[@"UILaunchImageSize"];
|
||||
|
||||
/* Ignore this image if the current version is too low. */
|
||||
if (minversion && !UIKit_IsSystemVersionAtLeast(minversion.doubleValue)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Ignore this image if the size doesn't match. */
|
||||
if (sizestring) {
|
||||
CGSize size = CGSizeFromString(sizestring);
|
||||
if ((int)(size.width + 0.5) != screenw || (int)(size.height + 0.5) != screenh) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
UIInterfaceOrientationMask orientmask = UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown;
|
||||
NSString *orientstring = dict[@"UILaunchImageOrientation"];
|
||||
|
||||
if (orientstring) {
|
||||
if ([orientstring isEqualToString:@"PortraitUpsideDown"]) {
|
||||
orientmask = UIInterfaceOrientationMaskPortraitUpsideDown;
|
||||
} else if ([orientstring isEqualToString:@"Landscape"]) {
|
||||
orientmask = UIInterfaceOrientationMaskLandscape;
|
||||
} else if ([orientstring isEqualToString:@"LandscapeLeft"]) {
|
||||
orientmask = UIInterfaceOrientationMaskLandscapeLeft;
|
||||
} else if ([orientstring isEqualToString:@"LandscapeRight"]) {
|
||||
orientmask = UIInterfaceOrientationMaskLandscapeRight;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ignore this image if the orientation doesn't match. */
|
||||
if ((orientmask & (1 << curorient)) == 0) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
imagename = dict[@"UILaunchImageName"];
|
||||
}
|
||||
|
||||
if (imagename) {
|
||||
image = [UIImage imageNamed:imagename];
|
||||
}
|
||||
}
|
||||
#if !TARGET_OS_TV
|
||||
else {
|
||||
imagename = [bundle objectForInfoDictionaryKey:@"UILaunchImageFile"];
|
||||
|
||||
if (imagename) {
|
||||
image = SDL_LoadLaunchImageNamed(imagename, screenh);
|
||||
}
|
||||
|
||||
if (!image) {
|
||||
image = SDL_LoadLaunchImageNamed(@"Default", screenh);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (image) {
|
||||
UIImageView *view = [[UIImageView alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
||||
UIImageOrientation imageorient = UIImageOrientationUp;
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
/* Bugs observed / workaround tested in iOS 8.3. */
|
||||
if (UIInterfaceOrientationIsLandscape(curorient)) {
|
||||
if (image.size.width < image.size.height) {
|
||||
/* On iOS 8, portrait launch images displayed in forced-
|
||||
* landscape mode (e.g. a standard Default.png on an iPhone
|
||||
* when Info.plist only supports landscape orientations) need
|
||||
* to be rotated to display in the expected orientation. */
|
||||
if (curorient == UIInterfaceOrientationLandscapeLeft) {
|
||||
imageorient = UIImageOrientationRight;
|
||||
} else if (curorient == UIInterfaceOrientationLandscapeRight) {
|
||||
imageorient = UIImageOrientationLeft;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Create the properly oriented image. */
|
||||
view.image = [[UIImage alloc] initWithCGImage:image.CGImage scale:image.scale orientation:imageorient];
|
||||
|
||||
self.view = view;
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)loadView
|
||||
{
|
||||
/* Do nothing. */
|
||||
}
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
- (BOOL)shouldAutorotate
|
||||
{
|
||||
/* If YES, the launch image will be incorrectly rotated in some cases. */
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSUInteger)supportedInterfaceOrientations
|
||||
{
|
||||
/* We keep the supported orientations unrestricted to avoid the case where
|
||||
* there are no common orientations between the ones set in Info.plist and
|
||||
* the ones set here (it will cause an exception in that case.) */
|
||||
return UIInterfaceOrientationMaskAll;
|
||||
}
|
||||
#endif /* !TARGET_OS_TV */
|
||||
|
||||
@end
|
||||
|
||||
@implementation SDLUIKitDelegate
|
||||
{
|
||||
UIWindow *launchWindow;
|
||||
}
|
||||
|
||||
/* convenience method */
|
||||
+ (id)sharedAppDelegate
|
||||
{
|
||||
/* the delegate is set in UIApplicationMain(), which is guaranteed to be
|
||||
* called before this method */
|
||||
return [UIApplication sharedApplication].delegate;
|
||||
}
|
||||
|
||||
+ (NSString *)getAppDelegateClassName
|
||||
{
|
||||
/* subclassing notice: when you subclass this appdelegate, make sure to add
|
||||
* a category to override this method and return the actual name of the
|
||||
* delegate */
|
||||
return @"SDLUIKitDelegate";
|
||||
}
|
||||
|
||||
- (void)hideLaunchScreen
|
||||
{
|
||||
UIWindow *window = launchWindow;
|
||||
|
||||
if (!window || window.hidden) {
|
||||
return;
|
||||
}
|
||||
|
||||
launchWindow = nil;
|
||||
|
||||
/* Do a nice animated fade-out (roughly matches the real launch behavior.) */
|
||||
[UIView animateWithDuration:0.2
|
||||
animations:^{
|
||||
window.alpha = 0.0;
|
||||
}
|
||||
completion:^(BOOL finished) {
|
||||
window.hidden = YES;
|
||||
UIKit_ForceUpdateHomeIndicator(); /* Wait for launch screen to hide so settings are applied to the actual view controller. */
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)postFinishLaunch
|
||||
{
|
||||
/* Hide the launch screen the next time the run loop is run. SDL apps will
|
||||
* have a chance to load resources while the launch screen is still up. */
|
||||
[self performSelector:@selector(hideLaunchScreen) withObject:nil afterDelay:0.0];
|
||||
|
||||
/* run the user's application, passing argc and argv */
|
||||
SDL_iPhoneSetEventPump(SDL_TRUE);
|
||||
exit_status = forward_main(forward_argc, forward_argv);
|
||||
SDL_iPhoneSetEventPump(SDL_FALSE);
|
||||
|
||||
if (launchWindow) {
|
||||
launchWindow.hidden = YES;
|
||||
launchWindow = nil;
|
||||
}
|
||||
|
||||
/* exit, passing the return status from the user's application */
|
||||
/* We don't actually exit to support applications that do setup in their
|
||||
* main function and then allow the Cocoa event loop to run. */
|
||||
/* exit(exit_status); */
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
NSBundle *bundle = [NSBundle mainBundle];
|
||||
|
||||
#ifdef SDL_IPHONE_LAUNCHSCREEN
|
||||
/* The normal launch screen is displayed until didFinishLaunching returns,
|
||||
* but SDL_main is called after that happens and there may be a noticeable
|
||||
* delay between the start of SDL_main and when the first real frame is
|
||||
* displayed (e.g. if resources are loaded before SDL_GL_SwapWindow is
|
||||
* called), so we show the launch screen programmatically until the first
|
||||
* time events are pumped. */
|
||||
UIViewController *vc = nil;
|
||||
NSString *screenname = nil;
|
||||
|
||||
/* tvOS only uses a plain launch image. */
|
||||
#if !TARGET_OS_TV
|
||||
screenname = [bundle objectForInfoDictionaryKey:@"UILaunchStoryboardName"];
|
||||
|
||||
if (screenname) {
|
||||
@try {
|
||||
/* The launch storyboard is actually a nib in some older versions of
|
||||
* Xcode. We'll try to load it as a storyboard first, as it's more
|
||||
* modern. */
|
||||
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:screenname bundle:bundle];
|
||||
__auto_type storyboardVc = [storyboard instantiateInitialViewController];
|
||||
vc = [[SDLLaunchStoryboardViewController alloc] initWithStoryboardViewController:storyboardVc];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
/* Do nothing (there's more code to execute below). */
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (vc == nil) {
|
||||
vc = [[SDLLaunchScreenController alloc] initWithNibName:screenname bundle:bundle];
|
||||
}
|
||||
|
||||
if (vc.view) {
|
||||
launchWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
||||
|
||||
/* We don't want the launch window immediately hidden when a real SDL
|
||||
* window is shown - we fade it out ourselves when we're ready. */
|
||||
launchWindow.windowLevel = UIWindowLevelNormal + 1.0;
|
||||
|
||||
/* Show the window but don't make it key. Events should always go to
|
||||
* other windows when possible. */
|
||||
launchWindow.hidden = NO;
|
||||
|
||||
launchWindow.rootViewController = vc;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Set working directory to resource path */
|
||||
[[NSFileManager defaultManager] changeCurrentDirectoryPath:[bundle resourcePath]];
|
||||
|
||||
SDL_SetMainReady();
|
||||
[self performSelector:@selector(postFinishLaunch) withObject:nil afterDelay:0.0];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (UIWindow *)window
|
||||
{
|
||||
SDL_VideoDevice *_this = SDL_GetVideoDevice();
|
||||
if (_this) {
|
||||
SDL_Window *window = NULL;
|
||||
for (window = _this->windows; window != NULL; window = window->next) {
|
||||
SDL_UIKitWindowData *data = (__bridge SDL_UIKitWindowData *)window->driverdata;
|
||||
if (data != nil) {
|
||||
return data.uiwindow;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)setWindow:(UIWindow *)window
|
||||
{
|
||||
/* Do nothing. */
|
||||
}
|
||||
|
||||
- (void)sendDropFileForURL:(NSURL *)url
|
||||
{
|
||||
NSURL *fileURL = url.filePathURL;
|
||||
if (fileURL != nil) {
|
||||
SDL_SendDropFile(NULL, fileURL.path.UTF8String);
|
||||
} else {
|
||||
SDL_SendDropFile(NULL, url.absoluteString.UTF8String);
|
||||
}
|
||||
SDL_SendDropComplete(NULL);
|
||||
}
|
||||
|
||||
#if TARGET_OS_TV || (defined(__IPHONE_9_0) && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_9_0)
|
||||
|
||||
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options
|
||||
{
|
||||
/* TODO: Handle options */
|
||||
[self sendDropFileForURL:url];
|
||||
return YES;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
|
||||
{
|
||||
[self sendDropFileForURL:url];
|
||||
return YES;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SDL_VIDEO_DRIVER_UIKIT */
|
33
external/sdl/SDL/src/video/uikit/SDL_uikitclipboard.h
vendored
Normal file
33
external/sdl/SDL/src/video/uikit/SDL_uikitclipboard.h
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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.
|
||||
*/
|
||||
#ifndef SDL_uikitclipboard_h_
|
||||
#define SDL_uikitclipboard_h_
|
||||
|
||||
#include "../SDL_sysvideo.h"
|
||||
|
||||
extern int UIKit_SetClipboardText(SDL_VideoDevice *_this, const char *text);
|
||||
extern char *UIKit_GetClipboardText(SDL_VideoDevice *_this);
|
||||
extern SDL_bool UIKit_HasClipboardText(SDL_VideoDevice *_this);
|
||||
|
||||
extern void UIKit_InitClipboard(SDL_VideoDevice *_this);
|
||||
extern void UIKit_QuitClipboard(SDL_VideoDevice *_this);
|
||||
|
||||
#endif /* SDL_uikitclipboard_h_ */
|
104
external/sdl/SDL/src/video/uikit/SDL_uikitclipboard.m
vendored
Normal file
104
external/sdl/SDL/src/video/uikit/SDL_uikitclipboard.m
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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"
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_UIKIT
|
||||
|
||||
#include "SDL_uikitvideo.h"
|
||||
#include "../../events/SDL_clipboardevents_c.h"
|
||||
|
||||
#import <UIKit/UIPasteboard.h>
|
||||
|
||||
int UIKit_SetClipboardText(SDL_VideoDevice *_this, const char *text)
|
||||
{
|
||||
#if TARGET_OS_TV
|
||||
return SDL_SetError("The clipboard is not available on tvOS");
|
||||
#else
|
||||
@autoreleasepool {
|
||||
[UIPasteboard generalPasteboard].string = @(text);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
char *UIKit_GetClipboardText(SDL_VideoDevice *_this)
|
||||
{
|
||||
#if TARGET_OS_TV
|
||||
return SDL_strdup(""); // Unsupported.
|
||||
#else
|
||||
@autoreleasepool {
|
||||
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
|
||||
NSString *string = pasteboard.string;
|
||||
|
||||
if (string != nil) {
|
||||
return SDL_strdup(string.UTF8String);
|
||||
} else {
|
||||
return SDL_strdup("");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
SDL_bool UIKit_HasClipboardText(SDL_VideoDevice *_this)
|
||||
{
|
||||
@autoreleasepool {
|
||||
#if !TARGET_OS_TV
|
||||
if ([UIPasteboard generalPasteboard].string != nil) {
|
||||
return SDL_TRUE;
|
||||
}
|
||||
#endif
|
||||
return SDL_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
void UIKit_InitClipboard(SDL_VideoDevice *_this)
|
||||
{
|
||||
#if !TARGET_OS_TV
|
||||
@autoreleasepool {
|
||||
SDL_UIKitVideoData *data = (__bridge SDL_UIKitVideoData *)_this->driverdata;
|
||||
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
|
||||
|
||||
id observer = [center addObserverForName:UIPasteboardChangedNotification
|
||||
object:nil
|
||||
queue:nil
|
||||
usingBlock:^(NSNotification *note) {
|
||||
SDL_SendClipboardUpdate();
|
||||
}];
|
||||
|
||||
data.pasteboardObserver = observer;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void UIKit_QuitClipboard(SDL_VideoDevice *_this)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL_UIKitVideoData *data = (__bridge SDL_UIKitVideoData *)_this->driverdata;
|
||||
|
||||
if (data.pasteboardObserver != nil) {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:data.pasteboardObserver];
|
||||
}
|
||||
|
||||
data.pasteboardObserver = nil;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* SDL_VIDEO_DRIVER_UIKIT */
|
40
external/sdl/SDL/src/video/uikit/SDL_uikitevents.h
vendored
Normal file
40
external/sdl/SDL/src/video/uikit/SDL_uikitevents.h
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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.
|
||||
*/
|
||||
#ifndef SDL_uikitevents_h_
|
||||
#define SDL_uikitevents_h_
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#include "../SDL_sysvideo.h"
|
||||
|
||||
extern Uint64 UIKit_GetEventTimestamp(NSTimeInterval nsTimestamp);
|
||||
extern void UIKit_PumpEvents(SDL_VideoDevice *_this);
|
||||
|
||||
extern void SDL_InitGCKeyboard(void);
|
||||
extern SDL_bool SDL_HasGCKeyboard(void);
|
||||
extern void SDL_QuitGCKeyboard(void);
|
||||
|
||||
extern void SDL_InitGCMouse(void);
|
||||
extern SDL_bool SDL_HasGCMouse(void);
|
||||
extern SDL_bool SDL_GCMouseRelativeMode(void);
|
||||
extern void SDL_QuitGCMouse(void);
|
||||
|
||||
#endif /* SDL_uikitevents_h_ */
|
454
external/sdl/SDL/src/video/uikit/SDL_uikitevents.m
vendored
Normal file
454
external/sdl/SDL/src/video/uikit/SDL_uikitevents.m
vendored
Normal file
@ -0,0 +1,454 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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"
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_UIKIT
|
||||
|
||||
#include "../../events/SDL_events_c.h"
|
||||
|
||||
#include "SDL_uikitevents.h"
|
||||
#include "SDL_uikitopengles.h"
|
||||
#include "SDL_uikitvideo.h"
|
||||
#include "SDL_uikitwindow.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 140000) || (__APPLETV_OS_VERSION_MAX_ALLOWED >= 140000) || (__MAC_OS_VERSION_MAX_ALLOWED > 1500000)
|
||||
#import <GameController/GameController.h>
|
||||
|
||||
#define ENABLE_GCKEYBOARD
|
||||
#define ENABLE_GCMOUSE
|
||||
#endif
|
||||
|
||||
static BOOL UIKit_EventPumpEnabled = YES;
|
||||
|
||||
@interface SDL_LifecycleObserver : NSObject
|
||||
@property(nonatomic, assign) BOOL isObservingNotifications;
|
||||
@end
|
||||
|
||||
@implementation SDL_LifecycleObserver
|
||||
|
||||
- (void)eventPumpChanged
|
||||
{
|
||||
NSNotificationCenter *notificationCenter = NSNotificationCenter.defaultCenter;
|
||||
if (UIKit_EventPumpEnabled && !self.isObservingNotifications) {
|
||||
self.isObservingNotifications = YES;
|
||||
[notificationCenter addObserver:self selector:@selector(applicationDidBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil];
|
||||
[notificationCenter addObserver:self selector:@selector(applicationWillResignActive) name:UIApplicationWillResignActiveNotification object:nil];
|
||||
[notificationCenter addObserver:self selector:@selector(applicationDidEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil];
|
||||
[notificationCenter addObserver:self selector:@selector(applicationWillEnterForeground) name:UIApplicationWillEnterForegroundNotification object:nil];
|
||||
[notificationCenter addObserver:self selector:@selector(applicationWillTerminate) name:UIApplicationWillTerminateNotification object:nil];
|
||||
[notificationCenter addObserver:self selector:@selector(applicationDidReceiveMemoryWarning) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
|
||||
#if !TARGET_OS_TV
|
||||
[notificationCenter addObserver:self
|
||||
selector:@selector(applicationDidChangeStatusBarOrientation)
|
||||
name:UIApplicationDidChangeStatusBarOrientationNotification
|
||||
object:nil];
|
||||
#endif
|
||||
} else if (!UIKit_EventPumpEnabled && self.isObservingNotifications) {
|
||||
self.isObservingNotifications = NO;
|
||||
[notificationCenter removeObserver:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationDidBecomeActive
|
||||
{
|
||||
SDL_OnApplicationDidBecomeActive();
|
||||
}
|
||||
|
||||
- (void)applicationWillResignActive
|
||||
{
|
||||
SDL_OnApplicationWillResignActive();
|
||||
}
|
||||
|
||||
- (void)applicationDidEnterBackground
|
||||
{
|
||||
SDL_OnApplicationDidEnterBackground();
|
||||
}
|
||||
|
||||
- (void)applicationWillEnterForeground
|
||||
{
|
||||
SDL_OnApplicationWillEnterForeground();
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate
|
||||
{
|
||||
SDL_OnApplicationWillTerminate();
|
||||
}
|
||||
|
||||
- (void)applicationDidReceiveMemoryWarning
|
||||
{
|
||||
SDL_OnApplicationDidReceiveMemoryWarning();
|
||||
}
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
- (void)applicationDidChangeStatusBarOrientation
|
||||
{
|
||||
SDL_OnApplicationDidChangeStatusBarOrientation();
|
||||
}
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
Uint64 UIKit_GetEventTimestamp(NSTimeInterval nsTimestamp)
|
||||
{
|
||||
static Uint64 timestamp_offset;
|
||||
Uint64 timestamp = (Uint64)(nsTimestamp * SDL_NS_PER_SECOND);
|
||||
Uint64 now = SDL_GetTicksNS();
|
||||
|
||||
if (!timestamp_offset) {
|
||||
timestamp_offset = (now - timestamp);
|
||||
}
|
||||
timestamp += timestamp_offset;
|
||||
|
||||
if (timestamp > now) {
|
||||
timestamp_offset -= (timestamp - now);
|
||||
timestamp = now;
|
||||
}
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
void SDL_iPhoneSetEventPump(SDL_bool enabled)
|
||||
{
|
||||
UIKit_EventPumpEnabled = enabled;
|
||||
|
||||
static SDL_LifecycleObserver *lifecycleObserver;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
lifecycleObserver = [SDL_LifecycleObserver new];
|
||||
});
|
||||
[lifecycleObserver eventPumpChanged];
|
||||
}
|
||||
|
||||
void UIKit_PumpEvents(SDL_VideoDevice *_this)
|
||||
{
|
||||
if (!UIKit_EventPumpEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Let the run loop run for a short amount of time: long enough for
|
||||
touch events to get processed (which is important to get certain
|
||||
elements of Game Center's GKLeaderboardViewController to respond
|
||||
to touch input), but not long enough to introduce a significant
|
||||
delay in the rest of the app.
|
||||
*/
|
||||
const CFTimeInterval seconds = 0.000002;
|
||||
|
||||
/* Pump most event types. */
|
||||
SInt32 result;
|
||||
do {
|
||||
result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, seconds, TRUE);
|
||||
} while (result == kCFRunLoopRunHandledSource);
|
||||
|
||||
/* Make sure UIScrollView objects scroll properly. */
|
||||
do {
|
||||
result = CFRunLoopRunInMode((CFStringRef)UITrackingRunLoopMode, seconds, TRUE);
|
||||
} while (result == kCFRunLoopRunHandledSource);
|
||||
|
||||
/* See the comment in the function definition. */
|
||||
#if defined(SDL_VIDEO_OPENGL_ES) || defined(SDL_VIDEO_OPENGL_ES2)
|
||||
UIKit_GL_RestoreCurrentContext();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ENABLE_GCKEYBOARD
|
||||
|
||||
static SDL_bool keyboard_connected = SDL_FALSE;
|
||||
static id keyboard_connect_observer = nil;
|
||||
static id keyboard_disconnect_observer = nil;
|
||||
|
||||
static void OnGCKeyboardConnected(GCKeyboard *keyboard) API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0))
|
||||
{
|
||||
keyboard_connected = SDL_TRUE;
|
||||
keyboard.keyboardInput.keyChangedHandler = ^(GCKeyboardInput *kbrd, GCControllerButtonInput *key, GCKeyCode keyCode, BOOL pressed) {
|
||||
SDL_SendKeyboardKey(0, pressed ? SDL_PRESSED : SDL_RELEASED, (SDL_Scancode)keyCode);
|
||||
};
|
||||
|
||||
dispatch_queue_t queue = dispatch_queue_create("org.libsdl.input.keyboard", DISPATCH_QUEUE_SERIAL);
|
||||
dispatch_set_target_queue(queue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0));
|
||||
keyboard.handlerQueue = queue;
|
||||
}
|
||||
|
||||
static void OnGCKeyboardDisconnected(GCKeyboard *keyboard) API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0))
|
||||
{
|
||||
keyboard.keyboardInput.keyChangedHandler = nil;
|
||||
keyboard_connected = SDL_FALSE;
|
||||
}
|
||||
|
||||
void SDL_InitGCKeyboard(void)
|
||||
{
|
||||
@autoreleasepool {
|
||||
if (@available(iOS 14.0, tvOS 14.0, *)) {
|
||||
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
|
||||
|
||||
keyboard_connect_observer = [center addObserverForName:GCKeyboardDidConnectNotification
|
||||
object:nil
|
||||
queue:nil
|
||||
usingBlock:^(NSNotification *note) {
|
||||
GCKeyboard *keyboard = note.object;
|
||||
OnGCKeyboardConnected(keyboard);
|
||||
}];
|
||||
|
||||
keyboard_disconnect_observer = [center addObserverForName:GCKeyboardDidDisconnectNotification
|
||||
object:nil
|
||||
queue:nil
|
||||
usingBlock:^(NSNotification *note) {
|
||||
GCKeyboard *keyboard = note.object;
|
||||
OnGCKeyboardDisconnected(keyboard);
|
||||
}];
|
||||
|
||||
if (GCKeyboard.coalescedKeyboard != nil) {
|
||||
OnGCKeyboardConnected(GCKeyboard.coalescedKeyboard);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SDL_bool SDL_HasGCKeyboard(void)
|
||||
{
|
||||
return keyboard_connected;
|
||||
}
|
||||
|
||||
void SDL_QuitGCKeyboard(void)
|
||||
{
|
||||
@autoreleasepool {
|
||||
if (@available(iOS 14.0, tvOS 14.0, *)) {
|
||||
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
|
||||
|
||||
if (keyboard_connect_observer) {
|
||||
[center removeObserver:keyboard_connect_observer name:GCKeyboardDidConnectNotification object:nil];
|
||||
keyboard_connect_observer = nil;
|
||||
}
|
||||
|
||||
if (keyboard_disconnect_observer) {
|
||||
[center removeObserver:keyboard_disconnect_observer name:GCKeyboardDidDisconnectNotification object:nil];
|
||||
keyboard_disconnect_observer = nil;
|
||||
}
|
||||
|
||||
if (GCKeyboard.coalescedKeyboard != nil) {
|
||||
OnGCKeyboardDisconnected(GCKeyboard.coalescedKeyboard);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void SDL_InitGCKeyboard(void)
|
||||
{
|
||||
}
|
||||
|
||||
SDL_bool SDL_HasGCKeyboard(void)
|
||||
{
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
void SDL_QuitGCKeyboard(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* ENABLE_GCKEYBOARD */
|
||||
|
||||
#ifdef ENABLE_GCMOUSE
|
||||
|
||||
static int mice_connected = 0;
|
||||
static id mouse_connect_observer = nil;
|
||||
static id mouse_disconnect_observer = nil;
|
||||
static bool mouse_relative_mode = SDL_FALSE;
|
||||
|
||||
static void UpdatePointerLock(void)
|
||||
{
|
||||
SDL_VideoDevice *_this = SDL_GetVideoDevice();
|
||||
SDL_Window *window;
|
||||
|
||||
for (window = _this->windows; window != NULL; window = window->next) {
|
||||
UIKit_UpdatePointerLock(_this, window);
|
||||
}
|
||||
}
|
||||
|
||||
static int SetGCMouseRelativeMode(SDL_bool enabled)
|
||||
{
|
||||
mouse_relative_mode = enabled;
|
||||
UpdatePointerLock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void OnGCMouseButtonChanged(SDL_MouseID mouseID, Uint8 button, BOOL pressed)
|
||||
{
|
||||
SDL_SendMouseButton(0, SDL_GetMouseFocus(), mouseID, pressed ? SDL_PRESSED : SDL_RELEASED, button);
|
||||
}
|
||||
|
||||
static void OnGCMouseConnected(GCMouse *mouse) API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0))
|
||||
{
|
||||
SDL_MouseID mouseID = mice_connected;
|
||||
|
||||
mouse.mouseInput.leftButton.pressedChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
OnGCMouseButtonChanged(mouseID, SDL_BUTTON_LEFT, pressed);
|
||||
};
|
||||
mouse.mouseInput.middleButton.pressedChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
OnGCMouseButtonChanged(mouseID, SDL_BUTTON_MIDDLE, pressed);
|
||||
};
|
||||
mouse.mouseInput.rightButton.pressedChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
OnGCMouseButtonChanged(mouseID, SDL_BUTTON_RIGHT, pressed);
|
||||
};
|
||||
|
||||
int auxiliary_button = SDL_BUTTON_X1;
|
||||
for (GCControllerButtonInput *btn in mouse.mouseInput.auxiliaryButtons) {
|
||||
btn.pressedChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
|
||||
OnGCMouseButtonChanged(mouseID, auxiliary_button, pressed);
|
||||
};
|
||||
++auxiliary_button;
|
||||
}
|
||||
|
||||
mouse.mouseInput.mouseMovedHandler = ^(GCMouseInput *mouseInput, float deltaX, float deltaY) {
|
||||
if (SDL_GCMouseRelativeMode()) {
|
||||
SDL_SendMouseMotion(0, SDL_GetMouseFocus(), mouseID, 1, deltaX, -deltaY);
|
||||
}
|
||||
};
|
||||
|
||||
mouse.mouseInput.scroll.valueChangedHandler = ^(GCControllerDirectionPad *dpad, float xValue, float yValue) {
|
||||
SDL_SendMouseWheel(0, SDL_GetMouseFocus(), 0, xValue, yValue, SDL_MOUSEWHEEL_NORMAL);
|
||||
};
|
||||
|
||||
dispatch_queue_t queue = dispatch_queue_create("org.libsdl.input.mouse", DISPATCH_QUEUE_SERIAL);
|
||||
dispatch_set_target_queue(queue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0));
|
||||
mouse.handlerQueue = queue;
|
||||
|
||||
++mice_connected;
|
||||
|
||||
UpdatePointerLock();
|
||||
}
|
||||
|
||||
static void OnGCMouseDisconnected(GCMouse *mouse) API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0))
|
||||
{
|
||||
--mice_connected;
|
||||
|
||||
mouse.mouseInput.mouseMovedHandler = nil;
|
||||
|
||||
mouse.mouseInput.leftButton.pressedChangedHandler = nil;
|
||||
mouse.mouseInput.middleButton.pressedChangedHandler = nil;
|
||||
mouse.mouseInput.rightButton.pressedChangedHandler = nil;
|
||||
|
||||
for (GCControllerButtonInput *button in mouse.mouseInput.auxiliaryButtons) {
|
||||
button.pressedChangedHandler = nil;
|
||||
}
|
||||
|
||||
UpdatePointerLock();
|
||||
}
|
||||
|
||||
void SDL_InitGCMouse(void)
|
||||
{
|
||||
@autoreleasepool {
|
||||
/* There is a bug where mouse accumulates duplicate deltas over time in iOS 14.0 */
|
||||
if (@available(iOS 14.1, tvOS 14.1, *)) {
|
||||
/* iOS will not send the new pointer touch events if you don't have this key,
|
||||
* and we need them to differentiate between mouse events and real touch events.
|
||||
*/
|
||||
BOOL indirect_input_available = [[[[NSBundle mainBundle] infoDictionary] objectForKey:@"UIApplicationSupportsIndirectInputEvents"] boolValue];
|
||||
if (indirect_input_available) {
|
||||
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
|
||||
|
||||
mouse_connect_observer = [center addObserverForName:GCMouseDidConnectNotification
|
||||
object:nil
|
||||
queue:nil
|
||||
usingBlock:^(NSNotification *note) {
|
||||
GCMouse *mouse = note.object;
|
||||
OnGCMouseConnected(mouse);
|
||||
}];
|
||||
|
||||
mouse_disconnect_observer = [center addObserverForName:GCMouseDidDisconnectNotification
|
||||
object:nil
|
||||
queue:nil
|
||||
usingBlock:^(NSNotification *note) {
|
||||
GCMouse *mouse = note.object;
|
||||
OnGCMouseDisconnected(mouse);
|
||||
}];
|
||||
|
||||
for (GCMouse *mouse in [GCMouse mice]) {
|
||||
OnGCMouseConnected(mouse);
|
||||
}
|
||||
|
||||
SDL_GetMouse()->SetRelativeMouseMode = SetGCMouseRelativeMode;
|
||||
} else {
|
||||
NSLog(@"You need UIApplicationSupportsIndirectInputEvents in your Info.plist for mouse support");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SDL_bool SDL_HasGCMouse(void)
|
||||
{
|
||||
return (mice_connected > 0);
|
||||
}
|
||||
|
||||
SDL_bool SDL_GCMouseRelativeMode(void)
|
||||
{
|
||||
return mouse_relative_mode;
|
||||
}
|
||||
|
||||
void SDL_QuitGCMouse(void)
|
||||
{
|
||||
@autoreleasepool {
|
||||
if (@available(iOS 14.1, tvOS 14.1, *)) {
|
||||
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
|
||||
|
||||
if (mouse_connect_observer) {
|
||||
[center removeObserver:mouse_connect_observer name:GCMouseDidConnectNotification object:nil];
|
||||
mouse_connect_observer = nil;
|
||||
}
|
||||
|
||||
if (mouse_disconnect_observer) {
|
||||
[center removeObserver:mouse_disconnect_observer name:GCMouseDidDisconnectNotification object:nil];
|
||||
mouse_disconnect_observer = nil;
|
||||
}
|
||||
|
||||
for (GCMouse *mouse in [GCMouse mice]) {
|
||||
OnGCMouseDisconnected(mouse);
|
||||
}
|
||||
|
||||
SDL_GetMouse()->SetRelativeMouseMode = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void SDL_InitGCMouse(void)
|
||||
{
|
||||
}
|
||||
|
||||
SDL_bool SDL_HasGCMouse(void)
|
||||
{
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
SDL_bool SDL_GCMouseRelativeMode(void)
|
||||
{
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
void SDL_QuitGCMouse(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* ENABLE_GCMOUSE */
|
||||
|
||||
#endif /* SDL_VIDEO_DRIVER_UIKIT */
|
29
external/sdl/SDL/src/video/uikit/SDL_uikitmessagebox.h
vendored
Normal file
29
external/sdl/SDL/src/video/uikit/SDL_uikitmessagebox.h
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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"
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_UIKIT
|
||||
|
||||
extern SDL_bool UIKit_ShowingMessageBox(void);
|
||||
|
||||
extern int UIKit_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid);
|
||||
|
||||
#endif /* SDL_VIDEO_DRIVER_UIKIT */
|
150
external/sdl/SDL/src/video/uikit/SDL_uikitmessagebox.m
vendored
Normal file
150
external/sdl/SDL/src/video/uikit/SDL_uikitmessagebox.m
vendored
Normal file
@ -0,0 +1,150 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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"
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_UIKIT
|
||||
|
||||
#include "SDL_uikitvideo.h"
|
||||
#include "SDL_uikitwindow.h"
|
||||
|
||||
/* Display a UIKit message box */
|
||||
|
||||
static SDL_bool s_showingMessageBox = SDL_FALSE;
|
||||
|
||||
SDL_bool UIKit_ShowingMessageBox(void)
|
||||
{
|
||||
return s_showingMessageBox;
|
||||
}
|
||||
|
||||
static void UIKit_WaitUntilMessageBoxClosed(const SDL_MessageBoxData *messageboxdata, int *clickedindex)
|
||||
{
|
||||
*clickedindex = messageboxdata->numbuttons;
|
||||
|
||||
@autoreleasepool {
|
||||
/* Run the main event loop until the alert has finished */
|
||||
/* Note that this needs to be done on the main thread */
|
||||
s_showingMessageBox = SDL_TRUE;
|
||||
while ((*clickedindex) == messageboxdata->numbuttons) {
|
||||
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
|
||||
}
|
||||
s_showingMessageBox = SDL_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL UIKit_ShowMessageBoxAlertController(const SDL_MessageBoxData *messageboxdata, int *buttonid)
|
||||
{
|
||||
int i;
|
||||
int __block clickedindex = messageboxdata->numbuttons;
|
||||
UIWindow *window = nil;
|
||||
UIWindow *alertwindow = nil;
|
||||
|
||||
if (![UIAlertController class]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
UIAlertController *alert;
|
||||
alert = [UIAlertController alertControllerWithTitle:@(messageboxdata->title)
|
||||
message:@(messageboxdata->message)
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
for (i = 0; i < messageboxdata->numbuttons; i++) {
|
||||
UIAlertAction *action;
|
||||
UIAlertActionStyle style = UIAlertActionStyleDefault;
|
||||
const SDL_MessageBoxButtonData *sdlButton;
|
||||
|
||||
if (messageboxdata->flags & SDL_MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT) {
|
||||
sdlButton = &messageboxdata->buttons[messageboxdata->numbuttons - 1 - i];
|
||||
} else {
|
||||
sdlButton = &messageboxdata->buttons[i];
|
||||
}
|
||||
|
||||
if (sdlButton->flags & SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT) {
|
||||
style = UIAlertActionStyleCancel;
|
||||
}
|
||||
|
||||
action = [UIAlertAction actionWithTitle:@(sdlButton->text)
|
||||
style:style
|
||||
handler:^(UIAlertAction *alertAction) {
|
||||
clickedindex = (int)(sdlButton - messageboxdata->buttons);
|
||||
}];
|
||||
[alert addAction:action];
|
||||
|
||||
if (sdlButton->flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT) {
|
||||
alert.preferredAction = action;
|
||||
}
|
||||
}
|
||||
|
||||
if (messageboxdata->window) {
|
||||
SDL_UIKitWindowData *data = (__bridge SDL_UIKitWindowData *)messageboxdata->window->driverdata;
|
||||
window = data.uiwindow;
|
||||
}
|
||||
|
||||
if (window == nil || window.rootViewController == nil) {
|
||||
alertwindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
||||
alertwindow.rootViewController = [UIViewController new];
|
||||
alertwindow.windowLevel = UIWindowLevelAlert;
|
||||
|
||||
window = alertwindow;
|
||||
|
||||
[alertwindow makeKeyAndVisible];
|
||||
}
|
||||
|
||||
[window.rootViewController presentViewController:alert animated:YES completion:nil];
|
||||
UIKit_WaitUntilMessageBoxClosed(messageboxdata, &clickedindex);
|
||||
|
||||
if (alertwindow) {
|
||||
alertwindow.hidden = YES;
|
||||
}
|
||||
|
||||
UIKit_ForceUpdateHomeIndicator();
|
||||
|
||||
*buttonid = messageboxdata->buttons[clickedindex].buttonid;
|
||||
return YES;
|
||||
}
|
||||
|
||||
static void UIKit_ShowMessageBoxImpl(const SDL_MessageBoxData *messageboxdata, int *buttonid, int *returnValue)
|
||||
{
|
||||
@autoreleasepool {
|
||||
if (UIKit_ShowMessageBoxAlertController(messageboxdata, buttonid)) {
|
||||
*returnValue = 0;
|
||||
} else {
|
||||
*returnValue = SDL_SetError("Could not show message box.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int UIKit_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
|
||||
{
|
||||
@autoreleasepool {
|
||||
__block int returnValue = 0;
|
||||
|
||||
if ([NSThread isMainThread]) {
|
||||
UIKit_ShowMessageBoxImpl(messageboxdata, buttonid, &returnValue);
|
||||
} else {
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
UIKit_ShowMessageBoxImpl(messageboxdata, buttonid, &returnValue);
|
||||
});
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* SDL_VIDEO_DRIVER_UIKIT */
|
54
external/sdl/SDL/src/video/uikit/SDL_uikitmetalview.h
vendored
Normal file
54
external/sdl/SDL/src/video/uikit/SDL_uikitmetalview.h
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @author Mark Callow, www.edgewise-consulting.com.
|
||||
*
|
||||
* Thanks to @slime73 on GitHub for their gist showing how to add a CAMetalLayer
|
||||
* backed view.
|
||||
*/
|
||||
|
||||
#ifndef SDL_uikitmetalview_h_
|
||||
#define SDL_uikitmetalview_h_
|
||||
|
||||
#include "../SDL_sysvideo.h"
|
||||
#include "SDL_uikitwindow.h"
|
||||
|
||||
#if defined(SDL_VIDEO_DRIVER_UIKIT) && (defined(SDL_VIDEO_VULKAN) || defined(SDL_VIDEO_METAL))
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <Metal/Metal.h>
|
||||
#import <QuartzCore/CAMetalLayer.h>
|
||||
|
||||
@interface SDL_uikitmetalview : SDL_uikitview
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
scale:(CGFloat)scale;
|
||||
|
||||
@end
|
||||
|
||||
SDL_MetalView UIKit_Metal_CreateView(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
void UIKit_Metal_DestroyView(SDL_VideoDevice *_this, SDL_MetalView view);
|
||||
void *UIKit_Metal_GetLayer(SDL_VideoDevice *_this, SDL_MetalView view);
|
||||
|
||||
#endif /* SDL_VIDEO_DRIVER_UIKIT && (SDL_VIDEO_VULKAN || SDL_VIDEO_METAL) */
|
||||
|
||||
#endif /* SDL_uikitmetalview_h_ */
|
125
external/sdl/SDL/src/video/uikit/SDL_uikitmetalview.m
vendored
Normal file
125
external/sdl/SDL/src/video/uikit/SDL_uikitmetalview.m
vendored
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @author Mark Callow, www.edgewise-consulting.com.
|
||||
*
|
||||
* Thanks to @slime73 on GitHub for their gist showing how to add a CAMetalLayer
|
||||
* backed view.
|
||||
*/
|
||||
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#if defined(SDL_VIDEO_DRIVER_UIKIT) && (defined(SDL_VIDEO_VULKAN) || defined(SDL_VIDEO_METAL))
|
||||
|
||||
#include "../SDL_sysvideo.h"
|
||||
|
||||
#import "SDL_uikitwindow.h"
|
||||
#import "SDL_uikitmetalview.h"
|
||||
|
||||
#include <SDL3/SDL_syswm.h>
|
||||
|
||||
@implementation SDL_uikitmetalview
|
||||
|
||||
/* Returns a Metal-compatible layer. */
|
||||
+ (Class)layerClass
|
||||
{
|
||||
return [CAMetalLayer class];
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
scale:(CGFloat)scale
|
||||
{
|
||||
if ((self = [super initWithFrame:frame])) {
|
||||
self.tag = SDL_METALVIEW_TAG;
|
||||
self.layer.contentsScale = scale;
|
||||
[self updateDrawableSize];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/* Set the size of the metal drawables when the view is resized. */
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
[self updateDrawableSize];
|
||||
}
|
||||
|
||||
- (void)updateDrawableSize
|
||||
{
|
||||
CGSize size = self.bounds.size;
|
||||
size.width *= self.layer.contentsScale;
|
||||
size.height *= self.layer.contentsScale;
|
||||
((CAMetalLayer *)self.layer).drawableSize = size;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
SDL_MetalView UIKit_Metal_CreateView(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL_UIKitWindowData *data = (__bridge SDL_UIKitWindowData *)window->driverdata;
|
||||
CGFloat scale = 1.0;
|
||||
SDL_uikitmetalview *metalview;
|
||||
|
||||
if (window->flags & SDL_WINDOW_HIGH_PIXEL_DENSITY) {
|
||||
/* Set the scale to the natural scale factor of the screen - then
|
||||
* the backing dimensions of the Metal view will match the pixel
|
||||
* dimensions of the screen rather than the dimensions in points
|
||||
* yielding high resolution on retine displays.
|
||||
*/
|
||||
scale = data.uiwindow.screen.nativeScale;
|
||||
}
|
||||
|
||||
metalview = [[SDL_uikitmetalview alloc] initWithFrame:data.uiwindow.bounds
|
||||
scale:scale];
|
||||
if (metalview == nil) {
|
||||
SDL_OutOfMemory();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
[metalview setSDLWindow:window];
|
||||
|
||||
return (void *)CFBridgingRetain(metalview);
|
||||
}
|
||||
}
|
||||
|
||||
void UIKit_Metal_DestroyView(SDL_VideoDevice *_this, SDL_MetalView view)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL_uikitmetalview *metalview = CFBridgingRelease(view);
|
||||
|
||||
if ([metalview isKindOfClass:[SDL_uikitmetalview class]]) {
|
||||
[metalview setSDLWindow:NULL];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void *UIKit_Metal_GetLayer(SDL_VideoDevice *_this, SDL_MetalView view)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL_uikitview *uiview = (__bridge SDL_uikitview *)view;
|
||||
return (__bridge void *)uiview.layer;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* SDL_VIDEO_DRIVER_UIKIT && (SDL_VIDEO_VULKAN || SDL_VIDEO_METAL) */
|
52
external/sdl/SDL/src/video/uikit/SDL_uikitmodes.h
vendored
Normal file
52
external/sdl/SDL/src/video/uikit/SDL_uikitmodes.h
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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"
|
||||
|
||||
#ifndef SDL_uikitmodes_h_
|
||||
#define SDL_uikitmodes_h_
|
||||
|
||||
#include "SDL_uikitvideo.h"
|
||||
|
||||
@interface SDL_UIKitDisplayData : NSObject
|
||||
|
||||
- (instancetype)initWithScreen:(UIScreen *)screen;
|
||||
|
||||
@property(nonatomic, strong) UIScreen *uiscreen;
|
||||
|
||||
@end
|
||||
|
||||
@interface SDL_UIKitDisplayModeData : NSObject
|
||||
|
||||
@property(nonatomic, strong) UIScreenMode *uiscreenmode;
|
||||
|
||||
@end
|
||||
|
||||
extern SDL_bool UIKit_IsDisplayLandscape(UIScreen *uiscreen);
|
||||
|
||||
extern int UIKit_InitModes(SDL_VideoDevice *_this);
|
||||
extern int UIKit_AddDisplay(UIScreen *uiscreen, SDL_bool send_event);
|
||||
extern void UIKit_DelDisplay(UIScreen *uiscreen);
|
||||
extern int UIKit_GetDisplayModes(SDL_VideoDevice *_this, SDL_VideoDisplay *display);
|
||||
extern int UIKit_SetDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_DisplayMode *mode);
|
||||
extern void UIKit_QuitModes(SDL_VideoDevice *_this);
|
||||
extern int UIKit_GetDisplayUsableBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_Rect *rect);
|
||||
|
||||
#endif /* SDL_uikitmodes_h_ */
|
465
external/sdl/SDL/src/video/uikit/SDL_uikitmodes.m
vendored
Normal file
465
external/sdl/SDL/src/video/uikit/SDL_uikitmodes.m
vendored
Normal file
@ -0,0 +1,465 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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"
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_UIKIT
|
||||
|
||||
#include "SDL_uikitmodes.h"
|
||||
|
||||
#include "../../events/SDL_events_c.h"
|
||||
|
||||
#import <sys/utsname.h>
|
||||
|
||||
@implementation SDL_UIKitDisplayData
|
||||
|
||||
- (instancetype)initWithScreen:(UIScreen *)screen
|
||||
{
|
||||
if (self = [super init]) {
|
||||
self.uiscreen = screen;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@synthesize uiscreen;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SDL_UIKitDisplayModeData
|
||||
|
||||
@synthesize uiscreenmode;
|
||||
|
||||
@end
|
||||
|
||||
@interface SDL_DisplayWatch : NSObject
|
||||
@end
|
||||
|
||||
@implementation SDL_DisplayWatch
|
||||
|
||||
+ (void)start
|
||||
{
|
||||
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
|
||||
|
||||
[center addObserver:self
|
||||
selector:@selector(screenConnected:)
|
||||
name:UIScreenDidConnectNotification
|
||||
object:nil];
|
||||
[center addObserver:self
|
||||
selector:@selector(screenDisconnected:)
|
||||
name:UIScreenDidDisconnectNotification
|
||||
object:nil];
|
||||
}
|
||||
|
||||
+ (void)stop
|
||||
{
|
||||
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
|
||||
|
||||
[center removeObserver:self
|
||||
name:UIScreenDidConnectNotification
|
||||
object:nil];
|
||||
[center removeObserver:self
|
||||
name:UIScreenDidDisconnectNotification
|
||||
object:nil];
|
||||
}
|
||||
|
||||
+ (void)screenConnected:(NSNotification *)notification
|
||||
{
|
||||
UIScreen *uiscreen = [notification object];
|
||||
UIKit_AddDisplay(uiscreen, SDL_TRUE);
|
||||
}
|
||||
|
||||
+ (void)screenDisconnected:(NSNotification *)notification
|
||||
{
|
||||
UIScreen *uiscreen = [notification object];
|
||||
UIKit_DelDisplay(uiscreen);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static int UIKit_AllocateDisplayModeData(SDL_DisplayMode *mode,
|
||||
UIScreenMode *uiscreenmode)
|
||||
{
|
||||
SDL_UIKitDisplayModeData *data = nil;
|
||||
|
||||
if (uiscreenmode != nil) {
|
||||
/* Allocate the display mode data */
|
||||
data = [[SDL_UIKitDisplayModeData alloc] init];
|
||||
if (!data) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
data.uiscreenmode = uiscreenmode;
|
||||
}
|
||||
|
||||
mode->driverdata = (void *)CFBridgingRetain(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void UIKit_FreeDisplayModeData(SDL_DisplayMode *mode)
|
||||
{
|
||||
if (mode->driverdata != NULL) {
|
||||
CFRelease(mode->driverdata);
|
||||
mode->driverdata = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static float UIKit_GetDisplayModeRefreshRate(UIScreen *uiscreen)
|
||||
{
|
||||
#ifdef __IPHONE_10_3
|
||||
if ([uiscreen respondsToSelector:@selector(maximumFramesPerSecond)]) {
|
||||
return (float)uiscreen.maximumFramesPerSecond;
|
||||
}
|
||||
#endif
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
static int UIKit_AddSingleDisplayMode(SDL_VideoDisplay *display, int w, int h,
|
||||
UIScreen *uiscreen, UIScreenMode *uiscreenmode)
|
||||
{
|
||||
SDL_DisplayMode mode;
|
||||
|
||||
SDL_zero(mode);
|
||||
if (UIKit_AllocateDisplayModeData(&mode, uiscreenmode) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
mode.w = w;
|
||||
mode.h = h;
|
||||
mode.pixel_density = uiscreen.nativeScale;
|
||||
mode.refresh_rate = UIKit_GetDisplayModeRefreshRate(uiscreen);
|
||||
mode.format = SDL_PIXELFORMAT_ABGR8888;
|
||||
|
||||
if (SDL_AddFullscreenDisplayMode(display, &mode)) {
|
||||
return 0;
|
||||
} else {
|
||||
UIKit_FreeDisplayModeData(&mode);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int UIKit_AddDisplayMode(SDL_VideoDisplay *display, int w, int h,
|
||||
UIScreen *uiscreen, UIScreenMode *uiscreenmode, SDL_bool addRotation)
|
||||
{
|
||||
if (UIKit_AddSingleDisplayMode(display, w, h, uiscreen, uiscreenmode) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (addRotation) {
|
||||
/* Add the rotated version */
|
||||
if (UIKit_AddSingleDisplayMode(display, h, w, uiscreen, uiscreenmode) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static CGSize GetUIScreenModeSize(UIScreen *uiscreen, UIScreenMode *mode)
|
||||
{
|
||||
/* For devices such as iPhone 6/7/8 Plus, the UIScreenMode reported by iOS
|
||||
* isn't the physical pixels of the display, but rather the point size times
|
||||
* the scale. For example, on iOS 12.2 on iPhone 8 Plus the physical pixel
|
||||
* resolution is 1080x1920, the size reported by mode.size is 1242x2208,
|
||||
* the size in points is 414x736, the scale property is 3.0, and the
|
||||
* nativeScale property is ~2.6087 (ie 1920.0 / 736.0).
|
||||
*
|
||||
* What we want for the mode size is the point size, and the pixel density
|
||||
* is the native scale.
|
||||
*
|
||||
* Note that the iOS Simulator doesn't have this behavior for those devices.
|
||||
* https://github.com/libsdl-org/SDL/issues/3220
|
||||
*/
|
||||
CGSize size = mode.size;
|
||||
|
||||
size.width = SDL_round(size.width / uiscreen.scale);
|
||||
size.height = SDL_round(size.height / uiscreen.scale);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
int UIKit_AddDisplay(UIScreen *uiscreen, SDL_bool send_event)
|
||||
{
|
||||
UIScreenMode *uiscreenmode = uiscreen.currentMode;
|
||||
CGSize size = GetUIScreenModeSize(uiscreen, uiscreenmode);
|
||||
SDL_VideoDisplay display;
|
||||
SDL_DisplayMode mode;
|
||||
|
||||
/* Make sure the width/height are oriented correctly */
|
||||
if (UIKit_IsDisplayLandscape(uiscreen) != (size.width > size.height)) {
|
||||
CGFloat height = size.width;
|
||||
size.width = size.height;
|
||||
size.height = height;
|
||||
}
|
||||
|
||||
SDL_zero(mode);
|
||||
mode.w = (int)size.width;
|
||||
mode.h = (int)size.height;
|
||||
mode.pixel_density = uiscreen.nativeScale;
|
||||
mode.format = SDL_PIXELFORMAT_ABGR8888;
|
||||
mode.refresh_rate = UIKit_GetDisplayModeRefreshRate(uiscreen);
|
||||
|
||||
if (UIKit_AllocateDisplayModeData(&mode, uiscreenmode) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
SDL_zero(display);
|
||||
#if !TARGET_OS_TV
|
||||
if (uiscreen == [UIScreen mainScreen]) {
|
||||
/* The natural orientation (used by sensors) is portrait */
|
||||
display.natural_orientation = SDL_ORIENTATION_PORTRAIT;
|
||||
} else
|
||||
#endif
|
||||
if (UIKit_IsDisplayLandscape(uiscreen)) {
|
||||
display.natural_orientation = SDL_ORIENTATION_LANDSCAPE;
|
||||
} else {
|
||||
display.natural_orientation = SDL_ORIENTATION_PORTRAIT;
|
||||
}
|
||||
display.desktop_mode = mode;
|
||||
|
||||
/* Allocate the display data */
|
||||
SDL_UIKitDisplayData *data = [[SDL_UIKitDisplayData alloc] initWithScreen:uiscreen];
|
||||
if (!data) {
|
||||
UIKit_FreeDisplayModeData(&display.desktop_mode);
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
display.driverdata = (SDL_DisplayData *)CFBridgingRetain(data);
|
||||
if (SDL_AddVideoDisplay(&display, send_event) == 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void UIKit_DelDisplay(UIScreen *uiscreen)
|
||||
{
|
||||
SDL_DisplayID *displays;
|
||||
int i;
|
||||
|
||||
displays = SDL_GetDisplays(NULL);
|
||||
if (displays) {
|
||||
for (i = 0; displays[i]; ++i) {
|
||||
SDL_VideoDisplay *display = SDL_GetVideoDisplay(displays[i]);
|
||||
SDL_UIKitDisplayData *data = (__bridge SDL_UIKitDisplayData *)display->driverdata;
|
||||
|
||||
if (data && data.uiscreen == uiscreen) {
|
||||
CFRelease(display->driverdata);
|
||||
SDL_DelVideoDisplay(displays[i], SDL_FALSE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
SDL_free(displays);
|
||||
}
|
||||
}
|
||||
|
||||
SDL_bool UIKit_IsDisplayLandscape(UIScreen *uiscreen)
|
||||
{
|
||||
#if !TARGET_OS_TV
|
||||
if (uiscreen == [UIScreen mainScreen]) {
|
||||
return UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation);
|
||||
} else
|
||||
#endif /* !TARGET_OS_TV */
|
||||
{
|
||||
CGSize size = uiscreen.bounds.size;
|
||||
return (size.width > size.height);
|
||||
}
|
||||
}
|
||||
|
||||
int UIKit_InitModes(SDL_VideoDevice *_this)
|
||||
{
|
||||
@autoreleasepool {
|
||||
for (UIScreen *uiscreen in [UIScreen screens]) {
|
||||
if (UIKit_AddDisplay(uiscreen, SDL_FALSE) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#if !TARGET_OS_TV
|
||||
SDL_OnApplicationDidChangeStatusBarOrientation();
|
||||
#endif
|
||||
|
||||
[SDL_DisplayWatch start];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int UIKit_GetDisplayModes(SDL_VideoDevice *_this, SDL_VideoDisplay *display)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL_UIKitDisplayData *data = (__bridge SDL_UIKitDisplayData *)display->driverdata;
|
||||
|
||||
SDL_bool isLandscape = UIKit_IsDisplayLandscape(data.uiscreen);
|
||||
SDL_bool addRotation = (data.uiscreen == [UIScreen mainScreen]);
|
||||
NSArray *availableModes = nil;
|
||||
|
||||
#if TARGET_OS_TV
|
||||
addRotation = SDL_FALSE;
|
||||
availableModes = @[ data.uiscreen.currentMode ];
|
||||
#else
|
||||
availableModes = data.uiscreen.availableModes;
|
||||
#endif
|
||||
|
||||
for (UIScreenMode *uimode in availableModes) {
|
||||
CGSize size = GetUIScreenModeSize(data.uiscreen, uimode);
|
||||
int w = size.width;
|
||||
int h = size.height;
|
||||
|
||||
/* Make sure the width/height are oriented correctly */
|
||||
if (isLandscape != (w > h)) {
|
||||
int tmp = w;
|
||||
w = h;
|
||||
h = tmp;
|
||||
}
|
||||
|
||||
UIKit_AddDisplayMode(display, w, h, data.uiscreen, uimode, addRotation);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int UIKit_SetDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_DisplayMode *mode)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL_UIKitDisplayData *data = (__bridge SDL_UIKitDisplayData *)display->driverdata;
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
SDL_UIKitDisplayModeData *modedata = (__bridge SDL_UIKitDisplayModeData *)mode->driverdata;
|
||||
[data.uiscreen setCurrentMode:modedata.uiscreenmode];
|
||||
#endif
|
||||
|
||||
if (data.uiscreen == [UIScreen mainScreen]) {
|
||||
/* [UIApplication setStatusBarOrientation:] no longer works reliably
|
||||
* in recent iOS versions, so we can't rotate the screen when setting
|
||||
* the display mode. */
|
||||
if (mode->w > mode->h) {
|
||||
if (!UIKit_IsDisplayLandscape(data.uiscreen)) {
|
||||
return SDL_SetError("Screen orientation does not match display mode size");
|
||||
}
|
||||
} else if (mode->w < mode->h) {
|
||||
if (UIKit_IsDisplayLandscape(data.uiscreen)) {
|
||||
return SDL_SetError("Screen orientation does not match display mode size");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int UIKit_GetDisplayUsableBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_Rect *rect)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL_UIKitDisplayData *data = (__bridge SDL_UIKitDisplayData *)display->driverdata;
|
||||
CGRect frame = data.uiscreen.bounds;
|
||||
|
||||
/* the default function iterates displays to make a fake offset,
|
||||
as if all the displays were side-by-side, which is fine for iOS. */
|
||||
if (SDL_GetDisplayBounds(display->id, rect) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rect->x += frame.origin.x;
|
||||
rect->y += frame.origin.y;
|
||||
rect->w = frame.size.width;
|
||||
rect->h = frame.size.height;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void UIKit_QuitModes(SDL_VideoDevice *_this)
|
||||
{
|
||||
[SDL_DisplayWatch stop];
|
||||
|
||||
/* Release Objective-C objects, so higher level doesn't free() them. */
|
||||
int i, j;
|
||||
@autoreleasepool {
|
||||
for (i = 0; i < _this->num_displays; i++) {
|
||||
SDL_VideoDisplay *display = &_this->displays[i];
|
||||
|
||||
UIKit_FreeDisplayModeData(&display->desktop_mode);
|
||||
for (j = 0; j < display->num_fullscreen_modes; j++) {
|
||||
SDL_DisplayMode *mode = &display->fullscreen_modes[j];
|
||||
UIKit_FreeDisplayModeData(mode);
|
||||
}
|
||||
|
||||
if (display->driverdata != NULL) {
|
||||
CFRelease(display->driverdata);
|
||||
display->driverdata = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
void SDL_OnApplicationDidChangeStatusBarOrientation(void)
|
||||
{
|
||||
BOOL isLandscape = UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation);
|
||||
SDL_VideoDisplay *display = SDL_GetVideoDisplay(SDL_GetPrimaryDisplay());
|
||||
|
||||
if (display) {
|
||||
SDL_DisplayMode *mode = &display->desktop_mode;
|
||||
SDL_DisplayOrientation orientation = SDL_ORIENTATION_UNKNOWN;
|
||||
int i;
|
||||
|
||||
/* The desktop display mode should be kept in sync with the screen
|
||||
* orientation so that updating a window's fullscreen state to
|
||||
* fullscreen desktop keeps the window dimensions in the
|
||||
* correct orientation. */
|
||||
if (isLandscape != (mode->w > mode->h)) {
|
||||
int height = mode->w;
|
||||
mode->w = mode->h;
|
||||
mode->h = height;
|
||||
}
|
||||
|
||||
/* Same deal with the fullscreen modes */
|
||||
for (i = 0; i < display->num_fullscreen_modes; ++i) {
|
||||
mode = &display->fullscreen_modes[i];
|
||||
if (isLandscape != (mode->w > mode->h)) {
|
||||
int height = mode->w;
|
||||
mode->w = mode->h;
|
||||
mode->h = height;
|
||||
}
|
||||
}
|
||||
|
||||
switch ([UIApplication sharedApplication].statusBarOrientation) {
|
||||
case UIInterfaceOrientationPortrait:
|
||||
orientation = SDL_ORIENTATION_PORTRAIT;
|
||||
break;
|
||||
case UIInterfaceOrientationPortraitUpsideDown:
|
||||
orientation = SDL_ORIENTATION_PORTRAIT_FLIPPED;
|
||||
break;
|
||||
case UIInterfaceOrientationLandscapeLeft:
|
||||
/* Bug: UIInterfaceOrientationLandscapeLeft/Right are reversed - http://openradar.appspot.com/7216046 */
|
||||
orientation = SDL_ORIENTATION_LANDSCAPE_FLIPPED;
|
||||
break;
|
||||
case UIInterfaceOrientationLandscapeRight:
|
||||
/* Bug: UIInterfaceOrientationLandscapeLeft/Right are reversed - http://openradar.appspot.com/7216046 */
|
||||
orientation = SDL_ORIENTATION_LANDSCAPE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
SDL_SendDisplayEvent(display, SDL_EVENT_DISPLAY_ORIENTATION, orientation);
|
||||
}
|
||||
}
|
||||
#endif /* !TARGET_OS_TV */
|
||||
|
||||
#endif /* SDL_VIDEO_DRIVER_UIKIT */
|
40
external/sdl/SDL/src/video/uikit/SDL_uikitopengles.h
vendored
Normal file
40
external/sdl/SDL/src/video/uikit/SDL_uikitopengles.h
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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.
|
||||
*/
|
||||
#ifndef SDL_uikitopengles_
|
||||
#define SDL_uikitopengles_
|
||||
|
||||
#if defined(SDL_VIDEO_OPENGL_ES) || defined(SDL_VIDEO_OPENGL_ES2)
|
||||
|
||||
#include "../SDL_sysvideo.h"
|
||||
|
||||
extern int UIKit_GL_MakeCurrent(SDL_VideoDevice *_this, SDL_Window *window,
|
||||
SDL_GLContext context);
|
||||
extern int UIKit_GL_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern SDL_GLContext UIKit_GL_CreateContext(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern int UIKit_GL_DeleteContext(SDL_VideoDevice *_this, SDL_GLContext context);
|
||||
extern SDL_FunctionPointer UIKit_GL_GetProcAddress(SDL_VideoDevice *_this, const char *proc);
|
||||
extern int UIKit_GL_LoadLibrary(SDL_VideoDevice *_this, const char *path);
|
||||
|
||||
extern void UIKit_GL_RestoreCurrentContext(void);
|
||||
|
||||
#endif // SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2
|
||||
|
||||
#endif /* SDL_uikitopengles_ */
|
216
external/sdl/SDL/src/video/uikit/SDL_uikitopengles.m
vendored
Normal file
216
external/sdl/SDL/src/video/uikit/SDL_uikitopengles.m
vendored
Normal file
@ -0,0 +1,216 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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 defined(SDL_VIDEO_DRIVER_UIKIT) && (defined(SDL_VIDEO_OPENGL_ES) || defined(SDL_VIDEO_OPENGL_ES2))
|
||||
|
||||
#include "SDL_uikitopengles.h"
|
||||
#import "SDL_uikitopenglview.h"
|
||||
#include "SDL_uikitmodes.h"
|
||||
#include "SDL_uikitwindow.h"
|
||||
#include "SDL_uikitevents.h"
|
||||
#include "../SDL_sysvideo.h"
|
||||
#include "../../events/SDL_keyboard_c.h"
|
||||
#include "../../events/SDL_mouse_c.h"
|
||||
#include "../../power/uikit/SDL_syspower.h"
|
||||
#include <dlfcn.h>
|
||||
|
||||
@interface SDLEAGLContext : EAGLContext
|
||||
|
||||
/* The OpenGL ES context owns a view / drawable. */
|
||||
@property(nonatomic, strong) SDL_uikitopenglview *sdlView;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SDLEAGLContext
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
/* When the context is deallocated, its view should be removed from any
|
||||
* SDL window that it's attached to. */
|
||||
[self.sdlView setSDLWindow:NULL];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
SDL_FunctionPointer UIKit_GL_GetProcAddress(SDL_VideoDevice *_this, const char *proc)
|
||||
{
|
||||
/* Look through all SO's for the proc symbol. Here's why:
|
||||
* -Looking for the path to the OpenGL Library seems not to work in the iOS Simulator.
|
||||
* -We don't know that the path won't change in the future. */
|
||||
return dlsym(RTLD_DEFAULT, proc);
|
||||
}
|
||||
|
||||
/*
|
||||
note that SDL_GL_DeleteContext makes it current without passing the window
|
||||
*/
|
||||
int UIKit_GL_MakeCurrent(SDL_VideoDevice *_this, SDL_Window *window, SDL_GLContext context)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDLEAGLContext *eaglcontext = (__bridge SDLEAGLContext *)context;
|
||||
|
||||
if (![EAGLContext setCurrentContext:eaglcontext]) {
|
||||
return SDL_SetError("Could not make EAGL context current");
|
||||
}
|
||||
|
||||
if (eaglcontext) {
|
||||
[eaglcontext.sdlView setSDLWindow:window];
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int UIKit_GL_LoadLibrary(SDL_VideoDevice *_this, const char *path)
|
||||
{
|
||||
/* We shouldn't pass a path to this function, since we've already loaded the
|
||||
* library. */
|
||||
if (path != NULL) {
|
||||
return SDL_SetError("iOS GL Load Library just here for compatibility");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int UIKit_GL_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDLEAGLContext *context = (__bridge SDLEAGLContext *)SDL_GL_GetCurrentContext();
|
||||
|
||||
#ifdef SDL_POWER_UIKIT
|
||||
/* Check once a frame to see if we should turn off the battery monitor. */
|
||||
SDL_UIKit_UpdateBatteryMonitoring();
|
||||
#endif
|
||||
|
||||
[context.sdlView swapBuffers];
|
||||
|
||||
/* You need to pump events in order for the OS to make changes visible.
|
||||
* We don't pump events here because we don't want iOS application events
|
||||
* (low memory, terminate, etc.) to happen inside low level rendering. */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
SDL_GLContext UIKit_GL_CreateContext(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDLEAGLContext *context = nil;
|
||||
SDL_uikitopenglview *view;
|
||||
SDL_UIKitWindowData *data = (__bridge SDL_UIKitWindowData *)window->driverdata;
|
||||
CGRect frame = UIKit_ComputeViewFrame(window, data.uiwindow.screen);
|
||||
EAGLSharegroup *sharegroup = nil;
|
||||
CGFloat scale = 1.0;
|
||||
int samples = 0;
|
||||
int major = _this->gl_config.major_version;
|
||||
int minor = _this->gl_config.minor_version;
|
||||
|
||||
/* The EAGLRenderingAPI enum values currently map 1:1 to major GLES
|
||||
* versions. */
|
||||
EAGLRenderingAPI api = major;
|
||||
|
||||
/* iOS currently doesn't support GLES >3.0. */
|
||||
if (major > 3 || (major == 3 && minor > 0)) {
|
||||
SDL_SetError("OpenGL ES %d.%d context could not be created", major, minor);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (_this->gl_config.multisamplebuffers > 0) {
|
||||
samples = _this->gl_config.multisamplesamples;
|
||||
}
|
||||
|
||||
if (_this->gl_config.share_with_current_context) {
|
||||
EAGLContext *currContext = (__bridge EAGLContext *)SDL_GL_GetCurrentContext();
|
||||
sharegroup = currContext.sharegroup;
|
||||
}
|
||||
|
||||
if (window->flags & SDL_WINDOW_HIGH_PIXEL_DENSITY) {
|
||||
/* Set the scale to the natural scale factor of the screen - the
|
||||
* backing dimensions of the OpenGL view will match the pixel
|
||||
* dimensions of the screen rather than the dimensions in points. */
|
||||
scale = data.uiwindow.screen.nativeScale;
|
||||
}
|
||||
|
||||
context = [[SDLEAGLContext alloc] initWithAPI:api sharegroup:sharegroup];
|
||||
if (!context) {
|
||||
SDL_SetError("OpenGL ES %d context could not be created", _this->gl_config.major_version);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* construct our view, passing in SDL's OpenGL configuration data */
|
||||
view = [[SDL_uikitopenglview alloc] initWithFrame:frame
|
||||
scale:scale
|
||||
retainBacking:_this->gl_config.retained_backing
|
||||
rBits:_this->gl_config.red_size
|
||||
gBits:_this->gl_config.green_size
|
||||
bBits:_this->gl_config.blue_size
|
||||
aBits:_this->gl_config.alpha_size
|
||||
depthBits:_this->gl_config.depth_size
|
||||
stencilBits:_this->gl_config.stencil_size
|
||||
sRGB:_this->gl_config.framebuffer_srgb_capable
|
||||
multisamples:samples
|
||||
context:context];
|
||||
|
||||
if (!view) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* The context owns the view / drawable. */
|
||||
context.sdlView = view;
|
||||
|
||||
if (UIKit_GL_MakeCurrent(_this, window, (__bridge SDL_GLContext)context) < 0) {
|
||||
UIKit_GL_DeleteContext(_this, (SDL_GLContext)CFBridgingRetain(context));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* We return a +1'd context. The window's driverdata owns the view (via
|
||||
* MakeCurrent.) */
|
||||
return (SDL_GLContext)CFBridgingRetain(context);
|
||||
}
|
||||
}
|
||||
|
||||
int UIKit_GL_DeleteContext(SDL_VideoDevice *_this, SDL_GLContext context)
|
||||
{
|
||||
@autoreleasepool {
|
||||
/* The context was retained in SDL_GL_CreateContext, so we release it
|
||||
* here. The context's view will be detached from its window when the
|
||||
* context is deallocated. */
|
||||
CFRelease(context);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void UIKit_GL_RestoreCurrentContext(void)
|
||||
{
|
||||
@autoreleasepool {
|
||||
/* Some iOS system functionality (such as Dictation on the on-screen
|
||||
keyboard) uses its own OpenGL ES context but doesn't restore the
|
||||
previous one when it's done. This is a workaround to make sure the
|
||||
expected SDL-created OpenGL ES context is active after the OS is
|
||||
finished running its own code for the frame. If this isn't done, the
|
||||
app may crash or have other nasty symptoms when Dictation is used.
|
||||
*/
|
||||
EAGLContext *context = (__bridge EAGLContext *)SDL_GL_GetCurrentContext();
|
||||
if (context != NULL && [EAGLContext currentContext] != context) {
|
||||
[EAGLContext setCurrentContext:context];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* SDL_VIDEO_DRIVER_UIKIT */
|
62
external/sdl/SDL/src/video/uikit/SDL_uikitopenglview.h
vendored
Normal file
62
external/sdl/SDL/src/video/uikit/SDL_uikitopenglview.h
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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.
|
||||
*/
|
||||
|
||||
#if defined(SDL_VIDEO_OPENGL_ES) || defined(SDL_VIDEO_OPENGL_ES2)
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <OpenGLES/EAGL.h>
|
||||
#import <OpenGLES/ES3/gl.h>
|
||||
|
||||
#import "SDL_uikitview.h"
|
||||
#include "SDL_uikitvideo.h"
|
||||
|
||||
@interface SDL_uikitopenglview : SDL_uikitview
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
scale:(CGFloat)scale
|
||||
retainBacking:(BOOL)retained
|
||||
rBits:(int)rBits
|
||||
gBits:(int)gBits
|
||||
bBits:(int)bBits
|
||||
aBits:(int)aBits
|
||||
depthBits:(int)depthBits
|
||||
stencilBits:(int)stencilBits
|
||||
sRGB:(BOOL)sRGB
|
||||
multisamples:(int)multisamples
|
||||
context:(EAGLContext *)glcontext;
|
||||
|
||||
@property(nonatomic, readonly, weak) EAGLContext *context;
|
||||
|
||||
/* The width and height of the drawable in pixels (as opposed to points.) */
|
||||
@property(nonatomic, readonly) int backingWidth;
|
||||
@property(nonatomic, readonly) int backingHeight;
|
||||
|
||||
@property(nonatomic, readonly) GLuint drawableRenderbuffer;
|
||||
@property(nonatomic, readonly) GLuint drawableFramebuffer;
|
||||
@property(nonatomic, readonly) GLuint msaaResolveFramebuffer;
|
||||
|
||||
- (void)swapBuffers;
|
||||
|
||||
- (void)updateFrame;
|
||||
|
||||
@end
|
||||
|
||||
#endif // SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2
|
377
external/sdl/SDL/src/video/uikit/SDL_uikitopenglview.m
vendored
Normal file
377
external/sdl/SDL/src/video/uikit/SDL_uikitopenglview.m
vendored
Normal file
@ -0,0 +1,377 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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 defined(SDL_VIDEO_DRIVER_UIKIT) && (defined(SDL_VIDEO_OPENGL_ES) || defined(SDL_VIDEO_OPENGL_ES2))
|
||||
|
||||
#include <OpenGLES/EAGLDrawable.h>
|
||||
#include <OpenGLES/ES2/glext.h>
|
||||
#import "SDL_uikitopenglview.h"
|
||||
#include "SDL_uikitwindow.h"
|
||||
|
||||
@implementation SDL_uikitopenglview
|
||||
{
|
||||
/* The renderbuffer and framebuffer used to render to this layer. */
|
||||
GLuint viewRenderbuffer, viewFramebuffer;
|
||||
|
||||
/* The depth buffer that is attached to viewFramebuffer, if it exists. */
|
||||
GLuint depthRenderbuffer;
|
||||
|
||||
GLenum colorBufferFormat;
|
||||
|
||||
/* format of depthRenderbuffer */
|
||||
GLenum depthBufferFormat;
|
||||
|
||||
/* The framebuffer and renderbuffer used for rendering with MSAA. */
|
||||
GLuint msaaFramebuffer, msaaRenderbuffer;
|
||||
|
||||
/* The number of MSAA samples. */
|
||||
int samples;
|
||||
|
||||
BOOL retainedBacking;
|
||||
}
|
||||
|
||||
@synthesize context;
|
||||
@synthesize backingWidth;
|
||||
@synthesize backingHeight;
|
||||
|
||||
+ (Class)layerClass
|
||||
{
|
||||
return [CAEAGLLayer class];
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
scale:(CGFloat)scale
|
||||
retainBacking:(BOOL)retained
|
||||
rBits:(int)rBits
|
||||
gBits:(int)gBits
|
||||
bBits:(int)bBits
|
||||
aBits:(int)aBits
|
||||
depthBits:(int)depthBits
|
||||
stencilBits:(int)stencilBits
|
||||
sRGB:(BOOL)sRGB
|
||||
multisamples:(int)multisamples
|
||||
context:(EAGLContext *)glcontext
|
||||
{
|
||||
if ((self = [super initWithFrame:frame])) {
|
||||
const BOOL useStencilBuffer = (stencilBits != 0);
|
||||
const BOOL useDepthBuffer = (depthBits != 0);
|
||||
NSString *colorFormat = nil;
|
||||
|
||||
context = glcontext;
|
||||
samples = multisamples;
|
||||
retainedBacking = retained;
|
||||
|
||||
if (!context || ![EAGLContext setCurrentContext:context]) {
|
||||
SDL_SetError("Could not create OpenGL ES drawable (could not make context current)");
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (samples > 0) {
|
||||
GLint maxsamples = 0;
|
||||
glGetIntegerv(GL_MAX_SAMPLES, &maxsamples);
|
||||
|
||||
/* Clamp the samples to the max supported count. */
|
||||
samples = SDL_min(samples, maxsamples);
|
||||
}
|
||||
|
||||
if (sRGB) {
|
||||
colorFormat = kEAGLColorFormatSRGBA8;
|
||||
colorBufferFormat = GL_SRGB8_ALPHA8;
|
||||
} else if (rBits >= 8 || gBits >= 8 || bBits >= 8 || aBits > 0) {
|
||||
/* if user specifically requests rbg888 or some color format higher than 16bpp */
|
||||
colorFormat = kEAGLColorFormatRGBA8;
|
||||
colorBufferFormat = GL_RGBA8;
|
||||
} else {
|
||||
/* default case (potentially faster) */
|
||||
colorFormat = kEAGLColorFormatRGB565;
|
||||
colorBufferFormat = GL_RGB565;
|
||||
}
|
||||
|
||||
CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
|
||||
|
||||
eaglLayer.opaque = YES;
|
||||
eaglLayer.drawableProperties = @{
|
||||
kEAGLDrawablePropertyRetainedBacking : @(retained),
|
||||
kEAGLDrawablePropertyColorFormat : colorFormat
|
||||
};
|
||||
|
||||
/* Set the appropriate scale (for retina display support) */
|
||||
self.contentScaleFactor = scale;
|
||||
|
||||
/* Create the color Renderbuffer Object */
|
||||
glGenRenderbuffers(1, &viewRenderbuffer);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
|
||||
|
||||
if (![context renderbufferStorage:GL_RENDERBUFFER fromDrawable:eaglLayer]) {
|
||||
SDL_SetError("Failed to create OpenGL ES drawable");
|
||||
return nil;
|
||||
}
|
||||
|
||||
/* Create the Framebuffer Object */
|
||||
glGenFramebuffers(1, &viewFramebuffer);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, viewFramebuffer);
|
||||
|
||||
/* attach the color renderbuffer to the FBO */
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, viewRenderbuffer);
|
||||
|
||||
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);
|
||||
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);
|
||||
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||
SDL_SetError("Failed creating OpenGL ES framebuffer");
|
||||
return nil;
|
||||
}
|
||||
|
||||
/* When MSAA is used we'll use a separate framebuffer for rendering to,
|
||||
* since we'll need to do an explicit MSAA resolve before presenting. */
|
||||
if (samples > 0) {
|
||||
glGenFramebuffers(1, &msaaFramebuffer);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, msaaFramebuffer);
|
||||
|
||||
glGenRenderbuffers(1, &msaaRenderbuffer);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, msaaRenderbuffer);
|
||||
|
||||
glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, colorBufferFormat, backingWidth, backingHeight);
|
||||
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, msaaRenderbuffer);
|
||||
}
|
||||
|
||||
if (useDepthBuffer || useStencilBuffer) {
|
||||
if (useStencilBuffer) {
|
||||
/* Apparently you need to pack stencil and depth into one buffer. */
|
||||
depthBufferFormat = GL_DEPTH24_STENCIL8_OES;
|
||||
} else if (useDepthBuffer) {
|
||||
/* iOS only uses 32-bit float (exposed as fixed point 24-bit)
|
||||
* depth buffers. */
|
||||
depthBufferFormat = GL_DEPTH_COMPONENT24_OES;
|
||||
}
|
||||
|
||||
glGenRenderbuffers(1, &depthRenderbuffer);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);
|
||||
|
||||
if (samples > 0) {
|
||||
glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, depthBufferFormat, backingWidth, backingHeight);
|
||||
} else {
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, depthBufferFormat, backingWidth, backingHeight);
|
||||
}
|
||||
|
||||
if (useDepthBuffer) {
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);
|
||||
}
|
||||
if (useStencilBuffer) {
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);
|
||||
}
|
||||
}
|
||||
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||
SDL_SetError("Failed creating OpenGL ES framebuffer");
|
||||
return nil;
|
||||
}
|
||||
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
|
||||
|
||||
[self setDebugLabels];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (GLuint)drawableRenderbuffer
|
||||
{
|
||||
return viewRenderbuffer;
|
||||
}
|
||||
|
||||
- (GLuint)drawableFramebuffer
|
||||
{
|
||||
/* When MSAA is used, the MSAA draw framebuffer is used for drawing. */
|
||||
if (msaaFramebuffer) {
|
||||
return msaaFramebuffer;
|
||||
} else {
|
||||
return viewFramebuffer;
|
||||
}
|
||||
}
|
||||
|
||||
- (GLuint)msaaResolveFramebuffer
|
||||
{
|
||||
/* When MSAA is used, the MSAA draw framebuffer is used for drawing and the
|
||||
* view framebuffer is used as a MSAA resolve framebuffer. */
|
||||
if (msaaFramebuffer) {
|
||||
return viewFramebuffer;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateFrame
|
||||
{
|
||||
GLint prevRenderbuffer = 0;
|
||||
glGetIntegerv(GL_RENDERBUFFER_BINDING, &prevRenderbuffer);
|
||||
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
|
||||
[context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer];
|
||||
|
||||
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);
|
||||
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);
|
||||
|
||||
if (msaaRenderbuffer != 0) {
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, msaaRenderbuffer);
|
||||
glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, colorBufferFormat, backingWidth, backingHeight);
|
||||
}
|
||||
|
||||
if (depthRenderbuffer != 0) {
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);
|
||||
|
||||
if (samples > 0) {
|
||||
glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, depthBufferFormat, backingWidth, backingHeight);
|
||||
} else {
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, depthBufferFormat, backingWidth, backingHeight);
|
||||
}
|
||||
}
|
||||
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, prevRenderbuffer);
|
||||
}
|
||||
|
||||
- (void)setDebugLabels
|
||||
{
|
||||
if (viewFramebuffer != 0) {
|
||||
glLabelObjectEXT(GL_FRAMEBUFFER, viewFramebuffer, 0, "context FBO");
|
||||
}
|
||||
|
||||
if (viewRenderbuffer != 0) {
|
||||
glLabelObjectEXT(GL_RENDERBUFFER, viewRenderbuffer, 0, "context color buffer");
|
||||
}
|
||||
|
||||
if (depthRenderbuffer != 0) {
|
||||
if (depthBufferFormat == GL_DEPTH24_STENCIL8_OES) {
|
||||
glLabelObjectEXT(GL_RENDERBUFFER, depthRenderbuffer, 0, "context depth-stencil buffer");
|
||||
} else {
|
||||
glLabelObjectEXT(GL_RENDERBUFFER, depthRenderbuffer, 0, "context depth buffer");
|
||||
}
|
||||
}
|
||||
|
||||
if (msaaFramebuffer != 0) {
|
||||
glLabelObjectEXT(GL_FRAMEBUFFER, msaaFramebuffer, 0, "context MSAA FBO");
|
||||
}
|
||||
|
||||
if (msaaRenderbuffer != 0) {
|
||||
glLabelObjectEXT(GL_RENDERBUFFER, msaaRenderbuffer, 0, "context MSAA renderbuffer");
|
||||
}
|
||||
}
|
||||
|
||||
- (void)swapBuffers
|
||||
{
|
||||
if (msaaFramebuffer) {
|
||||
const GLenum attachments[] = { GL_COLOR_ATTACHMENT0 };
|
||||
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, viewFramebuffer);
|
||||
|
||||
/* OpenGL ES 3+ provides explicit MSAA resolves via glBlitFramebuffer.
|
||||
* In OpenGL ES 1 and 2, MSAA resolves must be done via an extension. */
|
||||
if (context.API >= kEAGLRenderingAPIOpenGLES3) {
|
||||
int w = backingWidth;
|
||||
int h = backingHeight;
|
||||
glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
|
||||
if (!retainedBacking) {
|
||||
/* Discard the contents of the MSAA drawable color buffer. */
|
||||
glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, 1, attachments);
|
||||
}
|
||||
} else {
|
||||
glResolveMultisampleFramebufferAPPLE();
|
||||
|
||||
if (!retainedBacking) {
|
||||
glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER, 1, attachments);
|
||||
}
|
||||
}
|
||||
|
||||
/* We assume the "drawable framebuffer" (MSAA draw framebuffer) was
|
||||
* previously bound... */
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, msaaFramebuffer);
|
||||
}
|
||||
|
||||
/* viewRenderbuffer should always be bound here. Code that binds something
|
||||
* else is responsible for rebinding viewRenderbuffer, to reduce duplicate
|
||||
* state changes. */
|
||||
[context presentRenderbuffer:GL_RENDERBUFFER];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
|
||||
int width = (int)(self.bounds.size.width * self.contentScaleFactor);
|
||||
int height = (int)(self.bounds.size.height * self.contentScaleFactor);
|
||||
|
||||
/* Update the color and depth buffer storage if the layer size has changed. */
|
||||
if (width != backingWidth || height != backingHeight) {
|
||||
EAGLContext *prevContext = [EAGLContext currentContext];
|
||||
if (prevContext != context) {
|
||||
[EAGLContext setCurrentContext:context];
|
||||
}
|
||||
|
||||
[self updateFrame];
|
||||
|
||||
if (prevContext != context) {
|
||||
[EAGLContext setCurrentContext:prevContext];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)destroyFramebuffer
|
||||
{
|
||||
if (viewFramebuffer != 0) {
|
||||
glDeleteFramebuffers(1, &viewFramebuffer);
|
||||
viewFramebuffer = 0;
|
||||
}
|
||||
|
||||
if (viewRenderbuffer != 0) {
|
||||
glDeleteRenderbuffers(1, &viewRenderbuffer);
|
||||
viewRenderbuffer = 0;
|
||||
}
|
||||
|
||||
if (depthRenderbuffer != 0) {
|
||||
glDeleteRenderbuffers(1, &depthRenderbuffer);
|
||||
depthRenderbuffer = 0;
|
||||
}
|
||||
|
||||
if (msaaFramebuffer != 0) {
|
||||
glDeleteFramebuffers(1, &msaaFramebuffer);
|
||||
msaaFramebuffer = 0;
|
||||
}
|
||||
|
||||
if (msaaRenderbuffer != 0) {
|
||||
glDeleteRenderbuffers(1, &msaaRenderbuffer);
|
||||
msaaRenderbuffer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
if (context && context == [EAGLContext currentContext]) {
|
||||
[self destroyFramebuffer];
|
||||
[EAGLContext setCurrentContext:nil];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SDL_VIDEO_DRIVER_UIKIT */
|
48
external/sdl/SDL/src/video/uikit/SDL_uikitvideo.h
vendored
Normal file
48
external/sdl/SDL/src/video/uikit/SDL_uikitvideo.h
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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.
|
||||
*/
|
||||
#ifndef SDL_uikitvideo_h_
|
||||
#define SDL_uikitvideo_h_
|
||||
|
||||
#include "../SDL_sysvideo.h"
|
||||
|
||||
#ifdef __OBJC__
|
||||
|
||||
#include <UIKit/UIKit.h>
|
||||
|
||||
@interface SDL_UIKitVideoData : NSObject
|
||||
|
||||
@property(nonatomic, assign) id pasteboardObserver;
|
||||
|
||||
@end
|
||||
|
||||
CGRect UIKit_ComputeViewFrame(SDL_Window *window, UIScreen *screen);
|
||||
|
||||
#endif /* __OBJC__ */
|
||||
|
||||
int UIKit_SuspendScreenSaver(SDL_VideoDevice *_this);
|
||||
|
||||
void UIKit_ForceUpdateHomeIndicator(void);
|
||||
|
||||
SDL_bool UIKit_IsSystemVersionAtLeast(double version);
|
||||
|
||||
SDL_SystemTheme UIKit_GetSystemTheme(void);
|
||||
|
||||
#endif /* SDL_uikitvideo_h_ */
|
294
external/sdl/SDL/src/video/uikit/SDL_uikitvideo.m
vendored
Normal file
294
external/sdl/SDL/src/video/uikit/SDL_uikitvideo.m
vendored
Normal file
@ -0,0 +1,294 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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"
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_UIKIT
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#include "../SDL_sysvideo.h"
|
||||
#include "../SDL_pixels_c.h"
|
||||
#include "../../events/SDL_events_c.h"
|
||||
|
||||
#include "SDL_uikitvideo.h"
|
||||
#include "SDL_uikitevents.h"
|
||||
#include "SDL_uikitmodes.h"
|
||||
#include "SDL_uikitwindow.h"
|
||||
#include "SDL_uikitopengles.h"
|
||||
#include "SDL_uikitclipboard.h"
|
||||
#include "SDL_uikitvulkan.h"
|
||||
#include "SDL_uikitmetalview.h"
|
||||
|
||||
#define UIKITVID_DRIVER_NAME "uikit"
|
||||
|
||||
@implementation SDL_UIKitVideoData
|
||||
|
||||
@end
|
||||
|
||||
/* Initialization/Query functions */
|
||||
static int UIKit_VideoInit(SDL_VideoDevice *_this);
|
||||
static void UIKit_VideoQuit(SDL_VideoDevice *_this);
|
||||
|
||||
/* DUMMY driver bootstrap functions */
|
||||
|
||||
static void UIKit_DeleteDevice(SDL_VideoDevice *device)
|
||||
{
|
||||
@autoreleasepool {
|
||||
CFRelease(device->driverdata);
|
||||
SDL_free(device);
|
||||
}
|
||||
}
|
||||
|
||||
static SDL_VideoDevice *UIKit_CreateDevice(void)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL_VideoDevice *device;
|
||||
SDL_UIKitVideoData *data;
|
||||
|
||||
/* Initialize all variables that we clean on shutdown */
|
||||
device = (SDL_VideoDevice *)SDL_calloc(1, sizeof(SDL_VideoDevice));
|
||||
if (device) {
|
||||
data = [SDL_UIKitVideoData new];
|
||||
} else {
|
||||
SDL_free(device);
|
||||
SDL_OutOfMemory();
|
||||
return (0);
|
||||
}
|
||||
|
||||
device->driverdata = (SDL_VideoData *)CFBridgingRetain(data);
|
||||
device->system_theme = UIKit_GetSystemTheme();
|
||||
|
||||
/* Set the function pointers */
|
||||
device->VideoInit = UIKit_VideoInit;
|
||||
device->VideoQuit = UIKit_VideoQuit;
|
||||
device->GetDisplayModes = UIKit_GetDisplayModes;
|
||||
device->SetDisplayMode = UIKit_SetDisplayMode;
|
||||
device->PumpEvents = UIKit_PumpEvents;
|
||||
device->SuspendScreenSaver = UIKit_SuspendScreenSaver;
|
||||
device->CreateSDLWindow = UIKit_CreateWindow;
|
||||
device->SetWindowTitle = UIKit_SetWindowTitle;
|
||||
device->ShowWindow = UIKit_ShowWindow;
|
||||
device->HideWindow = UIKit_HideWindow;
|
||||
device->RaiseWindow = UIKit_RaiseWindow;
|
||||
device->SetWindowBordered = UIKit_SetWindowBordered;
|
||||
device->SetWindowFullscreen = UIKit_SetWindowFullscreen;
|
||||
device->SetWindowMouseGrab = UIKit_SetWindowMouseGrab;
|
||||
device->DestroyWindow = UIKit_DestroyWindow;
|
||||
device->GetWindowWMInfo = UIKit_GetWindowWMInfo;
|
||||
device->GetDisplayUsableBounds = UIKit_GetDisplayUsableBounds;
|
||||
device->GetWindowSizeInPixels = UIKit_GetWindowSizeInPixels;
|
||||
|
||||
#ifdef SDL_IPHONE_KEYBOARD
|
||||
device->HasScreenKeyboardSupport = UIKit_HasScreenKeyboardSupport;
|
||||
device->ShowScreenKeyboard = UIKit_ShowScreenKeyboard;
|
||||
device->HideScreenKeyboard = UIKit_HideScreenKeyboard;
|
||||
device->IsScreenKeyboardShown = UIKit_IsScreenKeyboardShown;
|
||||
device->SetTextInputRect = UIKit_SetTextInputRect;
|
||||
#endif
|
||||
|
||||
device->SetClipboardText = UIKit_SetClipboardText;
|
||||
device->GetClipboardText = UIKit_GetClipboardText;
|
||||
device->HasClipboardText = UIKit_HasClipboardText;
|
||||
|
||||
/* OpenGL (ES) functions */
|
||||
#if defined(SDL_VIDEO_OPENGL_ES) || defined(SDL_VIDEO_OPENGL_ES2)
|
||||
device->GL_MakeCurrent = UIKit_GL_MakeCurrent;
|
||||
device->GL_SwapWindow = UIKit_GL_SwapWindow;
|
||||
device->GL_CreateContext = UIKit_GL_CreateContext;
|
||||
device->GL_DeleteContext = UIKit_GL_DeleteContext;
|
||||
device->GL_GetProcAddress = UIKit_GL_GetProcAddress;
|
||||
device->GL_LoadLibrary = UIKit_GL_LoadLibrary;
|
||||
#endif
|
||||
device->free = UIKit_DeleteDevice;
|
||||
|
||||
#ifdef SDL_VIDEO_VULKAN
|
||||
device->Vulkan_LoadLibrary = UIKit_Vulkan_LoadLibrary;
|
||||
device->Vulkan_UnloadLibrary = UIKit_Vulkan_UnloadLibrary;
|
||||
device->Vulkan_GetInstanceExtensions = UIKit_Vulkan_GetInstanceExtensions;
|
||||
device->Vulkan_CreateSurface = UIKit_Vulkan_CreateSurface;
|
||||
#endif
|
||||
|
||||
#ifdef SDL_VIDEO_METAL
|
||||
device->Metal_CreateView = UIKit_Metal_CreateView;
|
||||
device->Metal_DestroyView = UIKit_Metal_DestroyView;
|
||||
device->Metal_GetLayer = UIKit_Metal_GetLayer;
|
||||
#endif
|
||||
|
||||
device->gl_config.accelerated = 1;
|
||||
|
||||
return device;
|
||||
}
|
||||
}
|
||||
|
||||
VideoBootStrap UIKIT_bootstrap = {
|
||||
UIKITVID_DRIVER_NAME, "SDL UIKit video driver",
|
||||
UIKit_CreateDevice
|
||||
};
|
||||
|
||||
int UIKit_VideoInit(SDL_VideoDevice *_this)
|
||||
{
|
||||
_this->gl_config.driver_loaded = 1;
|
||||
|
||||
if (UIKit_InitModes(_this) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
SDL_InitGCKeyboard();
|
||||
SDL_InitGCMouse();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void UIKit_VideoQuit(SDL_VideoDevice *_this)
|
||||
{
|
||||
SDL_QuitGCKeyboard();
|
||||
SDL_QuitGCMouse();
|
||||
|
||||
UIKit_QuitModes(_this);
|
||||
}
|
||||
|
||||
int UIKit_SuspendScreenSaver(SDL_VideoDevice *_this)
|
||||
{
|
||||
@autoreleasepool {
|
||||
UIApplication *app = [UIApplication sharedApplication];
|
||||
|
||||
/* Prevent the display from dimming and going to sleep. */
|
||||
app.idleTimerDisabled = (_this->suspend_screensaver != SDL_FALSE);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
SDL_bool UIKit_IsSystemVersionAtLeast(double version)
|
||||
{
|
||||
return [[UIDevice currentDevice].systemVersion doubleValue] >= version;
|
||||
}
|
||||
|
||||
SDL_SystemTheme UIKit_GetSystemTheme(void)
|
||||
{
|
||||
if (@available(iOS 12.0, tvOS 10.0, *)) {
|
||||
switch ([UIScreen mainScreen].traitCollection.userInterfaceStyle) {
|
||||
case UIUserInterfaceStyleDark:
|
||||
return SDL_SYSTEM_THEME_DARK;
|
||||
case UIUserInterfaceStyleLight:
|
||||
return SDL_SYSTEM_THEME_LIGHT;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return SDL_SYSTEM_THEME_UNKNOWN;
|
||||
}
|
||||
|
||||
CGRect UIKit_ComputeViewFrame(SDL_Window *window, UIScreen *screen)
|
||||
{
|
||||
SDL_UIKitWindowData *data = (__bridge SDL_UIKitWindowData *)window->driverdata;
|
||||
CGRect frame = screen.bounds;
|
||||
|
||||
/* Use the UIWindow bounds instead of the UIScreen bounds, when possible.
|
||||
* The uiwindow bounds may be smaller than the screen bounds when Split View
|
||||
* is used on an iPad. */
|
||||
if (data != nil && data.uiwindow != nil) {
|
||||
frame = data.uiwindow.bounds;
|
||||
}
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
/* iOS 10 seems to have a bug where, in certain conditions, putting the
|
||||
* device to sleep with the a landscape-only app open, re-orienting the
|
||||
* device to portrait, and turning it back on will result in the screen
|
||||
* bounds returning portrait orientation despite the app being in landscape.
|
||||
* This is a workaround until a better solution can be found.
|
||||
* https://bugzilla.libsdl.org/show_bug.cgi?id=3505
|
||||
* https://bugzilla.libsdl.org/show_bug.cgi?id=3465
|
||||
* https://forums.developer.apple.com/thread/65337 */
|
||||
UIInterfaceOrientation orient = [UIApplication sharedApplication].statusBarOrientation;
|
||||
BOOL landscape = UIInterfaceOrientationIsLandscape(orient) ||
|
||||
!(UIKit_GetSupportedOrientations(window) & (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown));
|
||||
BOOL fullscreen = CGRectEqualToRect(screen.bounds, frame);
|
||||
|
||||
/* The orientation flip doesn't make sense when the window is smaller
|
||||
* than the screen (iPad Split View, for example). */
|
||||
if (fullscreen && (landscape != (frame.size.width > frame.size.height))) {
|
||||
float height = frame.size.width;
|
||||
frame.size.width = frame.size.height;
|
||||
frame.size.height = height;
|
||||
}
|
||||
#endif
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
void UIKit_ForceUpdateHomeIndicator(void)
|
||||
{
|
||||
#if !TARGET_OS_TV
|
||||
/* Force the main SDL window to re-evaluate home indicator state */
|
||||
SDL_Window *focus = SDL_GetFocusWindow();
|
||||
if (focus) {
|
||||
SDL_UIKitWindowData *data = (__bridge SDL_UIKitWindowData *)focus->driverdata;
|
||||
if (data != nil) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunguarded-availability-new"
|
||||
if ([data.viewcontroller respondsToSelector:@selector(setNeedsUpdateOfHomeIndicatorAutoHidden)]) {
|
||||
[data.viewcontroller performSelectorOnMainThread:@selector(setNeedsUpdateOfHomeIndicatorAutoHidden) withObject:nil waitUntilDone:NO];
|
||||
[data.viewcontroller performSelectorOnMainThread:@selector(setNeedsUpdateOfScreenEdgesDeferringSystemGestures) withObject:nil waitUntilDone:NO];
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
}
|
||||
#endif /* !TARGET_OS_TV */
|
||||
}
|
||||
|
||||
/*
|
||||
* iOS log support.
|
||||
*
|
||||
* This doesn't really have anything to do with the interfaces of the SDL video
|
||||
* subsystem, but we need to stuff this into an Objective-C source code file.
|
||||
*
|
||||
* NOTE: This is copypasted from src/video/cocoa/SDL_cocoavideo.m! Thus, if
|
||||
* Cocoa is supported, we use that one instead. Be sure both versions remain
|
||||
* identical!
|
||||
*/
|
||||
|
||||
#ifndef SDL_VIDEO_DRIVER_COCOA
|
||||
void SDL_NSLog(const char *prefix, const char *text)
|
||||
{
|
||||
@autoreleasepool {
|
||||
NSString *nsText = [NSString stringWithUTF8String:text];
|
||||
if (prefix) {
|
||||
NSString *nsPrefix = [NSString stringWithUTF8String:prefix];
|
||||
NSLog(@"%@: %@", nsPrefix, nsText);
|
||||
} else {
|
||||
NSLog(@"%@", nsText);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* SDL_VIDEO_DRIVER_COCOA */
|
||||
|
||||
/*
|
||||
* iOS Tablet detection
|
||||
*
|
||||
* This doesn't really have anything to do with the interfaces of the SDL video
|
||||
* subsystem, but we need to stuff this into an Objective-C source code file.
|
||||
*/
|
||||
SDL_bool SDL_IsIPad(void)
|
||||
{
|
||||
return ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad);
|
||||
}
|
||||
|
||||
#endif /* SDL_VIDEO_DRIVER_UIKIT */
|
46
external/sdl/SDL/src/video/uikit/SDL_uikitview.h
vendored
Normal file
46
external/sdl/SDL/src/video/uikit/SDL_uikitview.h
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#include "../SDL_sysvideo.h"
|
||||
|
||||
#if !TARGET_OS_TV && defined(__IPHONE_13_4)
|
||||
@interface SDL_uikitview : UIView <UIPointerInteractionDelegate>
|
||||
#else
|
||||
@interface SDL_uikitview : UIView
|
||||
#endif
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame;
|
||||
|
||||
- (void)setSDLWindow:(SDL_Window *)window;
|
||||
|
||||
#if !TARGET_OS_TV && defined(__IPHONE_13_4)
|
||||
- (UIPointerRegion *)pointerInteraction:(UIPointerInteraction *)interaction regionForRequest:(UIPointerRegionRequest *)request defaultRegion:(UIPointerRegion *)defaultRegion API_AVAILABLE(ios(13.4));
|
||||
- (UIPointerStyle *)pointerInteraction:(UIPointerInteraction *)interaction styleForRegion:(UIPointerRegion *)region API_AVAILABLE(ios(13.4));
|
||||
#endif
|
||||
|
||||
- (CGPoint)touchLocation:(UITouch *)touch shouldNormalize:(BOOL)normalize;
|
||||
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
|
||||
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
|
||||
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
|
||||
|
||||
@end
|
480
external/sdl/SDL/src/video/uikit/SDL_uikitview.m
vendored
Normal file
480
external/sdl/SDL/src/video/uikit/SDL_uikitview.m
vendored
Normal file
@ -0,0 +1,480 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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"
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_UIKIT
|
||||
|
||||
#include "SDL_uikitview.h"
|
||||
|
||||
#include "../../events/SDL_mouse_c.h"
|
||||
#include "../../events/SDL_touch_c.h"
|
||||
#include "../../events/SDL_events_c.h"
|
||||
|
||||
#include "SDL_uikitappdelegate.h"
|
||||
#include "SDL_uikitevents.h"
|
||||
#include "SDL_uikitmodes.h"
|
||||
#include "SDL_uikitwindow.h"
|
||||
|
||||
/* The maximum number of mouse buttons we support */
|
||||
#define MAX_MOUSE_BUTTONS 5
|
||||
|
||||
/* This is defined in SDL_sysjoystick.m */
|
||||
#ifndef SDL_JOYSTICK_DISABLED
|
||||
extern int SDL_AppleTVRemoteOpenedAsJoystick;
|
||||
#endif
|
||||
|
||||
@implementation SDL_uikitview
|
||||
{
|
||||
SDL_Window *sdlwindow;
|
||||
|
||||
SDL_TouchID directTouchId;
|
||||
SDL_TouchID indirectTouchId;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
if ((self = [super initWithFrame:frame])) {
|
||||
#if TARGET_OS_TV
|
||||
/* Apple TV Remote touchpad swipe gestures. */
|
||||
UISwipeGestureRecognizer *swipeUp = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGesture:)];
|
||||
swipeUp.direction = UISwipeGestureRecognizerDirectionUp;
|
||||
[self addGestureRecognizer:swipeUp];
|
||||
|
||||
UISwipeGestureRecognizer *swipeDown = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGesture:)];
|
||||
swipeDown.direction = UISwipeGestureRecognizerDirectionDown;
|
||||
[self addGestureRecognizer:swipeDown];
|
||||
|
||||
UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGesture:)];
|
||||
swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
|
||||
[self addGestureRecognizer:swipeLeft];
|
||||
|
||||
UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGesture:)];
|
||||
swipeRight.direction = UISwipeGestureRecognizerDirectionRight;
|
||||
[self addGestureRecognizer:swipeRight];
|
||||
#endif
|
||||
|
||||
self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
self.autoresizesSubviews = YES;
|
||||
|
||||
directTouchId = 1;
|
||||
indirectTouchId = 2;
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
self.multipleTouchEnabled = YES;
|
||||
SDL_AddTouch(directTouchId, SDL_TOUCH_DEVICE_DIRECT, "");
|
||||
#endif
|
||||
|
||||
#if !TARGET_OS_TV && defined(__IPHONE_13_4)
|
||||
if (@available(iOS 13.4, *)) {
|
||||
[self addInteraction:[[UIPointerInteraction alloc] initWithDelegate:self]];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setSDLWindow:(SDL_Window *)window
|
||||
{
|
||||
SDL_UIKitWindowData *data = nil;
|
||||
|
||||
if (window == sdlwindow) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Remove ourself from the old window. */
|
||||
if (sdlwindow) {
|
||||
SDL_uikitview *view = nil;
|
||||
data = (__bridge SDL_UIKitWindowData *)sdlwindow->driverdata;
|
||||
|
||||
[data.views removeObject:self];
|
||||
|
||||
[self removeFromSuperview];
|
||||
|
||||
/* Restore the next-oldest view in the old window. */
|
||||
view = data.views.lastObject;
|
||||
|
||||
data.viewcontroller.view = view;
|
||||
|
||||
data.uiwindow.rootViewController = nil;
|
||||
data.uiwindow.rootViewController = data.viewcontroller;
|
||||
|
||||
[data.uiwindow layoutIfNeeded];
|
||||
}
|
||||
|
||||
/* Add ourself to the new window. */
|
||||
if (window) {
|
||||
data = (__bridge SDL_UIKitWindowData *)window->driverdata;
|
||||
|
||||
/* Make sure the SDL window has a strong reference to this view. */
|
||||
[data.views addObject:self];
|
||||
|
||||
/* Replace the view controller's old view with this one. */
|
||||
[data.viewcontroller.view removeFromSuperview];
|
||||
data.viewcontroller.view = self;
|
||||
|
||||
/* The root view controller handles rotation and the status bar.
|
||||
* Assigning it also adds the controller's view to the window. We
|
||||
* explicitly re-set it to make sure the view is properly attached to
|
||||
* the window. Just adding the sub-view if the root view controller is
|
||||
* already correct causes orientation issues on iOS 7 and below. */
|
||||
data.uiwindow.rootViewController = nil;
|
||||
data.uiwindow.rootViewController = data.viewcontroller;
|
||||
|
||||
/* The view's bounds may not be correct until the next event cycle. That
|
||||
* might happen after the current dimensions are queried, so we force a
|
||||
* layout now to immediately update the bounds. */
|
||||
[data.uiwindow layoutIfNeeded];
|
||||
}
|
||||
|
||||
sdlwindow = window;
|
||||
}
|
||||
|
||||
#if !TARGET_OS_TV && defined(__IPHONE_13_4)
|
||||
- (UIPointerRegion *)pointerInteraction:(UIPointerInteraction *)interaction regionForRequest:(UIPointerRegionRequest *)request defaultRegion:(UIPointerRegion *)defaultRegion API_AVAILABLE(ios(13.4))
|
||||
{
|
||||
if (request != nil && !SDL_GCMouseRelativeMode()) {
|
||||
CGPoint origin = self.bounds.origin;
|
||||
CGPoint point = request.location;
|
||||
|
||||
point.x -= origin.x;
|
||||
point.y -= origin.y;
|
||||
|
||||
SDL_SendMouseMotion(0, sdlwindow, 0, 0, point.x, point.y);
|
||||
}
|
||||
return [UIPointerRegion regionWithRect:self.bounds identifier:nil];
|
||||
}
|
||||
|
||||
- (UIPointerStyle *)pointerInteraction:(UIPointerInteraction *)interaction styleForRegion:(UIPointerRegion *)region API_AVAILABLE(ios(13.4))
|
||||
{
|
||||
if (SDL_CursorVisible()) {
|
||||
return nil;
|
||||
} else {
|
||||
return [UIPointerStyle hiddenPointerStyle];
|
||||
}
|
||||
}
|
||||
#endif /* !TARGET_OS_TV && __IPHONE_13_4 */
|
||||
|
||||
- (SDL_TouchDeviceType)touchTypeForTouch:(UITouch *)touch
|
||||
{
|
||||
#ifdef __IPHONE_9_0
|
||||
if ([touch respondsToSelector:@selector((type))]) {
|
||||
if (touch.type == UITouchTypeIndirect) {
|
||||
return SDL_TOUCH_DEVICE_INDIRECT_RELATIVE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return SDL_TOUCH_DEVICE_DIRECT;
|
||||
}
|
||||
|
||||
- (SDL_TouchID)touchIdForType:(SDL_TouchDeviceType)type
|
||||
{
|
||||
switch (type) {
|
||||
case SDL_TOUCH_DEVICE_DIRECT:
|
||||
default:
|
||||
return directTouchId;
|
||||
case SDL_TOUCH_DEVICE_INDIRECT_RELATIVE:
|
||||
return indirectTouchId;
|
||||
}
|
||||
}
|
||||
|
||||
- (CGPoint)touchLocation:(UITouch *)touch shouldNormalize:(BOOL)normalize
|
||||
{
|
||||
CGPoint point = [touch locationInView:self];
|
||||
|
||||
if (normalize) {
|
||||
CGRect bounds = self.bounds;
|
||||
point.x /= bounds.size.width;
|
||||
point.y /= bounds.size.height;
|
||||
}
|
||||
|
||||
return point;
|
||||
}
|
||||
|
||||
- (float)pressureForTouch:(UITouch *)touch
|
||||
{
|
||||
#ifdef __IPHONE_9_0
|
||||
if ([touch respondsToSelector:@selector(force)]) {
|
||||
return (float)touch.force;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
for (UITouch *touch in touches) {
|
||||
BOOL handled = NO;
|
||||
|
||||
#if !TARGET_OS_TV && defined(__IPHONE_13_4)
|
||||
if (@available(iOS 13.4, *)) {
|
||||
if (touch.type == UITouchTypeIndirectPointer) {
|
||||
if (!SDL_HasGCMouse()) {
|
||||
int i;
|
||||
|
||||
for (i = 1; i <= MAX_MOUSE_BUTTONS; ++i) {
|
||||
if (event.buttonMask & SDL_BUTTON(i)) {
|
||||
Uint8 button;
|
||||
|
||||
switch (i) {
|
||||
case 1:
|
||||
button = SDL_BUTTON_LEFT;
|
||||
break;
|
||||
case 2:
|
||||
button = SDL_BUTTON_RIGHT;
|
||||
break;
|
||||
case 3:
|
||||
button = SDL_BUTTON_MIDDLE;
|
||||
break;
|
||||
default:
|
||||
button = (Uint8)i;
|
||||
break;
|
||||
}
|
||||
SDL_SendMouseButton(UIKit_GetEventTimestamp([event timestamp]), sdlwindow, 0, SDL_PRESSED, button);
|
||||
}
|
||||
}
|
||||
}
|
||||
handled = YES;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (!handled) {
|
||||
SDL_TouchDeviceType touchType = [self touchTypeForTouch:touch];
|
||||
SDL_TouchID touchId = [self touchIdForType:touchType];
|
||||
float pressure = [self pressureForTouch:touch];
|
||||
|
||||
if (SDL_AddTouch(touchId, touchType, "") < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* FIXME, need to send: int clicks = (int) touch.tapCount; ? */
|
||||
|
||||
CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
|
||||
SDL_SendTouch(UIKit_GetEventTimestamp([event timestamp]),
|
||||
touchId, (SDL_FingerID)((size_t)touch), sdlwindow,
|
||||
SDL_TRUE, locationInView.x, locationInView.y, pressure);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
for (UITouch *touch in touches) {
|
||||
BOOL handled = NO;
|
||||
|
||||
#if !TARGET_OS_TV && defined(__IPHONE_13_4)
|
||||
if (@available(iOS 13.4, *)) {
|
||||
if (touch.type == UITouchTypeIndirectPointer) {
|
||||
if (!SDL_HasGCMouse()) {
|
||||
int i;
|
||||
|
||||
for (i = 1; i <= MAX_MOUSE_BUTTONS; ++i) {
|
||||
if (event.buttonMask & SDL_BUTTON(i)) {
|
||||
Uint8 button;
|
||||
|
||||
switch (i) {
|
||||
case 1:
|
||||
button = SDL_BUTTON_LEFT;
|
||||
break;
|
||||
case 2:
|
||||
button = SDL_BUTTON_RIGHT;
|
||||
break;
|
||||
case 3:
|
||||
button = SDL_BUTTON_MIDDLE;
|
||||
break;
|
||||
default:
|
||||
button = (Uint8)i;
|
||||
break;
|
||||
}
|
||||
SDL_SendMouseButton(UIKit_GetEventTimestamp([event timestamp]), sdlwindow, 0, SDL_RELEASED, button);
|
||||
}
|
||||
}
|
||||
}
|
||||
handled = YES;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (!handled) {
|
||||
SDL_TouchDeviceType touchType = [self touchTypeForTouch:touch];
|
||||
SDL_TouchID touchId = [self touchIdForType:touchType];
|
||||
float pressure = [self pressureForTouch:touch];
|
||||
|
||||
if (SDL_AddTouch(touchId, touchType, "") < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* FIXME, need to send: int clicks = (int) touch.tapCount; ? */
|
||||
|
||||
CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
|
||||
SDL_SendTouch(UIKit_GetEventTimestamp([event timestamp]),
|
||||
touchId, (SDL_FingerID)((size_t)touch), sdlwindow,
|
||||
SDL_FALSE, locationInView.x, locationInView.y, pressure);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
[self touchesEnded:touches withEvent:event];
|
||||
}
|
||||
|
||||
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
for (UITouch *touch in touches) {
|
||||
BOOL handled = NO;
|
||||
|
||||
#if !TARGET_OS_TV && defined(__IPHONE_13_4)
|
||||
if (@available(iOS 13.4, *)) {
|
||||
if (touch.type == UITouchTypeIndirectPointer) {
|
||||
/* Already handled in pointerInteraction callback */
|
||||
handled = YES;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (!handled) {
|
||||
SDL_TouchDeviceType touchType = [self touchTypeForTouch:touch];
|
||||
SDL_TouchID touchId = [self touchIdForType:touchType];
|
||||
float pressure = [self pressureForTouch:touch];
|
||||
|
||||
if (SDL_AddTouch(touchId, touchType, "") < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
|
||||
SDL_SendTouchMotion(UIKit_GetEventTimestamp([event timestamp]),
|
||||
touchId, (SDL_FingerID)((size_t)touch), sdlwindow,
|
||||
locationInView.x, locationInView.y, pressure);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if TARGET_OS_TV || defined(__IPHONE_9_1)
|
||||
- (SDL_Scancode)scancodeFromPress:(UIPress *)press
|
||||
{
|
||||
#ifdef __IPHONE_13_4
|
||||
if ([press respondsToSelector:@selector((key))]) {
|
||||
if (press.key != nil) {
|
||||
return (SDL_Scancode)press.key.keyCode;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef SDL_JOYSTICK_DISABLED
|
||||
/* Presses from Apple TV remote */
|
||||
if (!SDL_AppleTVRemoteOpenedAsJoystick) {
|
||||
switch (press.type) {
|
||||
case UIPressTypeUpArrow:
|
||||
return SDL_SCANCODE_UP;
|
||||
case UIPressTypeDownArrow:
|
||||
return SDL_SCANCODE_DOWN;
|
||||
case UIPressTypeLeftArrow:
|
||||
return SDL_SCANCODE_LEFT;
|
||||
case UIPressTypeRightArrow:
|
||||
return SDL_SCANCODE_RIGHT;
|
||||
case UIPressTypeSelect:
|
||||
/* HIG says: "primary button behavior" */
|
||||
return SDL_SCANCODE_RETURN;
|
||||
case UIPressTypeMenu:
|
||||
/* HIG says: "returns to previous screen" */
|
||||
return SDL_SCANCODE_ESCAPE;
|
||||
case UIPressTypePlayPause:
|
||||
/* HIG says: "secondary button behavior" */
|
||||
return SDL_SCANCODE_PAUSE;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif /* !SDL_JOYSTICK_DISABLED */
|
||||
|
||||
return SDL_SCANCODE_UNKNOWN;
|
||||
}
|
||||
|
||||
- (void)pressesBegan:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
|
||||
{
|
||||
if (!SDL_HasGCKeyboard()) {
|
||||
for (UIPress *press in presses) {
|
||||
SDL_Scancode scancode = [self scancodeFromPress:press];
|
||||
SDL_SendKeyboardKey(UIKit_GetEventTimestamp([event timestamp]), SDL_PRESSED, scancode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
|
||||
{
|
||||
if (!SDL_HasGCKeyboard()) {
|
||||
for (UIPress *press in presses) {
|
||||
SDL_Scancode scancode = [self scancodeFromPress:press];
|
||||
SDL_SendKeyboardKey(UIKit_GetEventTimestamp([event timestamp]), SDL_RELEASED, scancode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)pressesCancelled:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
|
||||
{
|
||||
if (!SDL_HasGCKeyboard()) {
|
||||
for (UIPress *press in presses) {
|
||||
SDL_Scancode scancode = [self scancodeFromPress:press];
|
||||
SDL_SendKeyboardKey(UIKit_GetEventTimestamp([event timestamp]), SDL_RELEASED, scancode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)pressesChanged:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
|
||||
{
|
||||
/* This is only called when the force of a press changes. */
|
||||
}
|
||||
|
||||
#endif /* TARGET_OS_TV || defined(__IPHONE_9_1) */
|
||||
|
||||
#if TARGET_OS_TV
|
||||
- (void)swipeGesture:(UISwipeGestureRecognizer *)gesture
|
||||
{
|
||||
/* Swipe gestures don't trigger begin states. */
|
||||
if (gesture.state == UIGestureRecognizerStateEnded) {
|
||||
#ifndef SDL_JOYSTICK_DISABLED
|
||||
if (!SDL_AppleTVRemoteOpenedAsJoystick) {
|
||||
/* Send arrow key presses for now, as we don't have an external API
|
||||
* which better maps to swipe gestures. */
|
||||
switch (gesture.direction) {
|
||||
case UISwipeGestureRecognizerDirectionUp:
|
||||
SDL_SendKeyboardKeyAutoRelease(0, SDL_SCANCODE_UP);
|
||||
break;
|
||||
case UISwipeGestureRecognizerDirectionDown:
|
||||
SDL_SendKeyboardKeyAutoRelease(0, SDL_SCANCODE_DOWN);
|
||||
break;
|
||||
case UISwipeGestureRecognizerDirectionLeft:
|
||||
SDL_SendKeyboardKeyAutoRelease(0, SDL_SCANCODE_LEFT);
|
||||
break;
|
||||
case UISwipeGestureRecognizerDirectionRight:
|
||||
SDL_SendKeyboardKeyAutoRelease(0, SDL_SCANCODE_RIGHT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif /* !SDL_JOYSTICK_DISABLED */
|
||||
}
|
||||
}
|
||||
#endif /* TARGET_OS_TV */
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SDL_VIDEO_DRIVER_UIKIT */
|
95
external/sdl/SDL/src/video/uikit/SDL_uikitviewcontroller.h
vendored
Normal file
95
external/sdl/SDL/src/video/uikit/SDL_uikitviewcontroller.h
vendored
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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"
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#include "../SDL_sysvideo.h"
|
||||
|
||||
#if TARGET_OS_TV
|
||||
#import <GameController/GameController.h>
|
||||
#define SDLRootViewController GCEventViewController
|
||||
#else
|
||||
#define SDLRootViewController UIViewController
|
||||
#endif
|
||||
|
||||
@interface SDLUITextField : UITextField
|
||||
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender;
|
||||
@end
|
||||
|
||||
#ifdef SDL_IPHONE_KEYBOARD
|
||||
@interface SDL_uikitviewcontroller : SDLRootViewController <UITextFieldDelegate>
|
||||
#else
|
||||
@interface SDL_uikitviewcontroller : SDLRootViewController
|
||||
#endif
|
||||
|
||||
@property(nonatomic, assign) SDL_Window *window;
|
||||
|
||||
- (instancetype)initWithSDLWindow:(SDL_Window *)_window;
|
||||
|
||||
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection;
|
||||
|
||||
- (void)setAnimationCallback:(int)interval
|
||||
callback:(void (*)(void *))callback
|
||||
callbackParam:(void *)callbackParam;
|
||||
|
||||
- (void)startAnimation;
|
||||
- (void)stopAnimation;
|
||||
|
||||
- (void)doLoop:(CADisplayLink *)sender;
|
||||
|
||||
- (void)loadView;
|
||||
- (void)viewDidLayoutSubviews;
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
- (NSUInteger)supportedInterfaceOrientations;
|
||||
- (BOOL)prefersStatusBarHidden;
|
||||
- (BOOL)prefersHomeIndicatorAutoHidden;
|
||||
- (UIRectEdge)preferredScreenEdgesDeferringSystemGestures;
|
||||
|
||||
@property(nonatomic, assign) int homeIndicatorHidden;
|
||||
#endif
|
||||
|
||||
#ifdef SDL_IPHONE_KEYBOARD
|
||||
- (void)showKeyboard;
|
||||
- (void)hideKeyboard;
|
||||
- (void)initKeyboard;
|
||||
- (void)deinitKeyboard;
|
||||
|
||||
- (void)keyboardWillShow:(NSNotification *)notification;
|
||||
- (void)keyboardWillHide:(NSNotification *)notification;
|
||||
|
||||
- (void)updateKeyboard;
|
||||
|
||||
@property(nonatomic, assign, getter=isKeyboardVisible) BOOL keyboardVisible;
|
||||
@property(nonatomic, assign) SDL_Rect textInputRect;
|
||||
@property(nonatomic, assign) int keyboardHeight;
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
#ifdef SDL_IPHONE_KEYBOARD
|
||||
SDL_bool UIKit_HasScreenKeyboardSupport(SDL_VideoDevice *_this);
|
||||
void UIKit_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
void UIKit_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
SDL_bool UIKit_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
int UIKit_SetTextInputRect(SDL_VideoDevice *_this, const SDL_Rect *rect);
|
||||
#endif
|
575
external/sdl/SDL/src/video/uikit/SDL_uikitviewcontroller.m
vendored
Normal file
575
external/sdl/SDL/src/video/uikit/SDL_uikitviewcontroller.m
vendored
Normal file
@ -0,0 +1,575 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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"
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_UIKIT
|
||||
|
||||
#include "../SDL_sysvideo.h"
|
||||
#include "../../events/SDL_events_c.h"
|
||||
|
||||
#include "SDL_uikitviewcontroller.h"
|
||||
#include "SDL_uikitmessagebox.h"
|
||||
#include "SDL_uikitevents.h"
|
||||
#include "SDL_uikitvideo.h"
|
||||
#include "SDL_uikitmodes.h"
|
||||
#include "SDL_uikitwindow.h"
|
||||
#include "SDL_uikitopengles.h"
|
||||
|
||||
#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;
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunguarded-availability-new"
|
||||
if ([viewcontroller respondsToSelector:@selector(setNeedsUpdateOfHomeIndicatorAutoHidden)]) {
|
||||
[viewcontroller setNeedsUpdateOfHomeIndicatorAutoHidden];
|
||||
[viewcontroller setNeedsUpdateOfScreenEdgesDeferringSystemGestures];
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@implementation SDLUITextField : UITextField
|
||||
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
|
||||
{
|
||||
if (action == @selector(paste:)) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
return [super canPerformAction:action withSender:sender];
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation SDL_uikitviewcontroller
|
||||
{
|
||||
CADisplayLink *displayLink;
|
||||
int animationInterval;
|
||||
void (*animationCallback)(void *);
|
||||
void *animationCallbackParam;
|
||||
|
||||
#ifdef SDL_IPHONE_KEYBOARD
|
||||
SDLUITextField *textField;
|
||||
BOOL hardwareKeyboard;
|
||||
BOOL showingKeyboard;
|
||||
BOOL rotatingOrientation;
|
||||
NSString *committedText;
|
||||
NSString *obligateForBackspace;
|
||||
#endif
|
||||
}
|
||||
|
||||
@synthesize window;
|
||||
|
||||
- (instancetype)initWithSDLWindow:(SDL_Window *)_window
|
||||
{
|
||||
if (self = [super initWithNibName:nil bundle:nil]) {
|
||||
self.window = _window;
|
||||
|
||||
#ifdef SDL_IPHONE_KEYBOARD
|
||||
[self initKeyboard];
|
||||
hardwareKeyboard = NO;
|
||||
showingKeyboard = NO;
|
||||
rotatingOrientation = NO;
|
||||
#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
|
||||
{
|
||||
#ifdef 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)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
|
||||
{
|
||||
SDL_SetSystemTheme(UIKit_GetSystemTheme());
|
||||
}
|
||||
|
||||
- (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_UIKitWindowData *data = (__bridge SDL_UIKitWindowData *)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. */
|
||||
#if defined(SDL_VIDEO_OPENGL_ES) || defined(SDL_VIDEO_OPENGL_ES2)
|
||||
UIKit_GL_RestoreCurrentContext();
|
||||
#endif
|
||||
|
||||
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_EVENT_WINDOW_RESIZED, w, h);
|
||||
}
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
- (NSUInteger)supportedInterfaceOrientations
|
||||
{
|
||||
return UIKit_GetSupportedOrientations(window);
|
||||
}
|
||||
|
||||
- (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;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)prefersPointerLocked
|
||||
{
|
||||
return SDL_GCMouseRelativeMode() ? YES : NO;
|
||||
}
|
||||
|
||||
#endif /* !TARGET_OS_TV */
|
||||
|
||||
/*
|
||||
---- Keyboard related functionality below this line ----
|
||||
*/
|
||||
#ifdef SDL_IPHONE_KEYBOARD
|
||||
|
||||
@synthesize textInputRect;
|
||||
@synthesize keyboardHeight;
|
||||
@synthesize keyboardVisible;
|
||||
|
||||
/* Set ourselves up as a UITextFieldDelegate */
|
||||
- (void)initKeyboard
|
||||
{
|
||||
obligateForBackspace = @" "; /* 64 space */
|
||||
textField = [[SDLUITextField alloc] initWithFrame:CGRectZero];
|
||||
textField.delegate = self;
|
||||
/* placeholder so there is something to delete! */
|
||||
textField.text = obligateForBackspace;
|
||||
committedText = textField.text;
|
||||
|
||||
/* 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];
|
||||
}
|
||||
|
||||
- (NSArray *)keyCommands
|
||||
{
|
||||
NSMutableArray *commands = [[NSMutableArray alloc] init];
|
||||
[commands addObject:[UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow modifierFlags:kNilOptions action:@selector(handleCommand:)]];
|
||||
[commands addObject:[UIKeyCommand keyCommandWithInput:UIKeyInputDownArrow modifierFlags:kNilOptions action:@selector(handleCommand:)]];
|
||||
[commands addObject:[UIKeyCommand keyCommandWithInput:UIKeyInputLeftArrow modifierFlags:kNilOptions action:@selector(handleCommand:)]];
|
||||
[commands addObject:[UIKeyCommand keyCommandWithInput:UIKeyInputRightArrow modifierFlags:kNilOptions action:@selector(handleCommand:)]];
|
||||
[commands addObject:[UIKeyCommand keyCommandWithInput:UIKeyInputEscape modifierFlags:kNilOptions action:@selector(handleCommand:)]];
|
||||
return [NSArray arrayWithArray:commands];
|
||||
}
|
||||
|
||||
- (void)handleCommand:(UIKeyCommand *)keyCommand
|
||||
{
|
||||
SDL_Scancode scancode = SDL_SCANCODE_UNKNOWN;
|
||||
NSString *input = keyCommand.input;
|
||||
|
||||
if (input == UIKeyInputUpArrow) {
|
||||
scancode = SDL_SCANCODE_UP;
|
||||
} else if (input == UIKeyInputDownArrow) {
|
||||
scancode = SDL_SCANCODE_DOWN;
|
||||
} else if (input == UIKeyInputLeftArrow) {
|
||||
scancode = SDL_SCANCODE_LEFT;
|
||||
} else if (input == UIKeyInputRightArrow) {
|
||||
scancode = SDL_SCANCODE_RIGHT;
|
||||
} else if (input == UIKeyInputEscape) {
|
||||
scancode = SDL_SCANCODE_ESCAPE;
|
||||
}
|
||||
|
||||
if (scancode != SDL_SCANCODE_UNKNOWN) {
|
||||
SDL_SendKeyboardKeyAutoRelease(0, scancode);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setView:(UIView *)view
|
||||
{
|
||||
[super setView:view];
|
||||
|
||||
[view addSubview:textField];
|
||||
|
||||
if (keyboardVisible) {
|
||||
[self showKeyboard];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
|
||||
{
|
||||
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
|
||||
rotatingOrientation = YES;
|
||||
[coordinator
|
||||
animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
|
||||
}
|
||||
completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
|
||||
self->rotatingOrientation = NO;
|
||||
}];
|
||||
}
|
||||
|
||||
- (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) {
|
||||
showingKeyboard = YES;
|
||||
[textField becomeFirstResponder];
|
||||
showingKeyboard = NO;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 (!showingKeyboard && !rotatingOrientation) {
|
||||
SDL_StopTextInput();
|
||||
}
|
||||
[self setKeyboardHeight:0];
|
||||
}
|
||||
|
||||
- (void)textFieldTextDidChange:(NSNotification *)notification
|
||||
{
|
||||
if (textField.markedTextRange == nil) {
|
||||
NSUInteger compareLength = SDL_min(textField.text.length, committedText.length);
|
||||
NSUInteger matchLength;
|
||||
|
||||
/* Backspace over characters that are no longer in the string */
|
||||
for (matchLength = 0; matchLength < compareLength; ++matchLength) {
|
||||
if ([committedText characterAtIndex:matchLength] != [textField.text characterAtIndex:matchLength]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (matchLength < committedText.length) {
|
||||
size_t deleteLength = SDL_utf8strlen([[committedText substringFromIndex:matchLength] UTF8String]);
|
||||
while (deleteLength > 0) {
|
||||
/* Send distinct down and up events for each backspace action */
|
||||
SDL_SendKeyboardKey(0, SDL_PRESSED, SDL_SCANCODE_BACKSPACE);
|
||||
SDL_SendKeyboardKey(0, SDL_RELEASED, SDL_SCANCODE_BACKSPACE);
|
||||
--deleteLength;
|
||||
}
|
||||
}
|
||||
|
||||
if (matchLength < textField.text.length) {
|
||||
NSString *pendingText = [textField.text substringFromIndex:matchLength];
|
||||
if (!SDL_HardwareKeyboardKeyPressed()) {
|
||||
/* Go through all the characters in the string we've been sent and
|
||||
* convert them to key presses */
|
||||
NSUInteger i;
|
||||
for (i = 0; i < pendingText.length; i++) {
|
||||
SDL_SendKeyboardUnicodeKey(0, [pendingText characterAtIndex:i]);
|
||||
}
|
||||
}
|
||||
SDL_SendKeyboardText([pendingText UTF8String]);
|
||||
}
|
||||
committedText = textField.text;
|
||||
}
|
||||
}
|
||||
|
||||
- (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
|
||||
{
|
||||
if (textField.markedTextRange == nil) {
|
||||
if (textField.text.length < 16) {
|
||||
textField.text = obligateForBackspace;
|
||||
committedText = textField.text;
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
/* Terminates the editing session */
|
||||
- (BOOL)textFieldShouldReturn:(UITextField *)_textField
|
||||
{
|
||||
SDL_SendKeyboardKeyAutoRelease(0, SDL_SCANCODE_RETURN);
|
||||
if (keyboardVisible &&
|
||||
SDL_GetHintBoolean(SDL_HINT_RETURN_KEY_HIDES_IME, SDL_FALSE)) {
|
||||
SDL_StopTextInput();
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
/* iPhone keyboard addition functions */
|
||||
#ifdef SDL_IPHONE_KEYBOARD
|
||||
|
||||
static SDL_uikitviewcontroller *GetWindowViewController(SDL_Window *window)
|
||||
{
|
||||
if (!window || !window->driverdata) {
|
||||
SDL_SetError("Invalid window");
|
||||
return nil;
|
||||
}
|
||||
|
||||
SDL_UIKitWindowData *data = (__bridge SDL_UIKitWindowData *)window->driverdata;
|
||||
|
||||
return data.viewcontroller;
|
||||
}
|
||||
|
||||
SDL_bool UIKit_HasScreenKeyboardSupport(SDL_VideoDevice *_this)
|
||||
{
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
void UIKit_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL_uikitviewcontroller *vc = GetWindowViewController(window);
|
||||
[vc showKeyboard];
|
||||
}
|
||||
}
|
||||
|
||||
void UIKit_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL_uikitviewcontroller *vc = GetWindowViewController(window);
|
||||
[vc hideKeyboard];
|
||||
}
|
||||
}
|
||||
|
||||
SDL_bool UIKit_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL_uikitviewcontroller *vc = GetWindowViewController(window);
|
||||
if (vc != nil) {
|
||||
return vc.keyboardVisible;
|
||||
}
|
||||
return SDL_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
int UIKit_SetTextInputRect(SDL_VideoDevice *_this, const SDL_Rect *rect)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL_uikitviewcontroller *vc = GetWindowViewController(SDL_GetFocusWindow());
|
||||
if (vc != nil) {
|
||||
vc.textInputRect = *rect;
|
||||
|
||||
if (vc.keyboardVisible) {
|
||||
[vc updateKeyboard];
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* SDL_IPHONE_KEYBOARD */
|
||||
|
||||
#endif /* SDL_VIDEO_DRIVER_UIKIT */
|
49
external/sdl/SDL/src/video/uikit/SDL_uikitvulkan.h
vendored
Normal file
49
external/sdl/SDL/src/video/uikit/SDL_uikitvulkan.h
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @author Mark Callow, www.edgewise-consulting.com. Based on Jacob Lifshay's
|
||||
* SDL_x11vulkan.h.
|
||||
*/
|
||||
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_uikitvulkan_h_
|
||||
#define SDL_uikitvulkan_h_
|
||||
|
||||
#include "../SDL_vulkan_internal.h"
|
||||
#include "../SDL_sysvideo.h"
|
||||
|
||||
#if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_UIKIT)
|
||||
|
||||
int UIKit_Vulkan_LoadLibrary(SDL_VideoDevice *_this, const char *path);
|
||||
void UIKit_Vulkan_UnloadLibrary(SDL_VideoDevice *_this);
|
||||
SDL_bool UIKit_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this,
|
||||
unsigned *count,
|
||||
const char **names);
|
||||
SDL_bool UIKit_Vulkan_CreateSurface(SDL_VideoDevice *_this,
|
||||
SDL_Window *window,
|
||||
VkInstance instance,
|
||||
VkSurfaceKHR *surface);
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* SDL_uikitvulkan_h_ */
|
263
external/sdl/SDL/src/video/uikit/SDL_uikitvulkan.m
vendored
Normal file
263
external/sdl/SDL/src/video/uikit/SDL_uikitvulkan.m
vendored
Normal file
@ -0,0 +1,263 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @author Mark Callow, www.edgewise-consulting.com. Based on Jacob Lifshay's
|
||||
* SDL_x11vulkan.c.
|
||||
*/
|
||||
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_UIKIT)
|
||||
|
||||
#include "SDL_uikitvideo.h"
|
||||
#include "SDL_uikitwindow.h"
|
||||
|
||||
#include "SDL_uikitvulkan.h"
|
||||
#include "SDL_uikitmetalview.h"
|
||||
|
||||
#include <SDL3/SDL_syswm.h>
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
const char *defaultPaths[] = {
|
||||
"libvulkan.dylib",
|
||||
};
|
||||
|
||||
/* Since libSDL is static, could use RTLD_SELF. Using RTLD_DEFAULT is future
|
||||
* proofing. */
|
||||
#define DEFAULT_HANDLE RTLD_DEFAULT
|
||||
|
||||
int UIKit_Vulkan_LoadLibrary(SDL_VideoDevice *_this, const char *path)
|
||||
{
|
||||
VkExtensionProperties *extensions = NULL;
|
||||
Uint32 extensionCount = 0;
|
||||
SDL_bool hasSurfaceExtension = SDL_FALSE;
|
||||
SDL_bool hasMetalSurfaceExtension = SDL_FALSE;
|
||||
SDL_bool hasIOSSurfaceExtension = SDL_FALSE;
|
||||
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL;
|
||||
|
||||
if (_this->vulkan_config.loader_handle) {
|
||||
return SDL_SetError("Vulkan Portability library is already loaded.");
|
||||
}
|
||||
|
||||
/* Load the Vulkan loader library */
|
||||
if (!path) {
|
||||
path = SDL_getenv("SDL_VULKAN_LIBRARY");
|
||||
}
|
||||
|
||||
if (!path) {
|
||||
/* Handle the case where Vulkan Portability is linked statically. */
|
||||
vkGetInstanceProcAddr =
|
||||
(PFN_vkGetInstanceProcAddr)dlsym(DEFAULT_HANDLE,
|
||||
"vkGetInstanceProcAddr");
|
||||
}
|
||||
|
||||
if (vkGetInstanceProcAddr) {
|
||||
_this->vulkan_config.loader_handle = DEFAULT_HANDLE;
|
||||
} else {
|
||||
const char **paths;
|
||||
const char *foundPath = NULL;
|
||||
int numPaths;
|
||||
int i;
|
||||
|
||||
if (path) {
|
||||
paths = &path;
|
||||
numPaths = 1;
|
||||
} else {
|
||||
/* Look for the .dylib packaged with the application instead. */
|
||||
paths = defaultPaths;
|
||||
numPaths = SDL_arraysize(defaultPaths);
|
||||
}
|
||||
|
||||
for (i = 0; i < numPaths && _this->vulkan_config.loader_handle == NULL; i++) {
|
||||
foundPath = paths[i];
|
||||
_this->vulkan_config.loader_handle = SDL_LoadObject(foundPath);
|
||||
}
|
||||
|
||||
if (_this->vulkan_config.loader_handle == NULL) {
|
||||
return SDL_SetError("Failed to load Vulkan Portability library");
|
||||
}
|
||||
|
||||
SDL_strlcpy(_this->vulkan_config.loader_path, path,
|
||||
SDL_arraysize(_this->vulkan_config.loader_path));
|
||||
vkGetInstanceProcAddr =
|
||||
(PFN_vkGetInstanceProcAddr)SDL_LoadFunction(
|
||||
_this->vulkan_config.loader_handle,
|
||||
"vkGetInstanceProcAddr");
|
||||
}
|
||||
|
||||
if (!vkGetInstanceProcAddr) {
|
||||
SDL_SetError("Failed to find %s in either executable or %s: %s",
|
||||
"vkGetInstanceProcAddr",
|
||||
"linked Vulkan Portability library",
|
||||
(const char *)dlerror());
|
||||
goto fail;
|
||||
}
|
||||
|
||||
_this->vulkan_config.vkGetInstanceProcAddr = (void *)vkGetInstanceProcAddr;
|
||||
_this->vulkan_config.vkEnumerateInstanceExtensionProperties =
|
||||
(void *)((PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr)(
|
||||
VK_NULL_HANDLE, "vkEnumerateInstanceExtensionProperties");
|
||||
|
||||
if (!_this->vulkan_config.vkEnumerateInstanceExtensionProperties) {
|
||||
SDL_SetError("No vkEnumerateInstanceExtensionProperties found.");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
extensions = SDL_Vulkan_CreateInstanceExtensionsList(
|
||||
(PFN_vkEnumerateInstanceExtensionProperties)
|
||||
_this->vulkan_config.vkEnumerateInstanceExtensionProperties,
|
||||
&extensionCount);
|
||||
|
||||
if (!extensions) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (Uint32 i = 0; i < extensionCount; i++) {
|
||||
if (SDL_strcmp(VK_KHR_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) {
|
||||
hasSurfaceExtension = SDL_TRUE;
|
||||
} else if (SDL_strcmp(VK_EXT_METAL_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) {
|
||||
hasMetalSurfaceExtension = SDL_TRUE;
|
||||
} else if (SDL_strcmp(VK_MVK_IOS_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) {
|
||||
hasIOSSurfaceExtension = SDL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_free(extensions);
|
||||
|
||||
if (!hasSurfaceExtension) {
|
||||
SDL_SetError("Installed Vulkan Portability doesn't implement the " VK_KHR_SURFACE_EXTENSION_NAME " extension");
|
||||
goto fail;
|
||||
} else if (!hasMetalSurfaceExtension && !hasIOSSurfaceExtension) {
|
||||
SDL_SetError("Installed Vulkan Portability doesn't implement the " VK_EXT_METAL_SURFACE_EXTENSION_NAME " or " VK_MVK_IOS_SURFACE_EXTENSION_NAME " extensions");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
_this->vulkan_config.loader_handle = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void UIKit_Vulkan_UnloadLibrary(SDL_VideoDevice *_this)
|
||||
{
|
||||
if (_this->vulkan_config.loader_handle) {
|
||||
if (_this->vulkan_config.loader_handle != DEFAULT_HANDLE) {
|
||||
SDL_UnloadObject(_this->vulkan_config.loader_handle);
|
||||
}
|
||||
_this->vulkan_config.loader_handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_bool UIKit_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this,
|
||||
unsigned *count,
|
||||
const char **names)
|
||||
{
|
||||
static const char *const extensionsForUIKit[] = {
|
||||
VK_KHR_SURFACE_EXTENSION_NAME, VK_EXT_METAL_SURFACE_EXTENSION_NAME
|
||||
};
|
||||
if (!_this->vulkan_config.loader_handle) {
|
||||
SDL_SetError("Vulkan is not loaded");
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
return SDL_Vulkan_GetInstanceExtensions_Helper(
|
||||
count, names, SDL_arraysize(extensionsForUIKit),
|
||||
extensionsForUIKit);
|
||||
}
|
||||
|
||||
SDL_bool UIKit_Vulkan_CreateSurface(SDL_VideoDevice *_this,
|
||||
SDL_Window *window,
|
||||
VkInstance instance,
|
||||
VkSurfaceKHR *surface)
|
||||
{
|
||||
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr =
|
||||
(PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr;
|
||||
PFN_vkCreateMetalSurfaceEXT vkCreateMetalSurfaceEXT =
|
||||
(PFN_vkCreateMetalSurfaceEXT)vkGetInstanceProcAddr(
|
||||
(VkInstance)instance,
|
||||
"vkCreateMetalSurfaceEXT");
|
||||
PFN_vkCreateIOSSurfaceMVK vkCreateIOSSurfaceMVK =
|
||||
(PFN_vkCreateIOSSurfaceMVK)vkGetInstanceProcAddr(
|
||||
(VkInstance)instance,
|
||||
"vkCreateIOSSurfaceMVK");
|
||||
VkResult result;
|
||||
SDL_MetalView metalview;
|
||||
|
||||
if (!_this->vulkan_config.loader_handle) {
|
||||
SDL_SetError("Vulkan is not loaded");
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
if (!vkCreateMetalSurfaceEXT && !vkCreateIOSSurfaceMVK) {
|
||||
SDL_SetError(VK_EXT_METAL_SURFACE_EXTENSION_NAME " or " VK_MVK_IOS_SURFACE_EXTENSION_NAME
|
||||
" extensions are not enabled in the Vulkan instance.");
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
metalview = UIKit_Metal_CreateView(_this, window);
|
||||
if (metalview == NULL) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
if (vkCreateMetalSurfaceEXT) {
|
||||
VkMetalSurfaceCreateInfoEXT createInfo = {};
|
||||
createInfo.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT;
|
||||
createInfo.pNext = NULL;
|
||||
createInfo.flags = 0;
|
||||
createInfo.pLayer = (__bridge const CAMetalLayer *)
|
||||
UIKit_Metal_GetLayer(_this, metalview);
|
||||
result = vkCreateMetalSurfaceEXT(instance, &createInfo, NULL, surface);
|
||||
if (result != VK_SUCCESS) {
|
||||
UIKit_Metal_DestroyView(_this, metalview);
|
||||
SDL_SetError("vkCreateMetalSurfaceEXT failed: %s",
|
||||
SDL_Vulkan_GetResultString(result));
|
||||
return SDL_FALSE;
|
||||
}
|
||||
} else {
|
||||
VkIOSSurfaceCreateInfoMVK createInfo = {};
|
||||
createInfo.sType = VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK;
|
||||
createInfo.pNext = NULL;
|
||||
createInfo.flags = 0;
|
||||
createInfo.pView = (const void *)metalview;
|
||||
result = vkCreateIOSSurfaceMVK(instance, &createInfo,
|
||||
NULL, surface);
|
||||
if (result != VK_SUCCESS) {
|
||||
UIKit_Metal_DestroyView(_this, metalview);
|
||||
SDL_SetError("vkCreateIOSSurfaceMVK failed: %s",
|
||||
SDL_Vulkan_GetResultString(result));
|
||||
return SDL_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Unfortunately there's no SDL_Vulkan_DestroySurface function we can call
|
||||
* Metal_DestroyView from. Right now the metal view's ref count is +2 (one
|
||||
* from returning a new view object in CreateView, and one because it's
|
||||
* a subview of the window.) If we release the view here to make it +1, it
|
||||
* will be destroyed when the window is destroyed. */
|
||||
CFBridgingRelease(metalview);
|
||||
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
#endif
|
56
external/sdl/SDL/src/video/uikit/SDL_uikitwindow.h
vendored
Normal file
56
external/sdl/SDL/src/video/uikit/SDL_uikitwindow.h
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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.
|
||||
*/
|
||||
#ifndef SDL_uikitwindow_h_
|
||||
#define SDL_uikitwindow_h_
|
||||
|
||||
#include "../SDL_sysvideo.h"
|
||||
#import "SDL_uikitvideo.h"
|
||||
#import "SDL_uikitview.h"
|
||||
#import "SDL_uikitviewcontroller.h"
|
||||
|
||||
extern int UIKit_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern void UIKit_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern void UIKit_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern void UIKit_HideWindow(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern void UIKit_RaiseWindow(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern void UIKit_SetWindowBordered(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool bordered);
|
||||
extern void UIKit_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen);
|
||||
extern void UIKit_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed);
|
||||
extern void UIKit_UpdatePointerLock(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern void UIKit_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern void UIKit_GetWindowSizeInPixels(SDL_VideoDevice *_this, SDL_Window *window, int *w, int *h);
|
||||
extern int UIKit_GetWindowWMInfo(SDL_VideoDevice *_this, SDL_Window *window, struct SDL_SysWMinfo *info);
|
||||
|
||||
extern NSUInteger UIKit_GetSupportedOrientations(SDL_Window *window);
|
||||
|
||||
@class UIWindow;
|
||||
|
||||
@interface SDL_UIKitWindowData : NSObject
|
||||
|
||||
@property(nonatomic, strong) UIWindow *uiwindow;
|
||||
@property(nonatomic, strong) SDL_uikitviewcontroller *viewcontroller;
|
||||
|
||||
/* Array of SDL_uikitviews owned by this window. */
|
||||
@property(nonatomic, copy) NSMutableArray *views;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* SDL_uikitwindow_h_ */
|
472
external/sdl/SDL/src/video/uikit/SDL_uikitwindow.m
vendored
Normal file
472
external/sdl/SDL/src/video/uikit/SDL_uikitwindow.m
vendored
Normal file
@ -0,0 +1,472 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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"
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_UIKIT
|
||||
|
||||
#include "../SDL_sysvideo.h"
|
||||
#include "../SDL_pixels_c.h"
|
||||
#include "../../events/SDL_events_c.h"
|
||||
|
||||
#include "SDL_uikitvideo.h"
|
||||
#include "SDL_uikitevents.h"
|
||||
#include "SDL_uikitmodes.h"
|
||||
#include "SDL_uikitwindow.h"
|
||||
#include "SDL_uikitappdelegate.h"
|
||||
#include "SDL_uikitview.h"
|
||||
#include "SDL_uikitopenglview.h"
|
||||
|
||||
#include <SDL3/SDL_syswm.h>
|
||||
|
||||
#include <Foundation/Foundation.h>
|
||||
|
||||
@implementation SDL_UIKitWindowData
|
||||
|
||||
@synthesize uiwindow;
|
||||
@synthesize viewcontroller;
|
||||
@synthesize views;
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
views = [NSMutableArray new];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface SDL_uikitwindow : UIWindow
|
||||
|
||||
- (void)layoutSubviews;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SDL_uikitwindow
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
/* Workaround to fix window orientation issues in iOS 8. */
|
||||
/* As of July 1 2019, I haven't been able to reproduce any orientation
|
||||
* issues with this disabled on iOS 12. The issue this is meant to fix might
|
||||
* only happen on iOS 8, or it might have been fixed another way with other
|
||||
* code... This code prevents split view (iOS 9+) from working on iPads, so
|
||||
* we want to avoid using it if possible. */
|
||||
if (!UIKit_IsSystemVersionAtLeast(9.0)) {
|
||||
self.frame = self.screen.bounds;
|
||||
}
|
||||
[super layoutSubviews];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static int SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, UIWindow *uiwindow, SDL_bool created)
|
||||
{
|
||||
SDL_VideoDisplay *display = SDL_GetVideoDisplayForWindow(window);
|
||||
SDL_UIKitDisplayData *displaydata = (__bridge SDL_UIKitDisplayData *)display->driverdata;
|
||||
SDL_uikitview *view;
|
||||
|
||||
CGRect frame = UIKit_ComputeViewFrame(window, displaydata.uiscreen);
|
||||
int width = (int)frame.size.width;
|
||||
int height = (int)frame.size.height;
|
||||
|
||||
SDL_UIKitWindowData *data = [[SDL_UIKitWindowData alloc] init];
|
||||
if (!data) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
window->driverdata = (SDL_WindowData *)CFBridgingRetain(data);
|
||||
|
||||
data.uiwindow = uiwindow;
|
||||
|
||||
if (displaydata.uiscreen != [UIScreen mainScreen]) {
|
||||
window->flags &= ~SDL_WINDOW_RESIZABLE; /* window is NEVER resizable */
|
||||
window->flags &= ~SDL_WINDOW_INPUT_FOCUS; /* never has input focus */
|
||||
window->flags |= SDL_WINDOW_BORDERLESS; /* never has a status bar. */
|
||||
}
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
if (displaydata.uiscreen == [UIScreen mainScreen]) {
|
||||
/* SDL_CreateWindow sets the window w&h to the display's bounds if the
|
||||
* fullscreen flag is set. But the display bounds orientation might not
|
||||
* match what we want, and GetSupportedOrientations call below uses the
|
||||
* window w&h. They're overridden below anyway, so we'll just set them
|
||||
* to the requested size for the purposes of determining orientation. */
|
||||
window->w = window->windowed.w;
|
||||
window->h = window->windowed.h;
|
||||
|
||||
NSUInteger orients = UIKit_GetSupportedOrientations(window);
|
||||
BOOL supportsLandscape = (orients & UIInterfaceOrientationMaskLandscape) != 0;
|
||||
BOOL supportsPortrait = (orients & (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown)) != 0;
|
||||
|
||||
/* Make sure the width/height are oriented correctly */
|
||||
if ((width > height && !supportsLandscape) || (height > width && !supportsPortrait)) {
|
||||
int temp = width;
|
||||
width = height;
|
||||
height = temp;
|
||||
}
|
||||
}
|
||||
#endif /* !TARGET_OS_TV */
|
||||
|
||||
#if 0 /* Don't set the x/y position, it's already placed on a display */
|
||||
window->x = 0;
|
||||
window->y = 0;
|
||||
#endif
|
||||
window->w = width;
|
||||
window->h = height;
|
||||
|
||||
/* The View Controller will handle rotating the view when the device
|
||||
* orientation changes. This will trigger resize events, if appropriate. */
|
||||
data.viewcontroller = [[SDL_uikitviewcontroller alloc] initWithSDLWindow:window];
|
||||
|
||||
/* The window will initially contain a generic view so resizes, touch events,
|
||||
* etc. can be handled without an active OpenGL view/context. */
|
||||
view = [[SDL_uikitview alloc] initWithFrame:frame];
|
||||
|
||||
/* Sets this view as the controller's view, and adds the view to the window
|
||||
* hierarchy. */
|
||||
[view setSDLWindow:window];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int UIKit_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL_VideoDisplay *display = SDL_GetVideoDisplayForWindow(window);
|
||||
SDL_UIKitDisplayData *data = (__bridge SDL_UIKitDisplayData *)display->driverdata;
|
||||
SDL_Window *other;
|
||||
|
||||
/* We currently only handle a single window per display on iOS */
|
||||
for (other = _this->windows; other; other = other->next) {
|
||||
if (other != window && SDL_GetVideoDisplayForWindow(other) == display) {
|
||||
return SDL_SetError("Only one window allowed per display.");
|
||||
}
|
||||
}
|
||||
|
||||
/* If monitor has a resolution of 0x0 (hasn't been explicitly set by the
|
||||
* user, so it's in standby), try to force the display to a resolution
|
||||
* that most closely matches the desired window size. */
|
||||
#if !TARGET_OS_TV
|
||||
const CGSize origsize = data.uiscreen.currentMode.size;
|
||||
if ((origsize.width == 0.0f) && (origsize.height == 0.0f)) {
|
||||
const SDL_DisplayMode *bestmode;
|
||||
SDL_bool include_high_density_modes = SDL_FALSE;
|
||||
if (window->flags & SDL_WINDOW_HIGH_PIXEL_DENSITY) {
|
||||
include_high_density_modes = SDL_TRUE;
|
||||
}
|
||||
bestmode = SDL_GetClosestFullscreenDisplayMode(display->id, window->w, window->h, 0.0f, include_high_density_modes);
|
||||
if (bestmode) {
|
||||
SDL_UIKitDisplayModeData *modedata = (__bridge SDL_UIKitDisplayModeData *)bestmode->driverdata;
|
||||
[data.uiscreen setCurrentMode:modedata.uiscreenmode];
|
||||
|
||||
/* desktop_mode doesn't change here (the higher level will
|
||||
* use it to set all the screens back to their defaults
|
||||
* upon window destruction, SDL_Quit(), etc. */
|
||||
SDL_SetCurrentDisplayMode(display, bestmode);
|
||||
}
|
||||
}
|
||||
|
||||
if (data.uiscreen == [UIScreen mainScreen]) {
|
||||
if (window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_BORDERLESS)) {
|
||||
[UIApplication sharedApplication].statusBarHidden = YES;
|
||||
} else {
|
||||
[UIApplication sharedApplication].statusBarHidden = NO;
|
||||
}
|
||||
}
|
||||
#endif /* !TARGET_OS_TV */
|
||||
|
||||
/* ignore the size user requested, and make a fullscreen window */
|
||||
/* !!! FIXME: can we have a smaller view? */
|
||||
UIWindow *uiwindow = [[SDL_uikitwindow alloc] initWithFrame:data.uiscreen.bounds];
|
||||
|
||||
/* put the window on an external display if appropriate. */
|
||||
if (data.uiscreen != [UIScreen mainScreen]) {
|
||||
[uiwindow setScreen:data.uiscreen];
|
||||
}
|
||||
|
||||
if (SetupWindowData(_this, window, uiwindow, SDL_TRUE) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void UIKit_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL_UIKitWindowData *data = (__bridge SDL_UIKitWindowData *)window->driverdata;
|
||||
data.viewcontroller.title = @(window->title);
|
||||
}
|
||||
}
|
||||
|
||||
void UIKit_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL_UIKitWindowData *data = (__bridge SDL_UIKitWindowData *)window->driverdata;
|
||||
[data.uiwindow makeKeyAndVisible];
|
||||
|
||||
/* Make this window the current mouse focus for touch input */
|
||||
SDL_VideoDisplay *display = SDL_GetVideoDisplayForWindow(window);
|
||||
SDL_UIKitDisplayData *displaydata = (__bridge SDL_UIKitDisplayData *)display->driverdata;
|
||||
if (displaydata.uiscreen == [UIScreen mainScreen]) {
|
||||
SDL_SetMouseFocus(window);
|
||||
SDL_SetKeyboardFocus(window);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UIKit_HideWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL_UIKitWindowData *data = (__bridge SDL_UIKitWindowData *)window->driverdata;
|
||||
data.uiwindow.hidden = YES;
|
||||
}
|
||||
}
|
||||
|
||||
void UIKit_RaiseWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
{
|
||||
/* We don't currently offer a concept of "raising" the SDL window, since
|
||||
* we only allow one per display, in the iOS fashion.
|
||||
* However, we use this entry point to rebind the context to the view
|
||||
* during OnWindowRestored processing. */
|
||||
_this->GL_MakeCurrent(_this, _this->current_glwin, _this->current_glctx);
|
||||
}
|
||||
|
||||
static void UIKit_UpdateWindowBorder(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
{
|
||||
SDL_UIKitWindowData *data = (__bridge SDL_UIKitWindowData *)window->driverdata;
|
||||
SDL_uikitviewcontroller *viewcontroller = data.viewcontroller;
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
if (data.uiwindow.screen == [UIScreen mainScreen]) {
|
||||
if (window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_BORDERLESS)) {
|
||||
[UIApplication sharedApplication].statusBarHidden = YES;
|
||||
} else {
|
||||
[UIApplication sharedApplication].statusBarHidden = NO;
|
||||
}
|
||||
|
||||
[viewcontroller setNeedsStatusBarAppearanceUpdate];
|
||||
}
|
||||
|
||||
/* Update the view's frame to account for the status bar change. */
|
||||
viewcontroller.view.frame = UIKit_ComputeViewFrame(window, data.uiwindow.screen);
|
||||
#endif /* !TARGET_OS_TV */
|
||||
|
||||
#ifdef SDL_IPHONE_KEYBOARD
|
||||
/* Make sure the view is offset correctly when the keyboard is visible. */
|
||||
[viewcontroller updateKeyboard];
|
||||
#endif
|
||||
|
||||
[viewcontroller.view setNeedsLayout];
|
||||
[viewcontroller.view layoutIfNeeded];
|
||||
}
|
||||
|
||||
void UIKit_SetWindowBordered(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool bordered)
|
||||
{
|
||||
@autoreleasepool {
|
||||
UIKit_UpdateWindowBorder(_this, window);
|
||||
}
|
||||
}
|
||||
|
||||
void UIKit_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen)
|
||||
{
|
||||
@autoreleasepool {
|
||||
UIKit_UpdateWindowBorder(_this, window);
|
||||
}
|
||||
}
|
||||
|
||||
void UIKit_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed)
|
||||
{
|
||||
/* There really isn't a concept of window grab or cursor confinement on iOS */
|
||||
}
|
||||
|
||||
void UIKit_UpdatePointerLock(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
{
|
||||
#if !TARGET_OS_TV
|
||||
#if defined(__IPHONE_14_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0
|
||||
@autoreleasepool {
|
||||
SDL_UIKitWindowData *data = (__bridge SDL_UIKitWindowData *)window->driverdata;
|
||||
SDL_uikitviewcontroller *viewcontroller = data.viewcontroller;
|
||||
if (@available(iOS 14.0, *)) {
|
||||
[viewcontroller setNeedsUpdateOfPrefersPointerLocked];
|
||||
}
|
||||
}
|
||||
#endif /* defined(__IPHONE_14_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0 */
|
||||
#endif /* !TARGET_OS_TV */
|
||||
}
|
||||
|
||||
void UIKit_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
{
|
||||
@autoreleasepool {
|
||||
if (window->driverdata != NULL) {
|
||||
SDL_UIKitWindowData *data = (__bridge SDL_UIKitWindowData *)window->driverdata;
|
||||
NSArray *views = nil;
|
||||
|
||||
[data.viewcontroller stopAnimation];
|
||||
|
||||
/* Detach all views from this window. We use a copy of the array
|
||||
* because setSDLWindow will remove the object from the original
|
||||
* array, which would be undesirable if we were iterating over it. */
|
||||
views = [data.views copy];
|
||||
for (SDL_uikitview *view in views) {
|
||||
[view setSDLWindow:NULL];
|
||||
}
|
||||
|
||||
/* iOS may still hold a reference to the window after we release it.
|
||||
* We want to make sure the SDL view controller isn't accessed in
|
||||
* that case, because it would contain an invalid pointer to the old
|
||||
* SDL window. */
|
||||
data.uiwindow.rootViewController = nil;
|
||||
data.uiwindow.hidden = YES;
|
||||
|
||||
CFRelease(window->driverdata);
|
||||
window->driverdata = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UIKit_GetWindowSizeInPixels(SDL_VideoDevice *_this, SDL_Window *window, int *w, int *h)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL_UIKitWindowData *windata = (__bridge SDL_UIKitWindowData *)window->driverdata;
|
||||
UIView *view = windata.viewcontroller.view;
|
||||
CGSize size = view.bounds.size;
|
||||
CGFloat scale = 1.0;
|
||||
|
||||
if (window->flags & SDL_WINDOW_HIGH_PIXEL_DENSITY) {
|
||||
scale = windata.uiwindow.screen.nativeScale;
|
||||
}
|
||||
|
||||
/* Integer truncation of fractional values matches SDL_uikitmetalview and
|
||||
* SDL_uikitopenglview. */
|
||||
*w = size.width * scale;
|
||||
*h = size.height * scale;
|
||||
}
|
||||
}
|
||||
|
||||
int UIKit_GetWindowWMInfo(SDL_VideoDevice *_this, SDL_Window *window, SDL_SysWMinfo *info)
|
||||
{
|
||||
@autoreleasepool {
|
||||
SDL_UIKitWindowData *data = (__bridge SDL_UIKitWindowData *)window->driverdata;
|
||||
|
||||
info->subsystem = SDL_SYSWM_UIKIT;
|
||||
info->info.uikit.window = data.uiwindow;
|
||||
|
||||
#if defined(SDL_VIDEO_OPENGL_ES) || defined(SDL_VIDEO_OPENGL_ES2)
|
||||
if ([data.viewcontroller.view isKindOfClass:[SDL_uikitopenglview class]]) {
|
||||
SDL_uikitopenglview *glview = (SDL_uikitopenglview *)data.viewcontroller.view;
|
||||
info->info.uikit.framebuffer = glview.drawableFramebuffer;
|
||||
info->info.uikit.colorbuffer = glview.drawableRenderbuffer;
|
||||
info->info.uikit.resolveFramebuffer = glview.msaaResolveFramebuffer;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
NSUInteger
|
||||
UIKit_GetSupportedOrientations(SDL_Window *window)
|
||||
{
|
||||
const char *hint = SDL_GetHint(SDL_HINT_ORIENTATIONS);
|
||||
NSUInteger validOrientations = UIInterfaceOrientationMaskAll;
|
||||
NSUInteger orientationMask = 0;
|
||||
|
||||
@autoreleasepool {
|
||||
SDL_UIKitWindowData *data = (__bridge SDL_UIKitWindowData *)window->driverdata;
|
||||
UIApplication *app = [UIApplication sharedApplication];
|
||||
|
||||
/* Get all possible valid orientations. If the app delegate doesn't tell
|
||||
* us, we get the orientations from Info.plist via UIApplication. */
|
||||
if ([app.delegate respondsToSelector:@selector(application:supportedInterfaceOrientationsForWindow:)]) {
|
||||
validOrientations = [app.delegate application:app supportedInterfaceOrientationsForWindow:data.uiwindow];
|
||||
} else {
|
||||
validOrientations = [app supportedInterfaceOrientationsForWindow:data.uiwindow];
|
||||
}
|
||||
|
||||
if (hint != NULL) {
|
||||
NSArray *orientations = [@(hint) componentsSeparatedByString:@" "];
|
||||
|
||||
if ([orientations containsObject:@"LandscapeLeft"]) {
|
||||
orientationMask |= UIInterfaceOrientationMaskLandscapeLeft;
|
||||
}
|
||||
if ([orientations containsObject:@"LandscapeRight"]) {
|
||||
orientationMask |= UIInterfaceOrientationMaskLandscapeRight;
|
||||
}
|
||||
if ([orientations containsObject:@"Portrait"]) {
|
||||
orientationMask |= UIInterfaceOrientationMaskPortrait;
|
||||
}
|
||||
if ([orientations containsObject:@"PortraitUpsideDown"]) {
|
||||
orientationMask |= UIInterfaceOrientationMaskPortraitUpsideDown;
|
||||
}
|
||||
}
|
||||
|
||||
if (orientationMask == 0 && (window->flags & SDL_WINDOW_RESIZABLE)) {
|
||||
/* any orientation is okay. */
|
||||
orientationMask = UIInterfaceOrientationMaskAll;
|
||||
}
|
||||
|
||||
if (orientationMask == 0) {
|
||||
if (window->w >= window->h) {
|
||||
orientationMask |= UIInterfaceOrientationMaskLandscape;
|
||||
}
|
||||
if (window->h >= window->w) {
|
||||
orientationMask |= (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown);
|
||||
}
|
||||
}
|
||||
|
||||
/* Don't allow upside-down orientation on phones, so answering calls is in the natural orientation */
|
||||
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone) {
|
||||
orientationMask &= ~UIInterfaceOrientationMaskPortraitUpsideDown;
|
||||
}
|
||||
|
||||
/* If none of the specified orientations are actually supported by the
|
||||
* app, we'll revert to what the app supports. An exception would be
|
||||
* thrown by the system otherwise. */
|
||||
if ((validOrientations & orientationMask) == 0) {
|
||||
orientationMask = validOrientations;
|
||||
}
|
||||
}
|
||||
|
||||
return orientationMask;
|
||||
}
|
||||
#endif /* !TARGET_OS_TV */
|
||||
|
||||
int SDL_iPhoneSetAnimationCallback(SDL_Window *window, int interval, void (*callback)(void *), void *callbackParam)
|
||||
{
|
||||
if (!window || !window->driverdata) {
|
||||
return SDL_SetError("Invalid window");
|
||||
}
|
||||
|
||||
@autoreleasepool {
|
||||
SDL_UIKitWindowData *data = (__bridge SDL_UIKitWindowData *)window->driverdata;
|
||||
[data.viewcontroller setAnimationCallback:interval
|
||||
callback:callback
|
||||
callbackParam:callbackParam];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* SDL_VIDEO_DRIVER_UIKIT */
|
Reference in New Issue
Block a user