initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
This commit is contained in:
16
drivers/apple_embedded/SCsub
Normal file
16
drivers/apple_embedded/SCsub
Normal file
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
env_apple_embedded = env.Clone()
|
||||
|
||||
# Enable module support
|
||||
env_apple_embedded.Append(CCFLAGS=["-fmodules", "-fcxx-modules"])
|
||||
|
||||
# Use bundled Vulkan headers
|
||||
vulkan_dir = "#thirdparty/vulkan"
|
||||
env_apple_embedded.Prepend(CPPPATH=[vulkan_dir, vulkan_dir + "/include"])
|
||||
|
||||
# Driver source files
|
||||
env_apple_embedded.add_source_files(env.drivers_sources, "*.mm")
|
42
drivers/apple_embedded/app_delegate_service.h
Normal file
42
drivers/apple_embedded/app_delegate_service.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/**************************************************************************/
|
||||
/* app_delegate_service.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@class GDTViewController;
|
||||
|
||||
@interface GDTAppDelegateService : NSObject <UIApplicationDelegate>
|
||||
|
||||
@property(strong, nonatomic) UIWindow *window;
|
||||
@property(strong, class, readonly, nonatomic) GDTViewController *viewController;
|
||||
|
||||
@end
|
189
drivers/apple_embedded/app_delegate_service.mm
Normal file
189
drivers/apple_embedded/app_delegate_service.mm
Normal file
@@ -0,0 +1,189 @@
|
||||
/**************************************************************************/
|
||||
/* app_delegate_service.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "app_delegate_service.h"
|
||||
|
||||
#import "godot_view_apple_embedded.h"
|
||||
#import "os_apple_embedded.h"
|
||||
#import "view_controller.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#import "drivers/coreaudio/audio_driver_coreaudio.h"
|
||||
#include "main/main.h"
|
||||
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <AudioToolbox/AudioServices.h>
|
||||
|
||||
#define kRenderingFrequency 60
|
||||
|
||||
extern int gargc;
|
||||
extern char **gargv;
|
||||
|
||||
extern int apple_embedded_main(int, char **);
|
||||
extern void apple_embedded_finish();
|
||||
|
||||
@implementation GDTAppDelegateService
|
||||
|
||||
enum {
|
||||
SESSION_CATEGORY_AMBIENT,
|
||||
SESSION_CATEGORY_MULTI_ROUTE,
|
||||
SESSION_CATEGORY_PLAY_AND_RECORD,
|
||||
SESSION_CATEGORY_PLAYBACK,
|
||||
SESSION_CATEGORY_RECORD,
|
||||
SESSION_CATEGORY_SOLO_AMBIENT
|
||||
};
|
||||
|
||||
static GDTViewController *mainViewController = nil;
|
||||
|
||||
+ (GDTViewController *)viewController {
|
||||
return mainViewController;
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
||||
// TODO: might be required to make an early return, so app wouldn't crash because of timeout.
|
||||
// TODO: logo screen is not displayed while shaders are compiling
|
||||
// DummyViewController(Splash/LoadingViewController) -> setup -> GodotViewController
|
||||
|
||||
#if !defined(VISIONOS_ENABLED)
|
||||
// Create a full-screen window
|
||||
CGRect windowBounds = [[UIScreen mainScreen] bounds];
|
||||
self.window = [[UIWindow alloc] initWithFrame:windowBounds];
|
||||
#else
|
||||
self.window = [[UIWindow alloc] init];
|
||||
#endif
|
||||
|
||||
int err = apple_embedded_main(gargc, gargv);
|
||||
|
||||
if (err != 0) {
|
||||
// bail, things did not go very well for us, should probably output a message on screen with our error code...
|
||||
exit(0);
|
||||
return NO;
|
||||
}
|
||||
|
||||
GDTViewController *viewController = [[GDTViewController alloc] init];
|
||||
viewController.godotView.useCADisplayLink = bool(GLOBAL_DEF("display.iOS/use_cadisplaylink", true)) ? YES : NO;
|
||||
viewController.godotView.renderingInterval = 1.0 / kRenderingFrequency;
|
||||
|
||||
self.window.rootViewController = viewController;
|
||||
|
||||
// Show the window
|
||||
[self.window makeKeyAndVisible];
|
||||
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
addObserver:self
|
||||
selector:@selector(onAudioInterruption:)
|
||||
name:AVAudioSessionInterruptionNotification
|
||||
object:[AVAudioSession sharedInstance]];
|
||||
|
||||
mainViewController = viewController;
|
||||
|
||||
int sessionCategorySetting = GLOBAL_GET("audio/general/ios/session_category");
|
||||
|
||||
// Initialize with default Ambient category.
|
||||
AVAudioSessionCategory category = AVAudioSessionCategoryAmbient;
|
||||
AVAudioSessionCategoryOptions options = 0;
|
||||
|
||||
if (GLOBAL_GET("audio/general/ios/mix_with_others")) {
|
||||
options |= AVAudioSessionCategoryOptionMixWithOthers;
|
||||
}
|
||||
|
||||
if (sessionCategorySetting == SESSION_CATEGORY_MULTI_ROUTE) {
|
||||
category = AVAudioSessionCategoryMultiRoute;
|
||||
} else if (sessionCategorySetting == SESSION_CATEGORY_PLAY_AND_RECORD) {
|
||||
category = AVAudioSessionCategoryPlayAndRecord;
|
||||
options |= AVAudioSessionCategoryOptionDefaultToSpeaker;
|
||||
options |= AVAudioSessionCategoryOptionAllowBluetoothA2DP;
|
||||
options |= AVAudioSessionCategoryOptionAllowAirPlay;
|
||||
} else if (sessionCategorySetting == SESSION_CATEGORY_PLAYBACK) {
|
||||
category = AVAudioSessionCategoryPlayback;
|
||||
} else if (sessionCategorySetting == SESSION_CATEGORY_RECORD) {
|
||||
category = AVAudioSessionCategoryRecord;
|
||||
} else if (sessionCategorySetting == SESSION_CATEGORY_SOLO_AMBIENT) {
|
||||
category = AVAudioSessionCategorySoloAmbient;
|
||||
}
|
||||
|
||||
[[AVAudioSession sharedInstance] setCategory:category withOptions:options error:nil];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)onAudioInterruption:(NSNotification *)notification {
|
||||
if ([notification.name isEqualToString:AVAudioSessionInterruptionNotification]) {
|
||||
if ([[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] isEqualToNumber:[NSNumber numberWithInt:AVAudioSessionInterruptionTypeBegan]]) {
|
||||
NSLog(@"Audio interruption began");
|
||||
OS_AppleEmbedded::get_singleton()->on_focus_out();
|
||||
} else if ([[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] isEqualToNumber:[NSNumber numberWithInt:AVAudioSessionInterruptionTypeEnded]]) {
|
||||
NSLog(@"Audio interruption ended");
|
||||
OS_AppleEmbedded::get_singleton()->on_focus_in();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
|
||||
if (OS::get_singleton()->get_main_loop()) {
|
||||
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_MEMORY_WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(UIApplication *)application {
|
||||
apple_embedded_finish();
|
||||
}
|
||||
|
||||
// When application goes to background (e.g. user switches to another app or presses Home),
|
||||
// then applicationWillResignActive -> applicationDidEnterBackground are called.
|
||||
// When user opens the inactive app again,
|
||||
// applicationWillEnterForeground -> applicationDidBecomeActive are called.
|
||||
|
||||
// There are cases when applicationWillResignActive -> applicationDidBecomeActive
|
||||
// sequence is called without the app going to background. For example, that happens
|
||||
// if you open the app list without switching to another app or open/close the
|
||||
// notification panel by swiping from the upper part of the screen.
|
||||
|
||||
- (void)applicationWillResignActive:(UIApplication *)application {
|
||||
OS_AppleEmbedded::get_singleton()->on_focus_out();
|
||||
}
|
||||
|
||||
- (void)applicationDidBecomeActive:(UIApplication *)application {
|
||||
OS_AppleEmbedded::get_singleton()->on_focus_in();
|
||||
}
|
||||
|
||||
- (void)applicationDidEnterBackground:(UIApplication *)application {
|
||||
OS_AppleEmbedded::get_singleton()->on_enter_background();
|
||||
}
|
||||
|
||||
- (void)applicationWillEnterForeground:(UIApplication *)application {
|
||||
OS_AppleEmbedded::get_singleton()->on_exit_background();
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
self.window = nil;
|
||||
}
|
||||
|
||||
@end
|
59
drivers/apple_embedded/apple_embedded.h
Normal file
59
drivers/apple_embedded/apple_embedded.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/**************************************************************************/
|
||||
/* apple_embedded.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/object/class_db.h"
|
||||
|
||||
#import <CoreHaptics/CoreHaptics.h>
|
||||
|
||||
class AppleEmbedded : public Object {
|
||||
GDCLASS(AppleEmbedded, Object);
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
private:
|
||||
CHHapticEngine *haptic_engine API_AVAILABLE(ios(13)) = nullptr;
|
||||
|
||||
CHHapticEngine *get_haptic_engine_instance() API_AVAILABLE(ios(13));
|
||||
void start_haptic_engine();
|
||||
void stop_haptic_engine();
|
||||
|
||||
public:
|
||||
static void alert(const char *p_alert, const char *p_title);
|
||||
|
||||
bool supports_haptic_engine();
|
||||
void vibrate_haptic_engine(float p_duration_seconds, float p_amplitude);
|
||||
|
||||
String get_model() const;
|
||||
String get_rate_url(int p_app_id) const;
|
||||
|
||||
AppleEmbedded();
|
||||
};
|
200
drivers/apple_embedded/apple_embedded.mm
Normal file
200
drivers/apple_embedded/apple_embedded.mm
Normal file
@@ -0,0 +1,200 @@
|
||||
/**************************************************************************/
|
||||
/* apple_embedded.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "apple_embedded.h"
|
||||
|
||||
#import "app_delegate_service.h"
|
||||
#import "view_controller.h"
|
||||
|
||||
#import <CoreHaptics/CoreHaptics.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
void AppleEmbedded::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("get_rate_url", "app_id"), &AppleEmbedded::get_rate_url);
|
||||
ClassDB::bind_method(D_METHOD("supports_haptic_engine"), &AppleEmbedded::supports_haptic_engine);
|
||||
ClassDB::bind_method(D_METHOD("start_haptic_engine"), &AppleEmbedded::start_haptic_engine);
|
||||
ClassDB::bind_method(D_METHOD("stop_haptic_engine"), &AppleEmbedded::stop_haptic_engine);
|
||||
}
|
||||
|
||||
bool AppleEmbedded::supports_haptic_engine() {
|
||||
if (@available(iOS 13, *)) {
|
||||
id<CHHapticDeviceCapability> capabilities = [CHHapticEngine capabilitiesForHardware];
|
||||
return capabilities.supportsHaptics;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
CHHapticEngine *AppleEmbedded::get_haptic_engine_instance() API_AVAILABLE(ios(13)) {
|
||||
if (haptic_engine == nullptr) {
|
||||
NSError *error = nullptr;
|
||||
haptic_engine = [[CHHapticEngine alloc] initAndReturnError:&error];
|
||||
|
||||
if (!error) {
|
||||
[haptic_engine setAutoShutdownEnabled:true];
|
||||
} else {
|
||||
haptic_engine = nullptr;
|
||||
NSLog(@"Could not initialize haptic engine: %@", error);
|
||||
}
|
||||
}
|
||||
|
||||
return haptic_engine;
|
||||
}
|
||||
|
||||
void AppleEmbedded::vibrate_haptic_engine(float p_duration_seconds, float p_amplitude) API_AVAILABLE(ios(13)) {
|
||||
if (@available(iOS 13, *)) { // We need the @available check every time to make the compiler happy...
|
||||
if (supports_haptic_engine()) {
|
||||
CHHapticEngine *cur_haptic_engine = get_haptic_engine_instance();
|
||||
if (cur_haptic_engine) {
|
||||
NSDictionary *hapticDict;
|
||||
if (p_amplitude < 0) {
|
||||
hapticDict = @{
|
||||
CHHapticPatternKeyPattern : @[
|
||||
@{CHHapticPatternKeyEvent : @{
|
||||
CHHapticPatternKeyEventType : CHHapticEventTypeHapticContinuous,
|
||||
CHHapticPatternKeyTime : @(CHHapticTimeImmediate),
|
||||
CHHapticPatternKeyEventDuration : @(p_duration_seconds),
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
} else {
|
||||
hapticDict = @{
|
||||
CHHapticPatternKeyPattern : @[
|
||||
@{CHHapticPatternKeyEvent : @{
|
||||
CHHapticPatternKeyEventType : CHHapticEventTypeHapticContinuous,
|
||||
CHHapticPatternKeyTime : @(CHHapticTimeImmediate),
|
||||
CHHapticPatternKeyEventDuration : @(p_duration_seconds),
|
||||
CHHapticPatternKeyEventParameters : @[
|
||||
@{
|
||||
CHHapticPatternKeyParameterID : @("HapticIntensity"),
|
||||
CHHapticPatternKeyParameterValue : @(p_amplitude)
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
NSError *error;
|
||||
CHHapticPattern *pattern = [[CHHapticPattern alloc] initWithDictionary:hapticDict error:&error];
|
||||
|
||||
[[cur_haptic_engine createPlayerWithPattern:pattern error:&error] startAtTime:0 error:&error];
|
||||
|
||||
NSLog(@"Could not vibrate using haptic engine: %@", error);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
NSLog(@"Haptic engine is not supported");
|
||||
}
|
||||
|
||||
void AppleEmbedded::start_haptic_engine() {
|
||||
if (@available(iOS 13, *)) {
|
||||
if (supports_haptic_engine()) {
|
||||
CHHapticEngine *cur_haptic_engine = get_haptic_engine_instance();
|
||||
if (cur_haptic_engine) {
|
||||
[cur_haptic_engine startWithCompletionHandler:^(NSError *returnedError) {
|
||||
if (returnedError) {
|
||||
NSLog(@"Could not start haptic engine: %@", returnedError);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
NSLog(@"Haptic engine is not supported");
|
||||
}
|
||||
|
||||
void AppleEmbedded::stop_haptic_engine() {
|
||||
if (@available(iOS 13, *)) {
|
||||
if (supports_haptic_engine()) {
|
||||
CHHapticEngine *cur_haptic_engine = get_haptic_engine_instance();
|
||||
if (cur_haptic_engine) {
|
||||
[cur_haptic_engine stopWithCompletionHandler:^(NSError *returnedError) {
|
||||
if (returnedError) {
|
||||
NSLog(@"Could not stop haptic engine: %@", returnedError);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
NSLog(@"Haptic engine is not supported");
|
||||
}
|
||||
|
||||
void AppleEmbedded::alert(const char *p_alert, const char *p_title) {
|
||||
NSString *title = [NSString stringWithUTF8String:p_title];
|
||||
NSString *message = [NSString stringWithUTF8String:p_alert];
|
||||
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
|
||||
UIAlertAction *button = [UIAlertAction actionWithTitle:@"OK"
|
||||
style:UIAlertActionStyleCancel
|
||||
handler:^(id){
|
||||
}];
|
||||
|
||||
[alert addAction:button];
|
||||
|
||||
[GDTAppDelegateService.viewController presentViewController:alert animated:YES completion:nil];
|
||||
}
|
||||
|
||||
String AppleEmbedded::get_model() const {
|
||||
// [[UIDevice currentDevice] model] only returns "iPad" or "iPhone".
|
||||
size_t size;
|
||||
sysctlbyname("hw.machine", nullptr, &size, nullptr, 0);
|
||||
char *model = (char *)malloc(size);
|
||||
if (model == nullptr) {
|
||||
return "";
|
||||
}
|
||||
sysctlbyname("hw.machine", model, &size, nullptr, 0);
|
||||
NSString *platform = [NSString stringWithCString:model encoding:NSUTF8StringEncoding];
|
||||
free(model);
|
||||
const char *str = [platform UTF8String];
|
||||
return String::utf8(str != nullptr ? str : "");
|
||||
}
|
||||
|
||||
String AppleEmbedded::get_rate_url(int p_app_id) const {
|
||||
String app_url_path = "itms-apps://itunes.apple.com/app/idAPP_ID";
|
||||
|
||||
String ret = app_url_path.replace("APP_ID", String::num_int64(p_app_id));
|
||||
|
||||
print_verbose(vformat("Returning rate url %s", ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
AppleEmbedded::AppleEmbedded() {}
|
42
drivers/apple_embedded/display_layer_apple_embedded.h
Normal file
42
drivers/apple_embedded/display_layer_apple_embedded.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/**************************************************************************/
|
||||
/* display_layer_apple_embedded.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#import <QuartzCore/CAMetalLayer.h>
|
||||
|
||||
@protocol GDTDisplayLayer <NSObject>
|
||||
|
||||
- (void)startRenderDisplayLayer;
|
||||
- (void)stopRenderDisplayLayer;
|
||||
- (void)initializeDisplayLayer;
|
||||
- (void)layoutDisplayLayer;
|
||||
|
||||
@end
|
233
drivers/apple_embedded/display_server_apple_embedded.h
Normal file
233
drivers/apple_embedded/display_server_apple_embedded.h
Normal file
@@ -0,0 +1,233 @@
|
||||
/**************************************************************************/
|
||||
/* display_server_apple_embedded.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/input/input.h"
|
||||
#include "servers/display_server.h"
|
||||
|
||||
#if defined(RD_ENABLED)
|
||||
#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
|
||||
#include "servers/rendering/rendering_device.h"
|
||||
|
||||
#if defined(VULKAN_ENABLED)
|
||||
#import "rendering_context_driver_vulkan_apple_embedded.h"
|
||||
|
||||
#include "drivers/vulkan/godot_vulkan.h"
|
||||
#endif // VULKAN_ENABLED
|
||||
|
||||
#if defined(METAL_ENABLED)
|
||||
#import "drivers/metal/rendering_context_driver_metal.h"
|
||||
#endif // METAL_ENABLED
|
||||
#endif // RD_ENABLED
|
||||
|
||||
#if defined(GLES3_ENABLED)
|
||||
#include "drivers/gles3/rasterizer_gles3.h"
|
||||
#endif // GLES3_ENABLED
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <QuartzCore/CAMetalLayer.h>
|
||||
|
||||
class DisplayServerAppleEmbedded : public DisplayServer {
|
||||
GDSOFTCLASS(DisplayServerAppleEmbedded, DisplayServer);
|
||||
|
||||
_THREAD_SAFE_CLASS_
|
||||
|
||||
#if defined(RD_ENABLED)
|
||||
RenderingContextDriver *rendering_context = nullptr;
|
||||
RenderingDevice *rendering_device = nullptr;
|
||||
#endif
|
||||
NativeMenu *native_menu = nullptr;
|
||||
|
||||
id tts = nullptr;
|
||||
|
||||
DisplayServer::ScreenOrientation screen_orientation;
|
||||
|
||||
ObjectID window_attached_instance_id;
|
||||
|
||||
Callable window_event_callback;
|
||||
Callable window_resize_callback;
|
||||
Callable input_event_callback;
|
||||
Callable input_text_callback;
|
||||
|
||||
Callable system_theme_changed;
|
||||
|
||||
int virtual_keyboard_height = 0;
|
||||
|
||||
void perform_event(const Ref<InputEvent> &p_event);
|
||||
|
||||
void initialize_tts() const;
|
||||
|
||||
protected:
|
||||
DisplayServerAppleEmbedded(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error);
|
||||
~DisplayServerAppleEmbedded();
|
||||
|
||||
public:
|
||||
String rendering_driver;
|
||||
|
||||
static DisplayServerAppleEmbedded *get_singleton();
|
||||
|
||||
static Vector<String> get_rendering_drivers_func();
|
||||
|
||||
// MARK: - Events
|
||||
|
||||
virtual void process_events() override;
|
||||
|
||||
virtual void window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual void window_set_input_text_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual void window_set_drop_files_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
|
||||
static void _dispatch_input_events(const Ref<InputEvent> &p_event);
|
||||
void send_input_event(const Ref<InputEvent> &p_event) const;
|
||||
void send_input_text(const String &p_text) const;
|
||||
void send_window_event(DisplayServer::WindowEvent p_event) const;
|
||||
void _window_callback(const Callable &p_callable, const Variant &p_arg) const;
|
||||
|
||||
void emit_system_theme_changed();
|
||||
|
||||
// MARK: - Input
|
||||
|
||||
// MARK: Touches and Apple Pencil
|
||||
|
||||
void touch_press(int p_idx, int p_x, int p_y, bool p_pressed, bool p_double_click);
|
||||
void touch_drag(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y, float p_pressure, Vector2 p_tilt);
|
||||
void touches_canceled(int p_idx);
|
||||
|
||||
// MARK: Keyboard
|
||||
|
||||
void key(Key p_key, char32_t p_char, Key p_unshifted, Key p_physical, NSInteger p_modifier, bool p_pressed, KeyLocation p_location);
|
||||
bool is_keyboard_active() const;
|
||||
|
||||
// MARK: Motion
|
||||
|
||||
void update_gravity(const Vector3 &p_gravity);
|
||||
void update_accelerometer(const Vector3 &p_accelerometer);
|
||||
void update_magnetometer(const Vector3 &p_magnetometer);
|
||||
void update_gyroscope(const Vector3 &p_gyroscope);
|
||||
|
||||
// MARK: -
|
||||
|
||||
virtual bool has_feature(Feature p_feature) const override;
|
||||
|
||||
virtual bool tts_is_speaking() const override;
|
||||
virtual bool tts_is_paused() const override;
|
||||
virtual TypedArray<Dictionary> tts_get_voices() const override;
|
||||
|
||||
virtual void tts_speak(const String &p_text, const String &p_voice, int p_volume = 50, float p_pitch = 1.f, float p_rate = 1.f, int p_utterance_id = 0, bool p_interrupt = false) override;
|
||||
virtual void tts_pause() override;
|
||||
virtual void tts_resume() override;
|
||||
virtual void tts_stop() override;
|
||||
|
||||
virtual bool is_dark_mode_supported() const override;
|
||||
virtual bool is_dark_mode() const override;
|
||||
virtual void set_system_theme_change_callback(const Callable &p_callable) override;
|
||||
|
||||
virtual Rect2i get_display_safe_area() const override;
|
||||
|
||||
virtual int get_screen_count() const override;
|
||||
virtual int get_primary_screen() const override;
|
||||
virtual Point2i screen_get_position(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
|
||||
virtual Size2i screen_get_size(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
|
||||
virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
|
||||
|
||||
virtual Vector<DisplayServer::WindowID> get_window_list() const override;
|
||||
|
||||
virtual WindowID get_window_at_screen_position(const Point2i &p_position) const override;
|
||||
|
||||
virtual int64_t window_get_native_handle(HandleType p_handle_type, WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual void window_attach_instance_id(ObjectID p_instance, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual ObjectID window_get_attached_instance_id(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
|
||||
virtual int window_get_current_screen(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
virtual void window_set_current_screen(int p_screen, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
|
||||
virtual Point2i window_get_position(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
virtual Point2i window_get_position_with_decorations(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
virtual void window_set_position(const Point2i &p_position, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
|
||||
virtual void window_set_transient(WindowID p_window, WindowID p_parent) override;
|
||||
|
||||
virtual void window_set_max_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual Size2i window_get_max_size(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual void window_set_min_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual Size2i window_get_min_size(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual void window_set_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual Size2i window_get_size(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
virtual Size2i window_get_size_with_decorations(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual void window_set_mode(WindowMode p_mode, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual WindowMode window_get_mode(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual bool window_is_maximize_allowed(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual void window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual bool window_get_flag(WindowFlags p_flag, WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual void window_request_attention(WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual void window_move_to_foreground(WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual bool window_is_focused(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual float screen_get_max_scale() const override;
|
||||
|
||||
virtual void screen_set_orientation(DisplayServer::ScreenOrientation p_orientation, int p_screen) override;
|
||||
virtual DisplayServer::ScreenOrientation screen_get_orientation(int p_screen) const override;
|
||||
|
||||
virtual bool window_can_draw(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual bool can_any_window_draw() const override;
|
||||
|
||||
virtual void window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual DisplayServer::VSyncMode window_get_vsync_mode(WindowID p_vsync_mode) const override;
|
||||
|
||||
virtual bool is_touchscreen_available() const override;
|
||||
|
||||
virtual void virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect, VirtualKeyboardType p_type, int p_max_length, int p_cursor_start, int p_cursor_end) override;
|
||||
virtual void virtual_keyboard_hide() override;
|
||||
|
||||
void virtual_keyboard_set_height(int height);
|
||||
virtual int virtual_keyboard_get_height() const override;
|
||||
virtual bool has_hardware_keyboard() const override;
|
||||
|
||||
virtual void clipboard_set(const String &p_text) override;
|
||||
virtual String clipboard_get() const override;
|
||||
|
||||
virtual void screen_set_keep_on(bool p_enable) override;
|
||||
virtual bool screen_is_kept_on() const override;
|
||||
|
||||
void resize_window(CGSize size);
|
||||
virtual void swap_buffers() override {}
|
||||
};
|
820
drivers/apple_embedded/display_server_apple_embedded.mm
Normal file
820
drivers/apple_embedded/display_server_apple_embedded.mm
Normal file
@@ -0,0 +1,820 @@
|
||||
/**************************************************************************/
|
||||
/* display_server_apple_embedded.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "display_server_apple_embedded.h"
|
||||
|
||||
#import "app_delegate_service.h"
|
||||
#import "apple_embedded.h"
|
||||
#import "godot_view_apple_embedded.h"
|
||||
#import "key_mapping_apple_embedded.h"
|
||||
#import "keyboard_input_view.h"
|
||||
#import "os_apple_embedded.h"
|
||||
#import "tts_apple_embedded.h"
|
||||
#import "view_controller.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/io/file_access_pack.h"
|
||||
|
||||
#import <GameController/GameController.h>
|
||||
|
||||
static const float kDisplayServerIOSAcceleration = 1.f;
|
||||
|
||||
DisplayServerAppleEmbedded *DisplayServerAppleEmbedded::get_singleton() {
|
||||
return (DisplayServerAppleEmbedded *)DisplayServer::get_singleton();
|
||||
}
|
||||
|
||||
DisplayServerAppleEmbedded::DisplayServerAppleEmbedded(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error) {
|
||||
KeyMappingAppleEmbedded::initialize();
|
||||
|
||||
rendering_driver = p_rendering_driver;
|
||||
|
||||
// Init TTS
|
||||
bool tts_enabled = GLOBAL_GET("audio/general/text_to_speech");
|
||||
if (tts_enabled) {
|
||||
initialize_tts();
|
||||
}
|
||||
native_menu = memnew(NativeMenu);
|
||||
|
||||
bool has_made_render_compositor_current = false;
|
||||
|
||||
#if defined(RD_ENABLED)
|
||||
rendering_context = nullptr;
|
||||
rendering_device = nullptr;
|
||||
|
||||
CALayer *layer = nullptr;
|
||||
|
||||
union {
|
||||
#ifdef VULKAN_ENABLED
|
||||
RenderingContextDriverVulkanAppleEmbedded::WindowPlatformData vulkan;
|
||||
#endif
|
||||
#ifdef METAL_ENABLED
|
||||
GODOT_CLANG_WARNING_PUSH_AND_IGNORE("-Wunguarded-availability")
|
||||
// Eliminate "RenderingContextDriverMetal is only available on iOS 14.0 or newer".
|
||||
RenderingContextDriverMetal::WindowPlatformData metal;
|
||||
GODOT_CLANG_WARNING_POP
|
||||
#endif
|
||||
} wpd;
|
||||
|
||||
#if defined(VULKAN_ENABLED)
|
||||
if (rendering_driver == "vulkan") {
|
||||
layer = [GDTAppDelegateService.viewController.godotView initializeRenderingForDriver:@"vulkan"];
|
||||
if (!layer) {
|
||||
ERR_FAIL_MSG("Failed to create iOS Vulkan rendering layer.");
|
||||
}
|
||||
wpd.vulkan.layer_ptr = (CAMetalLayer *const *)&layer;
|
||||
rendering_context = memnew(RenderingContextDriverVulkanAppleEmbedded);
|
||||
}
|
||||
#endif
|
||||
#ifdef METAL_ENABLED
|
||||
if (rendering_driver == "metal") {
|
||||
if (@available(iOS 14.0, *)) {
|
||||
layer = [GDTAppDelegateService.viewController.godotView initializeRenderingForDriver:@"metal"];
|
||||
wpd.metal.layer = (CAMetalLayer *)layer;
|
||||
rendering_context = memnew(RenderingContextDriverMetal);
|
||||
} else {
|
||||
OS::get_singleton()->alert("Metal is only supported on iOS 14.0 and later.");
|
||||
r_error = ERR_UNAVAILABLE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (rendering_context) {
|
||||
if (rendering_context->initialize() != OK) {
|
||||
memdelete(rendering_context);
|
||||
rendering_context = nullptr;
|
||||
#if defined(GLES3_ENABLED)
|
||||
bool fallback_to_opengl3 = GLOBAL_GET("rendering/rendering_device/fallback_to_opengl3");
|
||||
if (fallback_to_opengl3 && rendering_driver != "opengl3") {
|
||||
WARN_PRINT("Your device does not seem to support MoltenVK or Metal, switching to OpenGL 3.");
|
||||
rendering_driver = "opengl3";
|
||||
OS::get_singleton()->set_current_rendering_method("gl_compatibility");
|
||||
OS::get_singleton()->set_current_rendering_driver_name(rendering_driver);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
ERR_PRINT(vformat("Failed to initialize %s context", rendering_driver));
|
||||
r_error = ERR_UNAVAILABLE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rendering_context) {
|
||||
if (rendering_context->window_create(MAIN_WINDOW_ID, &wpd) != OK) {
|
||||
ERR_PRINT(vformat("Failed to create %s window.", rendering_driver));
|
||||
memdelete(rendering_context);
|
||||
rendering_context = nullptr;
|
||||
r_error = ERR_UNAVAILABLE;
|
||||
return;
|
||||
}
|
||||
|
||||
Size2i size = Size2i(layer.bounds.size.width, layer.bounds.size.height) * screen_get_max_scale();
|
||||
rendering_context->window_set_size(MAIN_WINDOW_ID, size.width, size.height);
|
||||
rendering_context->window_set_vsync_mode(MAIN_WINDOW_ID, p_vsync_mode);
|
||||
|
||||
rendering_device = memnew(RenderingDevice);
|
||||
if (rendering_device->initialize(rendering_context, MAIN_WINDOW_ID) != OK) {
|
||||
rendering_device = nullptr;
|
||||
memdelete(rendering_context);
|
||||
rendering_context = nullptr;
|
||||
r_error = ERR_UNAVAILABLE;
|
||||
return;
|
||||
}
|
||||
rendering_device->screen_create(MAIN_WINDOW_ID);
|
||||
|
||||
RendererCompositorRD::make_current();
|
||||
has_made_render_compositor_current = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(GLES3_ENABLED)
|
||||
if (rendering_driver == "opengl3") {
|
||||
CALayer *layer = [GDTAppDelegateService.viewController.godotView initializeRenderingForDriver:@"opengl3"];
|
||||
|
||||
if (!layer) {
|
||||
ERR_FAIL_MSG("Failed to create iOS OpenGLES rendering layer.");
|
||||
}
|
||||
|
||||
RasterizerGLES3::make_current(false);
|
||||
has_made_render_compositor_current = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
ERR_FAIL_COND_MSG(!has_made_render_compositor_current, vformat("Failed to make RendererCompositor current for rendering driver %s", rendering_driver));
|
||||
|
||||
bool keep_screen_on = bool(GLOBAL_GET("display/window/energy_saving/keep_screen_on"));
|
||||
screen_set_keep_on(keep_screen_on);
|
||||
|
||||
Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events);
|
||||
|
||||
r_error = OK;
|
||||
}
|
||||
|
||||
DisplayServerAppleEmbedded::~DisplayServerAppleEmbedded() {
|
||||
if (native_menu) {
|
||||
memdelete(native_menu);
|
||||
native_menu = nullptr;
|
||||
}
|
||||
|
||||
#if defined(RD_ENABLED)
|
||||
if (rendering_device) {
|
||||
rendering_device->screen_free(MAIN_WINDOW_ID);
|
||||
memdelete(rendering_device);
|
||||
rendering_device = nullptr;
|
||||
}
|
||||
|
||||
if (rendering_context) {
|
||||
rendering_context->window_destroy(MAIN_WINDOW_ID);
|
||||
memdelete(rendering_context);
|
||||
rendering_context = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Vector<String> DisplayServerAppleEmbedded::get_rendering_drivers_func() {
|
||||
Vector<String> drivers;
|
||||
|
||||
#if defined(VULKAN_ENABLED)
|
||||
drivers.push_back("vulkan");
|
||||
#endif
|
||||
#if defined(METAL_ENABLED)
|
||||
if (@available(ios 14.0, *)) {
|
||||
drivers.push_back("metal");
|
||||
}
|
||||
#endif
|
||||
#if defined(GLES3_ENABLED)
|
||||
drivers.push_back("opengl3");
|
||||
#endif
|
||||
|
||||
return drivers;
|
||||
}
|
||||
|
||||
// MARK: Events
|
||||
|
||||
void DisplayServerAppleEmbedded::window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window) {
|
||||
window_resize_callback = p_callable;
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::window_set_window_event_callback(const Callable &p_callable, WindowID p_window) {
|
||||
window_event_callback = p_callable;
|
||||
}
|
||||
void DisplayServerAppleEmbedded::window_set_input_event_callback(const Callable &p_callable, WindowID p_window) {
|
||||
input_event_callback = p_callable;
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::window_set_input_text_callback(const Callable &p_callable, WindowID p_window) {
|
||||
input_text_callback = p_callable;
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::window_set_drop_files_callback(const Callable &p_callable, WindowID p_window) {
|
||||
// Probably not supported for iOS
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::process_events() {
|
||||
Input::get_singleton()->flush_buffered_events();
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::_dispatch_input_events(const Ref<InputEvent> &p_event) {
|
||||
DisplayServerAppleEmbedded::get_singleton()->send_input_event(p_event);
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::send_input_event(const Ref<InputEvent> &p_event) const {
|
||||
_window_callback(input_event_callback, p_event);
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::send_input_text(const String &p_text) const {
|
||||
_window_callback(input_text_callback, p_text);
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::send_window_event(DisplayServer::WindowEvent p_event) const {
|
||||
_window_callback(window_event_callback, int(p_event));
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::_window_callback(const Callable &p_callable, const Variant &p_arg) const {
|
||||
if (p_callable.is_valid()) {
|
||||
p_callable.call(p_arg);
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Input
|
||||
|
||||
// MARK: Touches
|
||||
|
||||
void DisplayServerAppleEmbedded::touch_press(int p_idx, int p_x, int p_y, bool p_pressed, bool p_double_click) {
|
||||
Ref<InputEventScreenTouch> ev;
|
||||
ev.instantiate();
|
||||
|
||||
ev->set_index(p_idx);
|
||||
ev->set_pressed(p_pressed);
|
||||
ev->set_position(Vector2(p_x, p_y));
|
||||
ev->set_double_tap(p_double_click);
|
||||
perform_event(ev);
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::touch_drag(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y, float p_pressure, Vector2 p_tilt) {
|
||||
Ref<InputEventScreenDrag> ev;
|
||||
ev.instantiate();
|
||||
ev->set_index(p_idx);
|
||||
ev->set_pressure(p_pressure);
|
||||
ev->set_tilt(p_tilt);
|
||||
ev->set_position(Vector2(p_x, p_y));
|
||||
ev->set_relative(Vector2(p_x - p_prev_x, p_y - p_prev_y));
|
||||
ev->set_relative_screen_position(ev->get_relative());
|
||||
perform_event(ev);
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::perform_event(const Ref<InputEvent> &p_event) {
|
||||
Input *input_singleton = Input::get_singleton();
|
||||
if (input_singleton == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
input_singleton->parse_input_event(p_event);
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::touches_canceled(int p_idx) {
|
||||
touch_press(p_idx, -1, -1, false, false);
|
||||
}
|
||||
|
||||
// MARK: Keyboard
|
||||
|
||||
void DisplayServerAppleEmbedded::key(Key p_key, char32_t p_char, Key p_unshifted, Key p_physical, NSInteger p_modifier, bool p_pressed, KeyLocation p_location) {
|
||||
Ref<InputEventKey> ev;
|
||||
ev.instantiate();
|
||||
ev->set_echo(false);
|
||||
ev->set_pressed(p_pressed);
|
||||
ev->set_keycode(fix_keycode(p_char, p_key));
|
||||
if (@available(iOS 13.4, *)) {
|
||||
if (p_key != Key::SHIFT) {
|
||||
ev->set_shift_pressed(p_modifier & UIKeyModifierShift);
|
||||
}
|
||||
if (p_key != Key::CTRL) {
|
||||
ev->set_ctrl_pressed(p_modifier & UIKeyModifierControl);
|
||||
}
|
||||
if (p_key != Key::ALT) {
|
||||
ev->set_alt_pressed(p_modifier & UIKeyModifierAlternate);
|
||||
}
|
||||
if (p_key != Key::META) {
|
||||
ev->set_meta_pressed(p_modifier & UIKeyModifierCommand);
|
||||
}
|
||||
}
|
||||
ev->set_key_label(p_unshifted);
|
||||
ev->set_physical_keycode(p_physical);
|
||||
ev->set_unicode(fix_unicode(p_char));
|
||||
ev->set_location(p_location);
|
||||
perform_event(ev);
|
||||
}
|
||||
|
||||
// MARK: Motion
|
||||
|
||||
void DisplayServerAppleEmbedded::update_gravity(const Vector3 &p_gravity) {
|
||||
Input::get_singleton()->set_gravity(p_gravity);
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::update_accelerometer(const Vector3 &p_accelerometer) {
|
||||
Input::get_singleton()->set_accelerometer(p_accelerometer / kDisplayServerIOSAcceleration);
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::update_magnetometer(const Vector3 &p_magnetometer) {
|
||||
Input::get_singleton()->set_magnetometer(p_magnetometer);
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::update_gyroscope(const Vector3 &p_gyroscope) {
|
||||
Input::get_singleton()->set_gyroscope(p_gyroscope);
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
bool DisplayServerAppleEmbedded::has_feature(Feature p_feature) const {
|
||||
switch (p_feature) {
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
case FEATURE_GLOBAL_MENU: {
|
||||
return (native_menu && native_menu->has_feature(NativeMenu::FEATURE_GLOBAL_MENU));
|
||||
} break;
|
||||
#endif
|
||||
// case FEATURE_CURSOR_SHAPE:
|
||||
// case FEATURE_CUSTOM_CURSOR_SHAPE:
|
||||
// case FEATURE_HIDPI:
|
||||
// case FEATURE_ICON:
|
||||
// case FEATURE_IME:
|
||||
// case FEATURE_MOUSE:
|
||||
// case FEATURE_MOUSE_WARP:
|
||||
// case FEATURE_NATIVE_DIALOG:
|
||||
// case FEATURE_NATIVE_DIALOG_INPUT:
|
||||
// case FEATURE_NATIVE_DIALOG_FILE:
|
||||
// case FEATURE_NATIVE_DIALOG_FILE_EXTRA:
|
||||
// case FEATURE_NATIVE_DIALOG_FILE_MIME:
|
||||
// case FEATURE_NATIVE_ICON:
|
||||
// case FEATURE_WINDOW_TRANSPARENCY:
|
||||
case FEATURE_CLIPBOARD:
|
||||
case FEATURE_KEEP_SCREEN_ON:
|
||||
case FEATURE_ORIENTATION:
|
||||
case FEATURE_TOUCHSCREEN:
|
||||
case FEATURE_VIRTUAL_KEYBOARD:
|
||||
case FEATURE_TEXT_TO_SPEECH:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::initialize_tts() const {
|
||||
const_cast<DisplayServerAppleEmbedded *>(this)->tts = [[GDTTTS alloc] init];
|
||||
}
|
||||
|
||||
bool DisplayServerAppleEmbedded::tts_is_speaking() const {
|
||||
if (unlikely(!tts)) {
|
||||
initialize_tts();
|
||||
}
|
||||
ERR_FAIL_NULL_V(tts, false);
|
||||
return [tts isSpeaking];
|
||||
}
|
||||
|
||||
bool DisplayServerAppleEmbedded::tts_is_paused() const {
|
||||
if (unlikely(!tts)) {
|
||||
initialize_tts();
|
||||
}
|
||||
ERR_FAIL_NULL_V(tts, false);
|
||||
return [tts isPaused];
|
||||
}
|
||||
|
||||
TypedArray<Dictionary> DisplayServerAppleEmbedded::tts_get_voices() const {
|
||||
if (unlikely(!tts)) {
|
||||
initialize_tts();
|
||||
}
|
||||
ERR_FAIL_NULL_V(tts, TypedArray<Dictionary>());
|
||||
return [tts getVoices];
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::tts_speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int p_utterance_id, bool p_interrupt) {
|
||||
if (unlikely(!tts)) {
|
||||
initialize_tts();
|
||||
}
|
||||
ERR_FAIL_NULL(tts);
|
||||
[tts speak:p_text voice:p_voice volume:p_volume pitch:p_pitch rate:p_rate utterance_id:p_utterance_id interrupt:p_interrupt];
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::tts_pause() {
|
||||
if (unlikely(!tts)) {
|
||||
initialize_tts();
|
||||
}
|
||||
ERR_FAIL_NULL(tts);
|
||||
[tts pauseSpeaking];
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::tts_resume() {
|
||||
if (unlikely(!tts)) {
|
||||
initialize_tts();
|
||||
}
|
||||
ERR_FAIL_NULL(tts);
|
||||
[tts resumeSpeaking];
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::tts_stop() {
|
||||
if (unlikely(!tts)) {
|
||||
initialize_tts();
|
||||
}
|
||||
ERR_FAIL_NULL(tts);
|
||||
[tts stopSpeaking];
|
||||
}
|
||||
|
||||
bool DisplayServerAppleEmbedded::is_dark_mode_supported() const {
|
||||
if (@available(iOS 13.0, *)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool DisplayServerAppleEmbedded::is_dark_mode() const {
|
||||
if (@available(iOS 13.0, *)) {
|
||||
return [UITraitCollection currentTraitCollection].userInterfaceStyle == UIUserInterfaceStyleDark;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::set_system_theme_change_callback(const Callable &p_callable) {
|
||||
system_theme_changed = p_callable;
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::emit_system_theme_changed() {
|
||||
if (system_theme_changed.is_valid()) {
|
||||
Variant ret;
|
||||
Callable::CallError ce;
|
||||
system_theme_changed.callp(nullptr, 0, ret, ce);
|
||||
if (ce.error != Callable::CallError::CALL_OK) {
|
||||
ERR_PRINT(vformat("Failed to execute system theme changed callback: %s.", Variant::get_callable_error_text(system_theme_changed, nullptr, 0, ce)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rect2i DisplayServerAppleEmbedded::get_display_safe_area() const {
|
||||
UIEdgeInsets insets = UIEdgeInsetsZero;
|
||||
UIView *view = GDTAppDelegateService.viewController.godotView;
|
||||
if ([view respondsToSelector:@selector(safeAreaInsets)]) {
|
||||
insets = [view safeAreaInsets];
|
||||
}
|
||||
float scale = screen_get_scale();
|
||||
Size2i insets_position = Size2i(insets.left, insets.top) * scale;
|
||||
Size2i insets_size = Size2i(insets.left + insets.right, insets.top + insets.bottom) * scale;
|
||||
return Rect2i(screen_get_position() + insets_position, screen_get_size() - insets_size);
|
||||
}
|
||||
|
||||
int DisplayServerAppleEmbedded::get_screen_count() const {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int DisplayServerAppleEmbedded::get_primary_screen() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Point2i DisplayServerAppleEmbedded::screen_get_position(int p_screen) const {
|
||||
p_screen = _get_screen_index(p_screen);
|
||||
int screen_count = get_screen_count();
|
||||
ERR_FAIL_INDEX_V(p_screen, screen_count, Point2i());
|
||||
|
||||
return Point2i(0, 0);
|
||||
}
|
||||
|
||||
Size2i DisplayServerAppleEmbedded::screen_get_size(int p_screen) const {
|
||||
p_screen = _get_screen_index(p_screen);
|
||||
int screen_count = get_screen_count();
|
||||
ERR_FAIL_INDEX_V(p_screen, screen_count, Size2i());
|
||||
|
||||
CALayer *layer = GDTAppDelegateService.viewController.godotView.renderingLayer;
|
||||
|
||||
if (!layer) {
|
||||
return Size2i();
|
||||
}
|
||||
|
||||
return Size2i(layer.bounds.size.width, layer.bounds.size.height) * screen_get_scale(p_screen);
|
||||
}
|
||||
|
||||
Rect2i DisplayServerAppleEmbedded::screen_get_usable_rect(int p_screen) const {
|
||||
p_screen = _get_screen_index(p_screen);
|
||||
int screen_count = get_screen_count();
|
||||
ERR_FAIL_INDEX_V(p_screen, screen_count, Rect2i());
|
||||
|
||||
return Rect2i(screen_get_position(p_screen), screen_get_size(p_screen));
|
||||
}
|
||||
|
||||
Vector<DisplayServer::WindowID> DisplayServerAppleEmbedded::get_window_list() const {
|
||||
Vector<DisplayServer::WindowID> list;
|
||||
list.push_back(MAIN_WINDOW_ID);
|
||||
return list;
|
||||
}
|
||||
|
||||
DisplayServer::WindowID DisplayServerAppleEmbedded::get_window_at_screen_position(const Point2i &p_position) const {
|
||||
return MAIN_WINDOW_ID;
|
||||
}
|
||||
|
||||
int64_t DisplayServerAppleEmbedded::window_get_native_handle(HandleType p_handle_type, WindowID p_window) const {
|
||||
ERR_FAIL_COND_V(p_window != MAIN_WINDOW_ID, 0);
|
||||
switch (p_handle_type) {
|
||||
case DISPLAY_HANDLE: {
|
||||
return 0; // Not supported.
|
||||
}
|
||||
case WINDOW_HANDLE: {
|
||||
return (int64_t)GDTAppDelegateService.viewController;
|
||||
}
|
||||
case WINDOW_VIEW: {
|
||||
return (int64_t)GDTAppDelegateService.viewController.godotView;
|
||||
}
|
||||
default: {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::window_attach_instance_id(ObjectID p_instance, WindowID p_window) {
|
||||
window_attached_instance_id = p_instance;
|
||||
}
|
||||
|
||||
ObjectID DisplayServerAppleEmbedded::window_get_attached_instance_id(WindowID p_window) const {
|
||||
return window_attached_instance_id;
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::window_set_title(const String &p_title, WindowID p_window) {
|
||||
// Probably not supported for iOS
|
||||
}
|
||||
|
||||
int DisplayServerAppleEmbedded::window_get_current_screen(WindowID p_window) const {
|
||||
ERR_FAIL_COND_V(p_window != MAIN_WINDOW_ID, INVALID_SCREEN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::window_set_current_screen(int p_screen, WindowID p_window) {
|
||||
// Probably not supported for iOS
|
||||
}
|
||||
|
||||
Point2i DisplayServerAppleEmbedded::window_get_position(WindowID p_window) const {
|
||||
return Point2i();
|
||||
}
|
||||
|
||||
Point2i DisplayServerAppleEmbedded::window_get_position_with_decorations(WindowID p_window) const {
|
||||
return Point2i();
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::window_set_position(const Point2i &p_position, WindowID p_window) {
|
||||
// Probably not supported for single window iOS app
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::window_set_transient(WindowID p_window, WindowID p_parent) {
|
||||
// Probably not supported for iOS
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::window_set_max_size(const Size2i p_size, WindowID p_window) {
|
||||
// Probably not supported for iOS
|
||||
}
|
||||
|
||||
Size2i DisplayServerAppleEmbedded::window_get_max_size(WindowID p_window) const {
|
||||
return Size2i();
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::window_set_min_size(const Size2i p_size, WindowID p_window) {
|
||||
// Probably not supported for iOS
|
||||
}
|
||||
|
||||
Size2i DisplayServerAppleEmbedded::window_get_min_size(WindowID p_window) const {
|
||||
return Size2i();
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::window_set_size(const Size2i p_size, WindowID p_window) {
|
||||
// Probably not supported for iOS
|
||||
}
|
||||
|
||||
Size2i DisplayServerAppleEmbedded::window_get_size(WindowID p_window) const {
|
||||
id<UIApplicationDelegate> appDelegate = [[UIApplication sharedApplication] delegate];
|
||||
CGRect windowBounds = appDelegate.window.bounds;
|
||||
return Size2i(windowBounds.size.width, windowBounds.size.height) * screen_get_max_scale();
|
||||
}
|
||||
|
||||
Size2i DisplayServerAppleEmbedded::window_get_size_with_decorations(WindowID p_window) const {
|
||||
return window_get_size(p_window);
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::window_set_mode(WindowMode p_mode, WindowID p_window) {
|
||||
// Probably not supported for iOS
|
||||
}
|
||||
|
||||
DisplayServer::WindowMode DisplayServerAppleEmbedded::window_get_mode(WindowID p_window) const {
|
||||
return WindowMode::WINDOW_MODE_FULLSCREEN;
|
||||
}
|
||||
|
||||
bool DisplayServerAppleEmbedded::window_is_maximize_allowed(WindowID p_window) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window) {
|
||||
// Probably not supported for iOS
|
||||
}
|
||||
|
||||
bool DisplayServerAppleEmbedded::window_get_flag(WindowFlags p_flag, WindowID p_window) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::window_request_attention(WindowID p_window) {
|
||||
// Probably not supported for iOS
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::window_move_to_foreground(WindowID p_window) {
|
||||
// Probably not supported for iOS
|
||||
}
|
||||
|
||||
bool DisplayServerAppleEmbedded::window_is_focused(WindowID p_window) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
float DisplayServerAppleEmbedded::screen_get_max_scale() const {
|
||||
return screen_get_scale(SCREEN_OF_MAIN_WINDOW);
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::screen_set_orientation(DisplayServer::ScreenOrientation p_orientation, int p_screen) {
|
||||
p_screen = _get_screen_index(p_screen);
|
||||
int screen_count = get_screen_count();
|
||||
ERR_FAIL_INDEX(p_screen, screen_count);
|
||||
|
||||
screen_orientation = p_orientation;
|
||||
if (@available(iOS 16.0, *)) {
|
||||
[GDTAppDelegateService.viewController setNeedsUpdateOfSupportedInterfaceOrientations];
|
||||
}
|
||||
#if !defined(VISIONOS_ENABLED)
|
||||
else {
|
||||
[UIViewController attemptRotationToDeviceOrientation];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
DisplayServer::ScreenOrientation DisplayServerAppleEmbedded::screen_get_orientation(int p_screen) const {
|
||||
p_screen = _get_screen_index(p_screen);
|
||||
int screen_count = get_screen_count();
|
||||
ERR_FAIL_INDEX_V(p_screen, screen_count, SCREEN_LANDSCAPE);
|
||||
|
||||
return screen_orientation;
|
||||
}
|
||||
|
||||
bool DisplayServerAppleEmbedded::window_can_draw(WindowID p_window) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DisplayServerAppleEmbedded::can_any_window_draw() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DisplayServerAppleEmbedded::is_touchscreen_available() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ int _convert_utf32_offset_to_utf16(const String &p_existing_text, int p_pos) {
|
||||
int limit = p_pos;
|
||||
for (int i = 0; i < MIN(p_existing_text.length(), p_pos); i++) {
|
||||
if (p_existing_text[i] > 0xffff) {
|
||||
limit++;
|
||||
}
|
||||
}
|
||||
return limit;
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect, VirtualKeyboardType p_type, int p_max_length, int p_cursor_start, int p_cursor_end) {
|
||||
NSString *existingString = [[NSString alloc] initWithUTF8String:p_existing_text.utf8().get_data()];
|
||||
|
||||
GDTAppDelegateService.viewController.keyboardView.keyboardType = UIKeyboardTypeDefault;
|
||||
GDTAppDelegateService.viewController.keyboardView.textContentType = nil;
|
||||
switch (p_type) {
|
||||
case KEYBOARD_TYPE_DEFAULT: {
|
||||
GDTAppDelegateService.viewController.keyboardView.keyboardType = UIKeyboardTypeDefault;
|
||||
} break;
|
||||
case KEYBOARD_TYPE_MULTILINE: {
|
||||
GDTAppDelegateService.viewController.keyboardView.keyboardType = UIKeyboardTypeDefault;
|
||||
} break;
|
||||
case KEYBOARD_TYPE_NUMBER: {
|
||||
GDTAppDelegateService.viewController.keyboardView.keyboardType = UIKeyboardTypeNumberPad;
|
||||
} break;
|
||||
case KEYBOARD_TYPE_NUMBER_DECIMAL: {
|
||||
GDTAppDelegateService.viewController.keyboardView.keyboardType = UIKeyboardTypeDecimalPad;
|
||||
} break;
|
||||
case KEYBOARD_TYPE_PHONE: {
|
||||
GDTAppDelegateService.viewController.keyboardView.keyboardType = UIKeyboardTypePhonePad;
|
||||
GDTAppDelegateService.viewController.keyboardView.textContentType = UITextContentTypeTelephoneNumber;
|
||||
} break;
|
||||
case KEYBOARD_TYPE_EMAIL_ADDRESS: {
|
||||
GDTAppDelegateService.viewController.keyboardView.keyboardType = UIKeyboardTypeEmailAddress;
|
||||
GDTAppDelegateService.viewController.keyboardView.textContentType = UITextContentTypeEmailAddress;
|
||||
} break;
|
||||
case KEYBOARD_TYPE_PASSWORD: {
|
||||
GDTAppDelegateService.viewController.keyboardView.keyboardType = UIKeyboardTypeDefault;
|
||||
GDTAppDelegateService.viewController.keyboardView.textContentType = UITextContentTypePassword;
|
||||
} break;
|
||||
case KEYBOARD_TYPE_URL: {
|
||||
GDTAppDelegateService.viewController.keyboardView.keyboardType = UIKeyboardTypeWebSearch;
|
||||
GDTAppDelegateService.viewController.keyboardView.textContentType = UITextContentTypeURL;
|
||||
} break;
|
||||
}
|
||||
|
||||
[GDTAppDelegateService.viewController.keyboardView
|
||||
becomeFirstResponderWithString:existingString
|
||||
cursorStart:_convert_utf32_offset_to_utf16(p_existing_text, p_cursor_start)
|
||||
cursorEnd:_convert_utf32_offset_to_utf16(p_existing_text, p_cursor_end)];
|
||||
}
|
||||
|
||||
bool DisplayServerAppleEmbedded::is_keyboard_active() const {
|
||||
return [GDTAppDelegateService.viewController.keyboardView isFirstResponder];
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::virtual_keyboard_hide() {
|
||||
[GDTAppDelegateService.viewController.keyboardView resignFirstResponder];
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::virtual_keyboard_set_height(int height) {
|
||||
virtual_keyboard_height = height * screen_get_max_scale();
|
||||
}
|
||||
|
||||
int DisplayServerAppleEmbedded::virtual_keyboard_get_height() const {
|
||||
return virtual_keyboard_height;
|
||||
}
|
||||
|
||||
bool DisplayServerAppleEmbedded::has_hardware_keyboard() const {
|
||||
if (@available(iOS 14.0, *)) {
|
||||
return [GCKeyboard coalescedKeyboard];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::clipboard_set(const String &p_text) {
|
||||
[UIPasteboard generalPasteboard].string = [NSString stringWithUTF8String:p_text.utf8().get_data()];
|
||||
}
|
||||
|
||||
String DisplayServerAppleEmbedded::clipboard_get() const {
|
||||
NSString *text = [UIPasteboard generalPasteboard].string;
|
||||
|
||||
return String::utf8([text UTF8String]);
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::screen_set_keep_on(bool p_enable) {
|
||||
[UIApplication sharedApplication].idleTimerDisabled = p_enable;
|
||||
}
|
||||
|
||||
bool DisplayServerAppleEmbedded::screen_is_kept_on() const {
|
||||
return [UIApplication sharedApplication].idleTimerDisabled;
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::resize_window(CGSize viewSize) {
|
||||
Size2i size = Size2i(viewSize.width, viewSize.height) * screen_get_max_scale();
|
||||
|
||||
#if defined(RD_ENABLED)
|
||||
if (rendering_context) {
|
||||
rendering_context->window_set_size(MAIN_WINDOW_ID, size.x, size.y);
|
||||
}
|
||||
#endif
|
||||
|
||||
Variant resize_rect = Rect2i(Point2i(), size);
|
||||
_window_callback(window_resize_callback, resize_rect);
|
||||
}
|
||||
|
||||
void DisplayServerAppleEmbedded::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) {
|
||||
_THREAD_SAFE_METHOD_
|
||||
#if defined(RD_ENABLED)
|
||||
if (rendering_context) {
|
||||
rendering_context->window_set_vsync_mode(p_window, p_vsync_mode);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
DisplayServer::VSyncMode DisplayServerAppleEmbedded::window_get_vsync_mode(WindowID p_window) const {
|
||||
_THREAD_SAFE_METHOD_
|
||||
#if defined(RD_ENABLED)
|
||||
if (rendering_context) {
|
||||
return rendering_context->window_get_vsync_mode(p_window);
|
||||
}
|
||||
#endif
|
||||
return DisplayServer::VSYNC_ENABLED;
|
||||
}
|
43
drivers/apple_embedded/godot_app_delegate.h
Normal file
43
drivers/apple_embedded/godot_app_delegate.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/**************************************************************************/
|
||||
/* godot_app_delegate.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
typedef NSObject<UIApplicationDelegate> GDTAppDelegateServiceProtocol;
|
||||
|
||||
@interface GDTApplicationDelegate : NSObject <UIApplicationDelegate>
|
||||
|
||||
@property(class, readonly, strong) NSArray<GDTAppDelegateServiceProtocol *> *services;
|
||||
|
||||
+ (void)addService:(GDTAppDelegateServiceProtocol *)service;
|
||||
|
||||
@end
|
463
drivers/apple_embedded/godot_app_delegate.mm
Normal file
463
drivers/apple_embedded/godot_app_delegate.mm
Normal file
@@ -0,0 +1,463 @@
|
||||
/**************************************************************************/
|
||||
/* godot_app_delegate.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "godot_app_delegate.h"
|
||||
|
||||
#import "app_delegate_service.h"
|
||||
|
||||
@implementation GDTApplicationDelegate
|
||||
|
||||
static NSMutableArray<GDTAppDelegateServiceProtocol *> *services = nil;
|
||||
|
||||
+ (NSArray<GDTAppDelegateServiceProtocol *> *)services {
|
||||
return services;
|
||||
}
|
||||
|
||||
+ (void)load {
|
||||
services = [NSMutableArray new];
|
||||
[services addObject:[GDTAppDelegateService new]];
|
||||
}
|
||||
|
||||
+ (void)addService:(GDTAppDelegateServiceProtocol *)service {
|
||||
if (!services || !service) {
|
||||
return;
|
||||
}
|
||||
[services addObject:service];
|
||||
}
|
||||
|
||||
// UIApplicationDelegate documentation can be found here: https://developer.apple.com/documentation/uikit/uiapplicationdelegate
|
||||
|
||||
// MARK: Window
|
||||
|
||||
- (UIWindow *)window {
|
||||
UIWindow *result = nil;
|
||||
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
UIWindow *value = [service window];
|
||||
|
||||
if (value) {
|
||||
result = value;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// MARK: Initializing
|
||||
|
||||
- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey, id> *)launchOptions {
|
||||
BOOL result = NO;
|
||||
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ([service application:application willFinishLaunchingWithOptions:launchOptions]) {
|
||||
result = YES;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey, id> *)launchOptions {
|
||||
BOOL result = NO;
|
||||
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ([service application:application didFinishLaunchingWithOptions:launchOptions]) {
|
||||
result = YES;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Can be handled by Info.plist. Not yet supported by Godot.
|
||||
|
||||
// MARK: Scene
|
||||
|
||||
- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options {}
|
||||
|
||||
- (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet<UISceneSession *> *)sceneSessions {}
|
||||
|
||||
*/
|
||||
|
||||
// MARK: Life-Cycle
|
||||
|
||||
- (void)applicationDidBecomeActive:(UIApplication *)application {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service applicationDidBecomeActive:application];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationWillResignActive:(UIApplication *)application {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service applicationWillResignActive:application];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationDidEnterBackground:(UIApplication *)application {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service applicationDidEnterBackground:application];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationWillEnterForeground:(UIApplication *)application {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service applicationWillEnterForeground:application];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(UIApplication *)application {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service applicationWillTerminate:application];
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Environment Changes
|
||||
|
||||
- (void)applicationProtectedDataDidBecomeAvailable:(UIApplication *)application {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service applicationProtectedDataDidBecomeAvailable:application];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationProtectedDataWillBecomeUnavailable:(UIApplication *)application {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service applicationProtectedDataWillBecomeUnavailable:application];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service applicationDidReceiveMemoryWarning:application];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationSignificantTimeChange:(UIApplication *)application {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service applicationSignificantTimeChange:application];
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: App State Restoration
|
||||
|
||||
- (BOOL)application:(UIApplication *)application shouldSaveSecureApplicationState:(NSCoder *)coder API_AVAILABLE(ios(13.2)) {
|
||||
BOOL result = NO;
|
||||
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ([service application:application shouldSaveSecureApplicationState:coder]) {
|
||||
result = YES;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication *)application shouldRestoreSecureApplicationState:(NSCoder *)coder API_AVAILABLE(ios(13.2)) {
|
||||
BOOL result = NO;
|
||||
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ([service application:application shouldRestoreSecureApplicationState:coder]) {
|
||||
result = YES;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (UIViewController *)application:(UIApplication *)application viewControllerWithRestorationIdentifierPath:(NSArray<NSString *> *)identifierComponents coder:(NSCoder *)coder {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
UIViewController *controller = [service application:application viewControllerWithRestorationIdentifierPath:identifierComponents coder:coder];
|
||||
|
||||
if (controller) {
|
||||
return controller;
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)application:(UIApplication *)application willEncodeRestorableStateWithCoder:(NSCoder *)coder {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service application:application willEncodeRestorableStateWithCoder:coder];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)application:(UIApplication *)application didDecodeRestorableStateWithCoder:(NSCoder *)coder {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service application:application didDecodeRestorableStateWithCoder:coder];
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Download Data in Background
|
||||
|
||||
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service application:application handleEventsForBackgroundURLSession:identifier completionHandler:completionHandler];
|
||||
}
|
||||
|
||||
completionHandler();
|
||||
}
|
||||
|
||||
// MARK: Remote Notification
|
||||
|
||||
// Moved to the iOS Plugin
|
||||
|
||||
// MARK: User Activity and Handling Quick Actions
|
||||
|
||||
- (BOOL)application:(UIApplication *)application willContinueUserActivityWithType:(NSString *)userActivityType {
|
||||
BOOL result = NO;
|
||||
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ([service application:application willContinueUserActivityWithType:userActivityType]) {
|
||||
result = YES;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> *restorableObjects))restorationHandler {
|
||||
BOOL result = NO;
|
||||
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ([service application:application continueUserActivity:userActivity restorationHandler:restorationHandler]) {
|
||||
result = YES;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (void)application:(UIApplication *)application didUpdateUserActivity:(NSUserActivity *)userActivity {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service application:application didUpdateUserActivity:userActivity];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)application:(UIApplication *)application didFailToContinueUserActivityWithType:(NSString *)userActivityType error:(NSError *)error {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service application:application didFailToContinueUserActivityWithType:userActivityType error:error];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL succeeded))completionHandler {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service application:application performActionForShortcutItem:shortcutItem completionHandler:completionHandler];
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: WatchKit
|
||||
|
||||
- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void (^)(NSDictionary *replyInfo))reply {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service application:application handleWatchKitExtensionRequest:userInfo reply:reply];
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: HealthKit
|
||||
|
||||
- (void)applicationShouldRequestHealthAuthorization:(UIApplication *)application {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service applicationShouldRequestHealthAuthorization:application];
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Opening an URL
|
||||
|
||||
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ([service application:app openURL:url options:options]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
// MARK: Disallowing Specified App Extension Types
|
||||
|
||||
- (BOOL)application:(UIApplication *)application shouldAllowExtensionPointIdentifier:(UIApplicationExtensionPointIdentifier)extensionPointIdentifier {
|
||||
BOOL result = NO;
|
||||
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ([service application:application shouldAllowExtensionPointIdentifier:extensionPointIdentifier]) {
|
||||
result = YES;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// MARK: SiriKit
|
||||
|
||||
- (id)application:(UIApplication *)application handlerForIntent:(INIntent *)intent API_AVAILABLE(ios(14.0)) {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
id result = [service application:application handlerForIntent:intent];
|
||||
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
// MARK: CloudKit
|
||||
|
||||
- (void)application:(UIApplication *)application userDidAcceptCloudKitShareWithMetadata:(CKShareMetadata *)cloudKitShareMetadata {
|
||||
for (GDTAppDelegateServiceProtocol *service in services) {
|
||||
if (![service respondsToSelector:_cmd]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[service application:application userDidAcceptCloudKitShareWithMetadata:cloudKitShareMetadata];
|
||||
}
|
||||
}
|
||||
|
||||
/* Handled By Info.plist file for now
|
||||
|
||||
// MARK: Interface Geometry
|
||||
|
||||
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {}
|
||||
|
||||
*/
|
||||
|
||||
@end
|
72
drivers/apple_embedded/godot_view_apple_embedded.h
Normal file
72
drivers/apple_embedded/godot_view_apple_embedded.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/**************************************************************************/
|
||||
/* godot_view_apple_embedded.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
class String;
|
||||
|
||||
@class GDTView;
|
||||
@protocol GDTDisplayLayer;
|
||||
@protocol GDTViewRendererProtocol;
|
||||
|
||||
@protocol GDTViewDelegate
|
||||
|
||||
- (BOOL)godotViewFinishedSetup:(GDTView *)view;
|
||||
|
||||
@end
|
||||
|
||||
@interface GDTView : UIView
|
||||
|
||||
@property(assign, nonatomic) id<GDTViewRendererProtocol> renderer;
|
||||
@property(assign, nonatomic) id<GDTViewDelegate> delegate;
|
||||
|
||||
@property(assign, readonly, nonatomic) BOOL isActive;
|
||||
|
||||
@property(assign, nonatomic) BOOL useCADisplayLink;
|
||||
@property(strong, readonly, nonatomic) CALayer<GDTDisplayLayer> *renderingLayer;
|
||||
@property(assign, readonly, nonatomic) BOOL canRender;
|
||||
|
||||
@property(assign, nonatomic) NSTimeInterval renderingInterval;
|
||||
|
||||
// Can be extended by subclasses
|
||||
- (void)godot_commonInit;
|
||||
|
||||
// Implemented in subclasses
|
||||
- (CALayer<GDTDisplayLayer> *)initializeRenderingForDriver:(NSString *)driverName;
|
||||
|
||||
- (void)stopRendering;
|
||||
- (void)startRendering;
|
||||
|
||||
@end
|
||||
|
||||
// Implemented in subclasses
|
||||
extern GDTView *GDTViewCreate();
|
470
drivers/apple_embedded/godot_view_apple_embedded.mm
Normal file
470
drivers/apple_embedded/godot_view_apple_embedded.mm
Normal file
@@ -0,0 +1,470 @@
|
||||
/**************************************************************************/
|
||||
/* godot_view_apple_embedded.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "godot_view_apple_embedded.h"
|
||||
|
||||
#import "display_layer_apple_embedded.h"
|
||||
#import "display_server_apple_embedded.h"
|
||||
#import "godot_view_renderer.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/os/keyboard.h"
|
||||
#include "core/string/ustring.h"
|
||||
|
||||
#import <CoreMotion/CoreMotion.h>
|
||||
|
||||
static const int max_touches = 32;
|
||||
static const float earth_gravity = 9.80665;
|
||||
|
||||
@interface GDTView () {
|
||||
UITouch *godot_touches[max_touches];
|
||||
}
|
||||
|
||||
@property(assign, nonatomic) BOOL isActive;
|
||||
|
||||
// CADisplayLink available on 3.1+ synchronizes the animation timer & drawing with the refresh rate of the display, only supports animation intervals of 1/60 1/30 & 1/15
|
||||
@property(strong, nonatomic) CADisplayLink *displayLink;
|
||||
|
||||
// An animation timer that, when animation is started, will periodically call -drawView at the given rate.
|
||||
// Only used if CADisplayLink is not
|
||||
@property(strong, nonatomic) NSTimer *animationTimer;
|
||||
|
||||
@property(strong, nonatomic) CALayer<GDTDisplayLayer> *renderingLayer;
|
||||
|
||||
@property(strong, nonatomic) CMMotionManager *motionManager;
|
||||
|
||||
@end
|
||||
|
||||
@implementation GDTView
|
||||
|
||||
// Implemented in subclasses
|
||||
- (CALayer<GDTDisplayLayer> *)initializeRenderingForDriver:(NSString *)driverName {
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)coder {
|
||||
self = [super initWithCoder:coder];
|
||||
|
||||
if (self) {
|
||||
[self godot_commonInit];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
|
||||
if (self) {
|
||||
[self godot_commonInit];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self stopRendering];
|
||||
|
||||
self.renderer = nil;
|
||||
self.delegate = nil;
|
||||
|
||||
if (self.renderingLayer) {
|
||||
[self.renderingLayer removeFromSuperlayer];
|
||||
self.renderingLayer = nil;
|
||||
}
|
||||
|
||||
if (self.motionManager) {
|
||||
[self.motionManager stopDeviceMotionUpdates];
|
||||
self.motionManager = nil;
|
||||
}
|
||||
|
||||
if (self.displayLink) {
|
||||
[self.displayLink invalidate];
|
||||
self.displayLink = nil;
|
||||
}
|
||||
|
||||
if (self.animationTimer) {
|
||||
[self.animationTimer invalidate];
|
||||
self.animationTimer = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)godot_commonInit {
|
||||
#if !defined(VISIONOS_ENABLED)
|
||||
self.contentScaleFactor = [UIScreen mainScreen].scale;
|
||||
#endif
|
||||
|
||||
if (@available(iOS 17.0, *)) {
|
||||
[self registerForTraitChanges:@[ [UITraitUserInterfaceStyle class] ] withTarget:self action:@selector(traitCollectionDidChangeWithView:previousTraitCollection:)];
|
||||
}
|
||||
|
||||
[self initTouches];
|
||||
|
||||
self.multipleTouchEnabled = YES;
|
||||
|
||||
// Configure and start accelerometer
|
||||
if (!self.motionManager) {
|
||||
self.motionManager = [[CMMotionManager alloc] init];
|
||||
if (self.motionManager.deviceMotionAvailable) {
|
||||
self.motionManager.deviceMotionUpdateInterval = 1.0 / 70.0;
|
||||
[self.motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXMagneticNorthZVertical];
|
||||
} else {
|
||||
self.motionManager = nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)system_theme_changed {
|
||||
DisplayServerAppleEmbedded *ds = (DisplayServerAppleEmbedded *)DisplayServer::get_singleton();
|
||||
if (ds) {
|
||||
ds->emit_system_theme_changed();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
|
||||
if (@available(iOS 13.0, *)) {
|
||||
#if !defined(VISIONOS_ENABLED)
|
||||
[super traitCollectionDidChange:previousTraitCollection];
|
||||
#endif
|
||||
[self traitCollectionDidChangeWithView:self
|
||||
previousTraitCollection:previousTraitCollection];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)traitCollectionDidChangeWithView:(UIView *)view previousTraitCollection:(UITraitCollection *)previousTraitCollection {
|
||||
if (@available(iOS 13.0, *)) {
|
||||
if ([UITraitCollection currentTraitCollection].userInterfaceStyle != previousTraitCollection.userInterfaceStyle) {
|
||||
[self system_theme_changed];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)stopRendering {
|
||||
if (!self.isActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.isActive = NO;
|
||||
|
||||
print_verbose("Stop animation!");
|
||||
|
||||
if (self.useCADisplayLink) {
|
||||
[self.displayLink invalidate];
|
||||
self.displayLink = nil;
|
||||
} else {
|
||||
[self.animationTimer invalidate];
|
||||
self.animationTimer = nil;
|
||||
}
|
||||
|
||||
[self clearTouches];
|
||||
}
|
||||
|
||||
- (void)startRendering {
|
||||
if (self.isActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.isActive = YES;
|
||||
|
||||
print_verbose("Start animation!");
|
||||
|
||||
if (self.useCADisplayLink) {
|
||||
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(drawView)];
|
||||
|
||||
if (GLOBAL_GET("display/window/ios/allow_high_refresh_rate")) {
|
||||
self.displayLink.preferredFramesPerSecond = 120;
|
||||
} else {
|
||||
self.displayLink.preferredFramesPerSecond = 60;
|
||||
}
|
||||
|
||||
// Setup DisplayLink in main thread
|
||||
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
|
||||
} else {
|
||||
self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:(1.0 / 60) target:self selector:@selector(drawView) userInfo:nil repeats:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)drawView {
|
||||
if (!self.isActive) {
|
||||
print_verbose("Draw view not active!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.useCADisplayLink) {
|
||||
// Pause the CADisplayLink to avoid recursion
|
||||
[self.displayLink setPaused:YES];
|
||||
|
||||
// Process all input events
|
||||
while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, TRUE) == kCFRunLoopRunHandledSource) {
|
||||
// Continue.
|
||||
}
|
||||
|
||||
// We are good to go, resume the CADisplayLink
|
||||
[self.displayLink setPaused:NO];
|
||||
}
|
||||
|
||||
[self.renderingLayer startRenderDisplayLayer];
|
||||
|
||||
if (!self.renderer) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ([self.renderer setupView:self]) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.delegate) {
|
||||
BOOL delegateFinishedSetup = [self.delegate godotViewFinishedSetup:self];
|
||||
|
||||
if (!delegateFinishedSetup) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[self handleMotion];
|
||||
[self.renderer renderOnView:self];
|
||||
|
||||
[self.renderingLayer stopRenderDisplayLayer];
|
||||
}
|
||||
|
||||
- (BOOL)canRender {
|
||||
if (self.useCADisplayLink) {
|
||||
return self.displayLink != nil;
|
||||
} else {
|
||||
return self.animationTimer != nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setRenderingInterval:(NSTimeInterval)renderingInterval {
|
||||
_renderingInterval = renderingInterval;
|
||||
|
||||
if (self.canRender) {
|
||||
[self stopRendering];
|
||||
[self startRendering];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
if (self.renderingLayer) {
|
||||
self.renderingLayer.frame = self.bounds;
|
||||
[self.renderingLayer layoutDisplayLayer];
|
||||
|
||||
if (DisplayServerAppleEmbedded::get_singleton()) {
|
||||
DisplayServerAppleEmbedded::get_singleton()->resize_window(self.bounds.size);
|
||||
}
|
||||
}
|
||||
|
||||
[super layoutSubviews];
|
||||
}
|
||||
|
||||
// MARK: - Input
|
||||
|
||||
// MARK: Touches
|
||||
|
||||
- (void)initTouches {
|
||||
for (int i = 0; i < max_touches; i++) {
|
||||
godot_touches[i] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
- (int)getTouchIDForTouch:(UITouch *)p_touch {
|
||||
int first = -1;
|
||||
for (int i = 0; i < max_touches; i++) {
|
||||
if (first == -1 && godot_touches[i] == nullptr) {
|
||||
first = i;
|
||||
continue;
|
||||
}
|
||||
if (godot_touches[i] == p_touch) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
if (first != -1) {
|
||||
godot_touches[first] = p_touch;
|
||||
return first;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
- (int)removeTouch:(UITouch *)p_touch {
|
||||
int remaining = 0;
|
||||
for (int i = 0; i < max_touches; i++) {
|
||||
if (godot_touches[i] == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (godot_touches[i] == p_touch) {
|
||||
godot_touches[i] = nullptr;
|
||||
} else {
|
||||
++remaining;
|
||||
}
|
||||
}
|
||||
return remaining;
|
||||
}
|
||||
|
||||
- (void)clearTouches {
|
||||
for (int i = 0; i < max_touches; i++) {
|
||||
godot_touches[i] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
|
||||
for (UITouch *touch in touches) {
|
||||
int tid = [self getTouchIDForTouch:touch];
|
||||
ERR_FAIL_COND(tid == -1);
|
||||
CGPoint touchPoint = [touch locationInView:self];
|
||||
DisplayServerAppleEmbedded::get_singleton()->touch_press(tid, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, true, touch.tapCount > 1);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
|
||||
for (UITouch *touch in touches) {
|
||||
int tid = [self getTouchIDForTouch:touch];
|
||||
ERR_FAIL_COND(tid == -1);
|
||||
CGPoint touchPoint = [touch locationInView:self];
|
||||
CGPoint prev_point = [touch previousLocationInView:self];
|
||||
CGFloat alt = [touch altitudeAngle];
|
||||
CGVector azim = [touch azimuthUnitVectorInView:self];
|
||||
DisplayServerAppleEmbedded::get_singleton()->touch_drag(tid, prev_point.x * self.contentScaleFactor, prev_point.y * self.contentScaleFactor, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, [touch force] / [touch maximumPossibleForce], Vector2(azim.dx, azim.dy) * Math::cos(alt));
|
||||
}
|
||||
}
|
||||
|
||||
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
|
||||
for (UITouch *touch in touches) {
|
||||
int tid = [self getTouchIDForTouch:touch];
|
||||
ERR_FAIL_COND(tid == -1);
|
||||
[self removeTouch:touch];
|
||||
CGPoint touchPoint = [touch locationInView:self];
|
||||
DisplayServerAppleEmbedded::get_singleton()->touch_press(tid, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
|
||||
for (UITouch *touch in touches) {
|
||||
int tid = [self getTouchIDForTouch:touch];
|
||||
ERR_FAIL_COND(tid == -1);
|
||||
DisplayServerAppleEmbedded::get_singleton()->touches_canceled(tid);
|
||||
}
|
||||
[self clearTouches];
|
||||
}
|
||||
|
||||
// MARK: Motion
|
||||
|
||||
- (void)handleMotion {
|
||||
if (!self.motionManager) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Just using polling approach for now, we can set this up so it sends
|
||||
// data to us in intervals, might be better. See Apple reference pages
|
||||
// for more details:
|
||||
// https://developer.apple.com/reference/coremotion/cmmotionmanager?language=objc
|
||||
|
||||
// Apple splits our accelerometer date into a gravity and user movement
|
||||
// component. We add them back together.
|
||||
CMAcceleration gravity = self.motionManager.deviceMotion.gravity;
|
||||
CMAcceleration acceleration = self.motionManager.deviceMotion.userAcceleration;
|
||||
|
||||
// To be consistent with Android we convert the unit of measurement from g (Earth's gravity)
|
||||
// to m/s^2.
|
||||
gravity.x *= earth_gravity;
|
||||
gravity.y *= earth_gravity;
|
||||
gravity.z *= earth_gravity;
|
||||
acceleration.x *= earth_gravity;
|
||||
acceleration.y *= earth_gravity;
|
||||
acceleration.z *= earth_gravity;
|
||||
|
||||
///@TODO We don't seem to be getting data here, is my device broken or
|
||||
/// is this code incorrect?
|
||||
CMMagneticField magnetic = self.motionManager.deviceMotion.magneticField.field;
|
||||
|
||||
///@TODO we can access rotationRate as a CMRotationRate variable
|
||||
///(processed date) or CMGyroData (raw data), have to see what works
|
||||
/// best
|
||||
CMRotationRate rotation = self.motionManager.deviceMotion.rotationRate;
|
||||
|
||||
// Adjust for screen orientation.
|
||||
// [[UIDevice currentDevice] orientation] changes even if we've fixed
|
||||
// our orientation which is not a good thing when you're trying to get
|
||||
// your user to move the screen in all directions and want consistent
|
||||
// output
|
||||
|
||||
///@TODO Using [[UIApplication sharedApplication] statusBarOrientation]
|
||||
/// is a bit of a hack. Godot obviously knows the orientation so maybe
|
||||
/// we
|
||||
// can use that instead? (note that left and right seem swapped)
|
||||
|
||||
UIInterfaceOrientation interfaceOrientation = UIInterfaceOrientationUnknown;
|
||||
|
||||
#if !defined(VISIONOS_ENABLED)
|
||||
#if __IPHONE_OS_VERSION_MAX_ALLOWED < 140000
|
||||
interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
|
||||
#else
|
||||
if (@available(iOS 13, *)) {
|
||||
interfaceOrientation = [UIApplication sharedApplication].delegate.window.windowScene.interfaceOrientation;
|
||||
#if !defined(TARGET_OS_SIMULATOR) || !TARGET_OS_SIMULATOR
|
||||
} else {
|
||||
interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
interfaceOrientation = [UIApplication sharedApplication].delegate.window.windowScene.interfaceOrientation;
|
||||
#endif
|
||||
|
||||
switch (interfaceOrientation) {
|
||||
case UIInterfaceOrientationLandscapeLeft: {
|
||||
DisplayServerAppleEmbedded::get_singleton()->update_gravity(Vector3(gravity.x, gravity.y, gravity.z).rotated(Vector3(0, 0, 1), -Math::PI * 0.5));
|
||||
DisplayServerAppleEmbedded::get_singleton()->update_accelerometer(Vector3(acceleration.x + gravity.x, acceleration.y + gravity.y, acceleration.z + gravity.z).rotated(Vector3(0, 0, 1), -Math::PI * 0.5));
|
||||
DisplayServerAppleEmbedded::get_singleton()->update_magnetometer(Vector3(magnetic.x, magnetic.y, magnetic.z).rotated(Vector3(0, 0, 1), -Math::PI * 0.5));
|
||||
DisplayServerAppleEmbedded::get_singleton()->update_gyroscope(Vector3(rotation.x, rotation.y, rotation.z).rotated(Vector3(0, 0, 1), -Math::PI * 0.5));
|
||||
} break;
|
||||
case UIInterfaceOrientationLandscapeRight: {
|
||||
DisplayServerAppleEmbedded::get_singleton()->update_gravity(Vector3(gravity.x, gravity.y, gravity.z).rotated(Vector3(0, 0, 1), Math::PI * 0.5));
|
||||
DisplayServerAppleEmbedded::get_singleton()->update_accelerometer(Vector3(acceleration.x + gravity.x, acceleration.y + gravity.y, acceleration.z + gravity.z).rotated(Vector3(0, 0, 1), Math::PI * 0.5));
|
||||
DisplayServerAppleEmbedded::get_singleton()->update_magnetometer(Vector3(magnetic.x, magnetic.y, magnetic.z).rotated(Vector3(0, 0, 1), Math::PI * 0.5));
|
||||
DisplayServerAppleEmbedded::get_singleton()->update_gyroscope(Vector3(rotation.x, rotation.y, rotation.z).rotated(Vector3(0, 0, 1), Math::PI * 0.5));
|
||||
} break;
|
||||
case UIInterfaceOrientationPortraitUpsideDown: {
|
||||
DisplayServerAppleEmbedded::get_singleton()->update_gravity(Vector3(gravity.x, gravity.y, gravity.z).rotated(Vector3(0, 0, 1), Math::PI));
|
||||
DisplayServerAppleEmbedded::get_singleton()->update_accelerometer(Vector3(acceleration.x + gravity.x, acceleration.y + gravity.y, acceleration.z + gravity.z).rotated(Vector3(0, 0, 1), Math::PI));
|
||||
DisplayServerAppleEmbedded::get_singleton()->update_magnetometer(Vector3(magnetic.x, magnetic.y, magnetic.z).rotated(Vector3(0, 0, 1), Math::PI));
|
||||
DisplayServerAppleEmbedded::get_singleton()->update_gyroscope(Vector3(rotation.x, rotation.y, rotation.z).rotated(Vector3(0, 0, 1), Math::PI));
|
||||
} break;
|
||||
default: { // assume portrait
|
||||
DisplayServerAppleEmbedded::get_singleton()->update_gravity(Vector3(gravity.x, gravity.y, gravity.z));
|
||||
DisplayServerAppleEmbedded::get_singleton()->update_accelerometer(Vector3(acceleration.x + gravity.x, acceleration.y + gravity.y, acceleration.z + gravity.z));
|
||||
DisplayServerAppleEmbedded::get_singleton()->update_magnetometer(Vector3(magnetic.x, magnetic.y, magnetic.z));
|
||||
DisplayServerAppleEmbedded::get_singleton()->update_gyroscope(Vector3(rotation.x, rotation.y, rotation.z));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
46
drivers/apple_embedded/godot_view_renderer.h
Normal file
46
drivers/apple_embedded/godot_view_renderer.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/**************************************************************************/
|
||||
/* godot_view_renderer.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@protocol GDTViewRendererProtocol <NSObject>
|
||||
|
||||
@property(assign, readonly, nonatomic) BOOL hasFinishedSetup;
|
||||
|
||||
- (BOOL)setupView:(UIView *)view;
|
||||
- (void)renderOnView:(UIView *)view;
|
||||
|
||||
@end
|
||||
|
||||
@interface GDTViewRenderer : NSObject <GDTViewRendererProtocol>
|
||||
|
||||
@end
|
119
drivers/apple_embedded/godot_view_renderer.mm
Normal file
119
drivers/apple_embedded/godot_view_renderer.mm
Normal file
@@ -0,0 +1,119 @@
|
||||
/**************************************************************************/
|
||||
/* godot_view_renderer.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "godot_view_renderer.h"
|
||||
|
||||
#import "display_server_apple_embedded.h"
|
||||
#import "os_apple_embedded.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/os/keyboard.h"
|
||||
#include "main/main.h"
|
||||
#include "servers/audio_server.h"
|
||||
|
||||
#import <AudioToolbox/AudioServices.h>
|
||||
#import <CoreMotion/CoreMotion.h>
|
||||
#import <GameController/GameController.h>
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface GDTViewRenderer ()
|
||||
|
||||
@property(assign, nonatomic) BOOL hasFinishedProjectDataSetup;
|
||||
@property(assign, nonatomic) BOOL hasStartedMain;
|
||||
@property(assign, nonatomic) BOOL hasFinishedSetup;
|
||||
|
||||
@end
|
||||
|
||||
@implementation GDTViewRenderer
|
||||
|
||||
- (BOOL)setupView:(UIView *)view {
|
||||
if (self.hasFinishedSetup) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (!OS::get_singleton()) {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (!self.hasFinishedProjectDataSetup) {
|
||||
[self setupProjectData];
|
||||
return YES;
|
||||
}
|
||||
|
||||
if (!self.hasStartedMain) {
|
||||
self.hasStartedMain = YES;
|
||||
OS_AppleEmbedded::get_singleton()->start();
|
||||
return YES;
|
||||
}
|
||||
|
||||
self.hasFinishedSetup = YES;
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)setupProjectData {
|
||||
self.hasFinishedProjectDataSetup = YES;
|
||||
|
||||
Main::setup2();
|
||||
|
||||
// this might be necessary before here
|
||||
NSDictionary *dict = [[NSBundle mainBundle] infoDictionary];
|
||||
for (NSString *key in dict) {
|
||||
NSObject *value = [dict objectForKey:key];
|
||||
String ukey = String::utf8([key UTF8String]);
|
||||
|
||||
// we need a NSObject to Variant conversor
|
||||
|
||||
if ([value isKindOfClass:[NSString class]]) {
|
||||
NSString *str = (NSString *)value;
|
||||
String uval = String::utf8([str UTF8String]);
|
||||
|
||||
ProjectSettings::get_singleton()->set("Info.plist/" + ukey, uval);
|
||||
|
||||
} else if ([value isKindOfClass:[NSNumber class]]) {
|
||||
NSNumber *n = (NSNumber *)value;
|
||||
double dval = [n doubleValue];
|
||||
|
||||
ProjectSettings::get_singleton()->set("Info.plist/" + ukey, dval);
|
||||
}
|
||||
// do stuff
|
||||
}
|
||||
}
|
||||
|
||||
- (void)renderOnView:(UIView *)view {
|
||||
if (!OS_AppleEmbedded::get_singleton()) {
|
||||
return;
|
||||
}
|
||||
|
||||
OS_AppleEmbedded::get_singleton()->iterate();
|
||||
}
|
||||
|
||||
@end
|
44
drivers/apple_embedded/key_mapping_apple_embedded.h
Normal file
44
drivers/apple_embedded/key_mapping_apple_embedded.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/**************************************************************************/
|
||||
/* key_mapping_apple_embedded.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/os/keyboard.h"
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
class KeyMappingAppleEmbedded {
|
||||
KeyMappingAppleEmbedded() {}
|
||||
|
||||
public:
|
||||
static void initialize();
|
||||
static Key remap_key(CFIndex p_keycode);
|
||||
static KeyLocation key_location(CFIndex p_keycode);
|
||||
};
|
206
drivers/apple_embedded/key_mapping_apple_embedded.mm
Normal file
206
drivers/apple_embedded/key_mapping_apple_embedded.mm
Normal file
@@ -0,0 +1,206 @@
|
||||
/**************************************************************************/
|
||||
/* key_mapping_apple_embedded.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "key_mapping_apple_embedded.h"
|
||||
|
||||
#include "core/templates/hash_map.h"
|
||||
|
||||
struct HashMapHasherKeys {
|
||||
static _FORCE_INLINE_ uint32_t hash(const Key p_key) { return hash_fmix32(static_cast<uint32_t>(p_key)); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const CFIndex p_key) { return hash_fmix32(p_key); }
|
||||
};
|
||||
|
||||
HashMap<CFIndex, Key, HashMapHasherKeys> keyusage_map;
|
||||
HashMap<CFIndex, KeyLocation, HashMapHasherKeys> location_map;
|
||||
|
||||
void KeyMappingAppleEmbedded::initialize() {
|
||||
if (@available(iOS 13.4, *)) {
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardA] = Key::A;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardB] = Key::B;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardC] = Key::C;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardD] = Key::D;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardE] = Key::E;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF] = Key::F;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardG] = Key::G;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardH] = Key::H;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardI] = Key::I;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardJ] = Key::J;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardK] = Key::K;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardL] = Key::L;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardM] = Key::M;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardN] = Key::N;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardO] = Key::O;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardP] = Key::P;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardQ] = Key::Q;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardR] = Key::R;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardS] = Key::S;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardT] = Key::T;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardU] = Key::U;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardV] = Key::V;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardW] = Key::W;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardX] = Key::X;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardY] = Key::Y;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardZ] = Key::Z;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboard0] = Key::KEY_0;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboard1] = Key::KEY_1;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboard2] = Key::KEY_2;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboard3] = Key::KEY_3;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboard4] = Key::KEY_4;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboard5] = Key::KEY_5;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboard6] = Key::KEY_6;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboard7] = Key::KEY_7;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboard8] = Key::KEY_8;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboard9] = Key::KEY_9;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardBackslash] = Key::BACKSLASH;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardCloseBracket] = Key::BRACKETRIGHT;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardComma] = Key::COMMA;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardEqualSign] = Key::EQUAL;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardHyphen] = Key::MINUS;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardNonUSBackslash] = Key::SECTION;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardNonUSPound] = Key::ASCIITILDE;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardOpenBracket] = Key::BRACKETLEFT;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardPeriod] = Key::PERIOD;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardQuote] = Key::QUOTEDBL;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardSemicolon] = Key::SEMICOLON;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardSeparator] = Key::SECTION;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardSlash] = Key::SLASH;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardSpacebar] = Key::SPACE;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardCapsLock] = Key::CAPSLOCK;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardLeftAlt] = Key::ALT;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardLeftControl] = Key::CTRL;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardLeftShift] = Key::SHIFT;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardRightAlt] = Key::ALT;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardRightControl] = Key::CTRL;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardRightShift] = Key::SHIFT;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardScrollLock] = Key::SCROLLLOCK;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardLeftArrow] = Key::LEFT;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardRightArrow] = Key::RIGHT;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardUpArrow] = Key::UP;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardDownArrow] = Key::DOWN;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardPageUp] = Key::PAGEUP;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardPageDown] = Key::PAGEDOWN;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardHome] = Key::HOME;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardEnd] = Key::END;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardDeleteForward] = Key::KEY_DELETE;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardDeleteOrBackspace] = Key::BACKSPACE;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardEscape] = Key::ESCAPE;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardInsert] = Key::INSERT;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardReturn] = Key::ENTER;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardTab] = Key::TAB;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF1] = Key::F1;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF2] = Key::F2;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF3] = Key::F3;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF4] = Key::F4;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF5] = Key::F5;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF6] = Key::F6;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF7] = Key::F7;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF8] = Key::F8;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF9] = Key::F9;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF10] = Key::F10;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF11] = Key::F11;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF12] = Key::F12;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF13] = Key::F13;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF14] = Key::F14;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF15] = Key::F15;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF16] = Key::F16;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF17] = Key::F17;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF18] = Key::F18;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF19] = Key::F19;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF20] = Key::F20;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF21] = Key::F21;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF22] = Key::F22;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF23] = Key::F23;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardF24] = Key::F24;
|
||||
keyusage_map[UIKeyboardHIDUsageKeypad0] = Key::KP_0;
|
||||
keyusage_map[UIKeyboardHIDUsageKeypad1] = Key::KP_1;
|
||||
keyusage_map[UIKeyboardHIDUsageKeypad2] = Key::KP_2;
|
||||
keyusage_map[UIKeyboardHIDUsageKeypad3] = Key::KP_3;
|
||||
keyusage_map[UIKeyboardHIDUsageKeypad4] = Key::KP_4;
|
||||
keyusage_map[UIKeyboardHIDUsageKeypad5] = Key::KP_5;
|
||||
keyusage_map[UIKeyboardHIDUsageKeypad6] = Key::KP_6;
|
||||
keyusage_map[UIKeyboardHIDUsageKeypad7] = Key::KP_7;
|
||||
keyusage_map[UIKeyboardHIDUsageKeypad8] = Key::KP_8;
|
||||
keyusage_map[UIKeyboardHIDUsageKeypad9] = Key::KP_9;
|
||||
keyusage_map[UIKeyboardHIDUsageKeypadAsterisk] = Key::KP_MULTIPLY;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardGraveAccentAndTilde] = Key::BAR;
|
||||
keyusage_map[UIKeyboardHIDUsageKeypadEnter] = Key::KP_ENTER;
|
||||
keyusage_map[UIKeyboardHIDUsageKeypadHyphen] = Key::KP_SUBTRACT;
|
||||
keyusage_map[UIKeyboardHIDUsageKeypadNumLock] = Key::NUMLOCK;
|
||||
keyusage_map[UIKeyboardHIDUsageKeypadPeriod] = Key::KP_PERIOD;
|
||||
keyusage_map[UIKeyboardHIDUsageKeypadPlus] = Key::KP_ADD;
|
||||
keyusage_map[UIKeyboardHIDUsageKeypadSlash] = Key::KP_DIVIDE;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardPause] = Key::PAUSE;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardStop] = Key::STOP;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardMute] = Key::VOLUMEMUTE;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardVolumeUp] = Key::VOLUMEUP;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardVolumeDown] = Key::VOLUMEDOWN;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardFind] = Key::SEARCH;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardHelp] = Key::HELP;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardLeftGUI] = Key::META;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardRightGUI] = Key::META;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardMenu] = Key::MENU;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardPrintScreen] = Key::PRINT;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardReturnOrEnter] = Key::ENTER;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardSysReqOrAttention] = Key::SYSREQ;
|
||||
keyusage_map[0x01AE] = Key::KEYBOARD; // On-screen keyboard key on smart connector keyboard.
|
||||
keyusage_map[0x029D] = Key::GLOBE; // "Globe" key on smart connector / Mac keyboard.
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardLANG1] = Key::JIS_EISU;
|
||||
keyusage_map[UIKeyboardHIDUsageKeyboardLANG2] = Key::JIS_KANA;
|
||||
|
||||
location_map[UIKeyboardHIDUsageKeyboardLeftAlt] = KeyLocation::LEFT;
|
||||
location_map[UIKeyboardHIDUsageKeyboardRightAlt] = KeyLocation::RIGHT;
|
||||
location_map[UIKeyboardHIDUsageKeyboardLeftControl] = KeyLocation::LEFT;
|
||||
location_map[UIKeyboardHIDUsageKeyboardRightControl] = KeyLocation::RIGHT;
|
||||
location_map[UIKeyboardHIDUsageKeyboardLeftShift] = KeyLocation::LEFT;
|
||||
location_map[UIKeyboardHIDUsageKeyboardRightShift] = KeyLocation::RIGHT;
|
||||
location_map[UIKeyboardHIDUsageKeyboardLeftGUI] = KeyLocation::LEFT;
|
||||
location_map[UIKeyboardHIDUsageKeyboardRightGUI] = KeyLocation::RIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
Key KeyMappingAppleEmbedded::remap_key(CFIndex p_keycode) {
|
||||
if (@available(iOS 13.4, *)) {
|
||||
const Key *key = keyusage_map.getptr(p_keycode);
|
||||
if (key) {
|
||||
return *key;
|
||||
}
|
||||
}
|
||||
return Key::NONE;
|
||||
}
|
||||
|
||||
KeyLocation KeyMappingAppleEmbedded::key_location(CFIndex p_keycode) {
|
||||
if (@available(iOS 13.4, *)) {
|
||||
const KeyLocation *location = location_map.getptr(p_keycode);
|
||||
if (location) {
|
||||
return *location;
|
||||
}
|
||||
}
|
||||
return KeyLocation::UNSPECIFIED;
|
||||
}
|
39
drivers/apple_embedded/keyboard_input_view.h
Normal file
39
drivers/apple_embedded/keyboard_input_view.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/**************************************************************************/
|
||||
/* keyboard_input_view.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface GDTKeyboardInputView : UITextView
|
||||
|
||||
- (BOOL)becomeFirstResponderWithString:(NSString *)existingString cursorStart:(NSInteger)start cursorEnd:(NSInteger)end;
|
||||
|
||||
@end
|
201
drivers/apple_embedded/keyboard_input_view.mm
Normal file
201
drivers/apple_embedded/keyboard_input_view.mm
Normal file
@@ -0,0 +1,201 @@
|
||||
/**************************************************************************/
|
||||
/* keyboard_input_view.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "keyboard_input_view.h"
|
||||
|
||||
#import "display_server_apple_embedded.h"
|
||||
#import "os_apple_embedded.h"
|
||||
|
||||
#include "core/os/keyboard.h"
|
||||
|
||||
@interface GDTKeyboardInputView () <UITextViewDelegate>
|
||||
|
||||
@property(nonatomic, copy) NSString *previousText;
|
||||
@property(nonatomic, assign) NSRange previousSelectedRange;
|
||||
|
||||
@end
|
||||
|
||||
@implementation GDTKeyboardInputView
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)coder {
|
||||
self = [super initWithCoder:coder];
|
||||
|
||||
if (self) {
|
||||
[self godot_commonInit];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame textContainer:(NSTextContainer *)textContainer {
|
||||
self = [super initWithFrame:frame textContainer:textContainer];
|
||||
|
||||
if (self) {
|
||||
[self godot_commonInit];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)godot_commonInit {
|
||||
self.hidden = YES;
|
||||
self.delegate = self;
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(observeTextChange:)
|
||||
name:UITextViewTextDidChangeNotification
|
||||
object:self];
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
self.delegate = nil;
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
// MARK: Keyboard
|
||||
|
||||
- (BOOL)canBecomeFirstResponder {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)becomeFirstResponderWithString:(NSString *)existingString cursorStart:(NSInteger)start cursorEnd:(NSInteger)end {
|
||||
self.text = existingString;
|
||||
self.previousText = existingString;
|
||||
|
||||
NSInteger safeStartIndex = MAX(start, 0);
|
||||
|
||||
NSRange textRange;
|
||||
|
||||
// Either a simple cursor or a selection.
|
||||
if (end > 0) {
|
||||
textRange = NSMakeRange(safeStartIndex, end - start);
|
||||
} else {
|
||||
textRange = NSMakeRange(safeStartIndex, 0);
|
||||
}
|
||||
|
||||
self.selectedRange = textRange;
|
||||
self.previousSelectedRange = textRange;
|
||||
|
||||
return [self becomeFirstResponder];
|
||||
}
|
||||
|
||||
- (BOOL)resignFirstResponder {
|
||||
self.text = nil;
|
||||
self.previousText = nil;
|
||||
return [super resignFirstResponder];
|
||||
}
|
||||
|
||||
// MARK: OS Messages
|
||||
|
||||
- (void)deleteText:(NSInteger)charactersToDelete {
|
||||
for (int i = 0; i < charactersToDelete; i++) {
|
||||
DisplayServerAppleEmbedded::get_singleton()->key(Key::BACKSPACE, 0, Key::BACKSPACE, Key::NONE, 0, true, KeyLocation::UNSPECIFIED);
|
||||
DisplayServerAppleEmbedded::get_singleton()->key(Key::BACKSPACE, 0, Key::BACKSPACE, Key::NONE, 0, false, KeyLocation::UNSPECIFIED);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)enterText:(NSString *)substring {
|
||||
String characters = String::utf8([substring UTF8String]);
|
||||
|
||||
for (int i = 0; i < characters.size(); i++) {
|
||||
int character = characters[i];
|
||||
Key key = Key::NONE;
|
||||
|
||||
if (character == '\t') { // 0x09
|
||||
key = Key::TAB;
|
||||
} else if (character == '\n') { // 0x0A
|
||||
key = Key::ENTER;
|
||||
} else if (character == 0x2006) {
|
||||
key = Key::SPACE;
|
||||
}
|
||||
|
||||
DisplayServerAppleEmbedded::get_singleton()->key(key, character, key, Key::NONE, 0, true, KeyLocation::UNSPECIFIED);
|
||||
DisplayServerAppleEmbedded::get_singleton()->key(key, character, key, Key::NONE, 0, false, KeyLocation::UNSPECIFIED);
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Observer
|
||||
|
||||
- (void)observeTextChange:(NSNotification *)notification {
|
||||
if (notification.object != self) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *substringToDelete = nil;
|
||||
if (self.previousSelectedRange.length == 0) {
|
||||
// Get previous text to delete.
|
||||
substringToDelete = [self.previousText substringToIndex:self.previousSelectedRange.location];
|
||||
} else {
|
||||
// If text was previously selected we are sending only one `backspace`. It will remove all text from text input.
|
||||
[self deleteText:1];
|
||||
}
|
||||
|
||||
NSString *substringToEnter = nil;
|
||||
if (self.selectedRange.length == 0) {
|
||||
// If previous cursor had a selection we have to calculate an inserted text.
|
||||
if (self.previousSelectedRange.length != 0) {
|
||||
NSInteger rangeEnd = self.selectedRange.location + self.selectedRange.length;
|
||||
NSInteger rangeStart = MIN(self.previousSelectedRange.location, self.selectedRange.location);
|
||||
NSInteger rangeLength = MAX(0, rangeEnd - rangeStart);
|
||||
|
||||
NSRange calculatedRange;
|
||||
|
||||
if (rangeLength >= 0) {
|
||||
calculatedRange = NSMakeRange(rangeStart, rangeLength);
|
||||
} else {
|
||||
calculatedRange = NSMakeRange(rangeStart, 0);
|
||||
}
|
||||
|
||||
substringToEnter = [self.text substringWithRange:calculatedRange];
|
||||
} else {
|
||||
substringToEnter = [self.text substringToIndex:self.selectedRange.location];
|
||||
}
|
||||
} else {
|
||||
substringToEnter = [self.text substringWithRange:self.selectedRange];
|
||||
}
|
||||
|
||||
NSInteger skip = 0;
|
||||
if (substringToDelete != nil) {
|
||||
for (NSUInteger i = 0; i < MIN([substringToDelete length], [substringToEnter length]); i++) {
|
||||
if ([substringToDelete characterAtIndex:i] == [substringToEnter characterAtIndex:i]) {
|
||||
skip++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
[self deleteText:[substringToDelete length] - skip]; // Delete changed part of previous text.
|
||||
}
|
||||
[self enterText:[substringToEnter substringFromIndex:skip]]; // Enter changed part of new text.
|
||||
|
||||
self.previousText = self.text;
|
||||
self.previousSelectedRange = self.selectedRange;
|
||||
}
|
||||
|
||||
@end
|
35
drivers/apple_embedded/main_utilities.h
Normal file
35
drivers/apple_embedded/main_utilities.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/**************************************************************************/
|
||||
/* main_utilities.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
void change_to_launch_dir(char **p_args);
|
||||
|
||||
int process_args(int p_argc, char **p_args, char **r_args);
|
95
drivers/apple_embedded/main_utilities.mm
Normal file
95
drivers/apple_embedded/main_utilities.mm
Normal file
@@ -0,0 +1,95 @@
|
||||
/**************************************************************************/
|
||||
/* main_utilities.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "core/string/ustring.h"
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <cstdio>
|
||||
|
||||
void change_to_launch_dir(char **p_args) {
|
||||
size_t len = strlen(p_args[0]);
|
||||
|
||||
while (len--) {
|
||||
if (p_args[0][len] == '/') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (len >= 0) {
|
||||
char path[512];
|
||||
memcpy(path, p_args[0], len > sizeof(path) ? sizeof(path) : len);
|
||||
path[len] = 0;
|
||||
chdir(path);
|
||||
}
|
||||
}
|
||||
|
||||
int add_path(int p_argc, char **p_args) {
|
||||
NSString *str = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"godot_path"];
|
||||
if (!str) {
|
||||
return p_argc;
|
||||
}
|
||||
|
||||
p_args[p_argc++] = (char *)"--path";
|
||||
p_args[p_argc++] = (char *)[str cStringUsingEncoding:NSUTF8StringEncoding];
|
||||
p_args[p_argc] = nullptr;
|
||||
|
||||
return p_argc;
|
||||
}
|
||||
|
||||
int add_cmdline(int p_argc, char **p_args) {
|
||||
NSArray *arr = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"godot_cmdline"];
|
||||
if (!arr) {
|
||||
return p_argc;
|
||||
}
|
||||
|
||||
for (NSUInteger i = 0; i < [arr count]; i++) {
|
||||
NSString *str = [arr objectAtIndex:i];
|
||||
if (!str) {
|
||||
continue;
|
||||
}
|
||||
p_args[p_argc++] = (char *)[str cStringUsingEncoding:NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
p_args[p_argc] = nullptr;
|
||||
|
||||
return p_argc;
|
||||
}
|
||||
|
||||
int process_args(int p_argc, char **p_args, char **r_args) {
|
||||
for (int i = 0; i < p_argc; i++) {
|
||||
r_args[i] = p_args[i];
|
||||
}
|
||||
r_args[p_argc] = nullptr;
|
||||
p_argc = add_path(p_argc, r_args);
|
||||
p_argc = add_cmdline(p_argc, r_args);
|
||||
return p_argc;
|
||||
}
|
145
drivers/apple_embedded/os_apple_embedded.h
Normal file
145
drivers/apple_embedded/os_apple_embedded.h
Normal file
@@ -0,0 +1,145 @@
|
||||
/**************************************************************************/
|
||||
/* os_apple_embedded.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef APPLE_EMBEDDED_ENABLED
|
||||
|
||||
#import "apple_embedded.h"
|
||||
|
||||
#import "drivers/apple/joypad_apple.h"
|
||||
#import "drivers/coreaudio/audio_driver_coreaudio.h"
|
||||
#include "drivers/unix/os_unix.h"
|
||||
#include "servers/audio_server.h"
|
||||
#include "servers/rendering/renderer_compositor.h"
|
||||
|
||||
#if defined(RD_ENABLED)
|
||||
#include "servers/rendering/rendering_device.h"
|
||||
|
||||
#if defined(VULKAN_ENABLED)
|
||||
#import "rendering_context_driver_vulkan_apple_embedded.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
class OS_AppleEmbedded : public OS_Unix {
|
||||
private:
|
||||
static HashMap<String, void *> dynamic_symbol_lookup_table;
|
||||
friend void register_dynamic_symbol(char *name, void *address);
|
||||
|
||||
AudioDriverCoreAudio audio_driver;
|
||||
|
||||
AppleEmbedded *apple_embedded = nullptr;
|
||||
|
||||
JoypadApple *joypad_apple = nullptr;
|
||||
|
||||
MainLoop *main_loop = nullptr;
|
||||
|
||||
virtual void initialize_core() override;
|
||||
virtual void initialize() override;
|
||||
|
||||
virtual void initialize_joypads() override;
|
||||
|
||||
virtual void set_main_loop(MainLoop *p_main_loop) override;
|
||||
virtual MainLoop *get_main_loop() const override;
|
||||
|
||||
virtual void delete_main_loop() override;
|
||||
|
||||
virtual void finalize() override;
|
||||
|
||||
bool is_focused = false;
|
||||
|
||||
CGFloat _weight_to_ct(int p_weight) const;
|
||||
CGFloat _stretch_to_ct(int p_stretch) const;
|
||||
String _get_default_fontname(const String &p_font_name) const;
|
||||
|
||||
static _FORCE_INLINE_ String get_framework_executable(const String &p_path);
|
||||
|
||||
void deinitialize_modules();
|
||||
|
||||
mutable String remote_fs_dir;
|
||||
|
||||
public:
|
||||
static OS_AppleEmbedded *get_singleton();
|
||||
|
||||
OS_AppleEmbedded();
|
||||
~OS_AppleEmbedded();
|
||||
|
||||
void initialize_modules();
|
||||
|
||||
bool iterate();
|
||||
|
||||
void start();
|
||||
|
||||
virtual void alert(const String &p_alert, const String &p_title = "ALERT!") override;
|
||||
|
||||
virtual Vector<String> get_system_fonts() const override;
|
||||
virtual Vector<String> get_system_font_path_for_text(const String &p_font_name, const String &p_text, const String &p_locale = String(), const String &p_script = String(), int p_weight = 400, int p_stretch = 100, bool p_italic = false) const override;
|
||||
virtual String get_system_font_path(const String &p_font_name, int p_weight = 400, int p_stretch = 100, bool p_italic = false) const override;
|
||||
|
||||
virtual Error open_dynamic_library(const String &p_path, void *&p_library_handle, GDExtensionData *p_data = nullptr) override;
|
||||
virtual Error close_dynamic_library(void *p_library_handle) override;
|
||||
virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String &p_name, void *&p_symbol_handle, bool p_optional = false) override;
|
||||
|
||||
virtual String get_distribution_name() const override;
|
||||
virtual String get_version() const override;
|
||||
virtual String get_model_name() const override;
|
||||
|
||||
virtual Error shell_open(const String &p_uri) override;
|
||||
|
||||
virtual String get_user_data_dir(const String &p_user_dir) const override;
|
||||
|
||||
virtual String get_cache_path() const override;
|
||||
virtual String get_temp_path() const override;
|
||||
virtual String get_resource_dir() const override;
|
||||
|
||||
virtual String get_locale() const override;
|
||||
|
||||
virtual String get_unique_id() const override;
|
||||
virtual String get_processor_name() const override;
|
||||
|
||||
virtual void vibrate_handheld(int p_duration_ms = 500, float p_amplitude = -1.0) override;
|
||||
|
||||
virtual bool _check_internal_feature_support(const String &p_feature) override;
|
||||
|
||||
virtual Error setup_remote_filesystem(const String &p_server_host, int p_port, const String &p_password, String &r_project_path) override;
|
||||
|
||||
void on_focus_out();
|
||||
void on_focus_in();
|
||||
|
||||
void on_enter_background();
|
||||
void on_exit_background();
|
||||
|
||||
virtual Rect2 calculate_boot_screen_rect(const Size2 &p_window_size, const Size2 &p_imgrect_size) const override;
|
||||
|
||||
virtual bool request_permission(const String &p_name) override;
|
||||
virtual Vector<String> get_granted_permissions() const override;
|
||||
};
|
||||
|
||||
#endif // APPLE_EMBEDDED_ENABLED
|
768
drivers/apple_embedded/os_apple_embedded.mm
Normal file
768
drivers/apple_embedded/os_apple_embedded.mm
Normal file
@@ -0,0 +1,768 @@
|
||||
/**************************************************************************/
|
||||
/* os_apple_embedded.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "os_apple_embedded.h"
|
||||
|
||||
#ifdef APPLE_EMBEDDED_ENABLED
|
||||
|
||||
#import "app_delegate_service.h"
|
||||
#import "display_server_apple_embedded.h"
|
||||
#import "godot_view_apple_embedded.h"
|
||||
#import "view_controller.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/io/dir_access.h"
|
||||
#include "core/io/file_access.h"
|
||||
#import "drivers/apple/os_log_logger.h"
|
||||
#include "main/main.h"
|
||||
|
||||
#import <AVFoundation/AVFAudio.h>
|
||||
#import <AudioToolbox/AudioServices.h>
|
||||
#import <CoreText/CoreText.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <dlfcn.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#if defined(RD_ENABLED)
|
||||
#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
|
||||
#import <QuartzCore/CAMetalLayer.h>
|
||||
|
||||
#if defined(VULKAN_ENABLED)
|
||||
#include "drivers/vulkan/godot_vulkan.h"
|
||||
#endif // VULKAN_ENABLED
|
||||
#endif
|
||||
|
||||
// Initialization order between compilation units is not guaranteed,
|
||||
// so we use this as a hack to ensure certain code is called before
|
||||
// everything else, but after all units are initialized.
|
||||
typedef void (*init_callback)();
|
||||
static init_callback *apple_init_callbacks = nullptr;
|
||||
static int apple_embedded_platform_init_callbacks_count = 0;
|
||||
static int apple_embedded_platform_init_callbacks_capacity = 0;
|
||||
HashMap<String, void *> OS_AppleEmbedded::dynamic_symbol_lookup_table;
|
||||
|
||||
void add_apple_embedded_platform_init_callback(init_callback cb) {
|
||||
if (apple_embedded_platform_init_callbacks_count == apple_embedded_platform_init_callbacks_capacity) {
|
||||
void *new_ptr = realloc(apple_init_callbacks, sizeof(cb) * (apple_embedded_platform_init_callbacks_capacity + 32));
|
||||
if (new_ptr) {
|
||||
apple_init_callbacks = (init_callback *)(new_ptr);
|
||||
apple_embedded_platform_init_callbacks_capacity += 32;
|
||||
} else {
|
||||
ERR_FAIL_MSG("Unable to allocate memory for extension callbacks.");
|
||||
}
|
||||
}
|
||||
apple_init_callbacks[apple_embedded_platform_init_callbacks_count++] = cb;
|
||||
}
|
||||
|
||||
void register_dynamic_symbol(char *name, void *address) {
|
||||
OS_AppleEmbedded::dynamic_symbol_lookup_table[String(name)] = address;
|
||||
}
|
||||
|
||||
Rect2 fit_keep_aspect_centered(const Vector2 &p_container, const Vector2 &p_rect) {
|
||||
real_t available_ratio = p_container.width / p_container.height;
|
||||
real_t fit_ratio = p_rect.width / p_rect.height;
|
||||
Rect2 result;
|
||||
if (fit_ratio < available_ratio) {
|
||||
// Fit height - we'll have horizontal gaps
|
||||
result.size.height = p_container.height;
|
||||
result.size.width = p_container.height * fit_ratio;
|
||||
result.position.y = 0;
|
||||
result.position.x = (p_container.width - result.size.width) * 0.5f;
|
||||
} else {
|
||||
// Fit width - we'll have vertical gaps
|
||||
result.size.width = p_container.width;
|
||||
result.size.height = p_container.width / fit_ratio;
|
||||
result.position.x = 0;
|
||||
result.position.y = (p_container.height - result.size.height) * 0.5f;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Rect2 fit_keep_aspect_covered(const Vector2 &p_container, const Vector2 &p_rect) {
|
||||
real_t available_ratio = p_container.width / p_container.height;
|
||||
real_t fit_ratio = p_rect.width / p_rect.height;
|
||||
Rect2 result;
|
||||
if (fit_ratio < available_ratio) {
|
||||
// Need to scale up to fit width, and crop height
|
||||
result.size.width = p_container.width;
|
||||
result.size.height = p_container.width / fit_ratio;
|
||||
result.position.x = 0;
|
||||
result.position.y = (p_container.height - result.size.height) * 0.5f;
|
||||
} else {
|
||||
// Need to scale up to fit height, and crop width
|
||||
result.size.width = p_container.height * fit_ratio;
|
||||
result.size.height = p_container.height;
|
||||
result.position.x = (p_container.width - result.size.width) * 0.5f;
|
||||
result.position.y = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
OS_AppleEmbedded *OS_AppleEmbedded::get_singleton() {
|
||||
return (OS_AppleEmbedded *)OS::get_singleton();
|
||||
}
|
||||
|
||||
OS_AppleEmbedded::OS_AppleEmbedded() {
|
||||
for (int i = 0; i < apple_embedded_platform_init_callbacks_count; ++i) {
|
||||
apple_init_callbacks[i]();
|
||||
}
|
||||
free(apple_init_callbacks);
|
||||
apple_init_callbacks = nullptr;
|
||||
apple_embedded_platform_init_callbacks_count = 0;
|
||||
apple_embedded_platform_init_callbacks_capacity = 0;
|
||||
|
||||
main_loop = nullptr;
|
||||
|
||||
Vector<Logger *> loggers;
|
||||
loggers.push_back(memnew(OsLogLogger(NSBundle.mainBundle.bundleIdentifier.UTF8String)));
|
||||
_set_logger(memnew(CompositeLogger(loggers)));
|
||||
|
||||
AudioDriverManager::add_driver(&audio_driver);
|
||||
}
|
||||
|
||||
OS_AppleEmbedded::~OS_AppleEmbedded() {}
|
||||
|
||||
void OS_AppleEmbedded::alert(const String &p_alert, const String &p_title) {
|
||||
const CharString utf8_alert = p_alert.utf8();
|
||||
const CharString utf8_title = p_title.utf8();
|
||||
AppleEmbedded::alert(utf8_alert.get_data(), utf8_title.get_data());
|
||||
}
|
||||
|
||||
void OS_AppleEmbedded::initialize_core() {
|
||||
OS_Unix::initialize_core();
|
||||
}
|
||||
|
||||
void OS_AppleEmbedded::initialize() {
|
||||
initialize_core();
|
||||
}
|
||||
|
||||
void OS_AppleEmbedded::initialize_joypads() {
|
||||
joypad_apple = memnew(JoypadApple);
|
||||
}
|
||||
|
||||
void OS_AppleEmbedded::initialize_modules() {
|
||||
apple_embedded = memnew(AppleEmbedded);
|
||||
Engine::get_singleton()->add_singleton(Engine::Singleton("AppleEmbedded", apple_embedded));
|
||||
}
|
||||
|
||||
void OS_AppleEmbedded::deinitialize_modules() {
|
||||
if (joypad_apple) {
|
||||
memdelete(joypad_apple);
|
||||
}
|
||||
|
||||
if (apple_embedded) {
|
||||
memdelete(apple_embedded);
|
||||
}
|
||||
}
|
||||
|
||||
void OS_AppleEmbedded::set_main_loop(MainLoop *p_main_loop) {
|
||||
main_loop = p_main_loop;
|
||||
}
|
||||
|
||||
MainLoop *OS_AppleEmbedded::get_main_loop() const {
|
||||
return main_loop;
|
||||
}
|
||||
|
||||
void OS_AppleEmbedded::delete_main_loop() {
|
||||
if (main_loop) {
|
||||
main_loop->finalize();
|
||||
memdelete(main_loop);
|
||||
}
|
||||
|
||||
main_loop = nullptr;
|
||||
}
|
||||
|
||||
bool OS_AppleEmbedded::iterate() {
|
||||
if (!main_loop) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (DisplayServer::get_singleton()) {
|
||||
DisplayServer::get_singleton()->process_events();
|
||||
}
|
||||
|
||||
joypad_apple->process_joypads();
|
||||
|
||||
return Main::iteration();
|
||||
}
|
||||
|
||||
void OS_AppleEmbedded::start() {
|
||||
if (Main::start() == EXIT_SUCCESS) {
|
||||
main_loop->initialize();
|
||||
}
|
||||
}
|
||||
|
||||
void OS_AppleEmbedded::finalize() {
|
||||
deinitialize_modules();
|
||||
|
||||
// Already gets called
|
||||
//delete_main_loop();
|
||||
}
|
||||
|
||||
// MARK: Dynamic Libraries
|
||||
|
||||
_FORCE_INLINE_ String OS_AppleEmbedded::get_framework_executable(const String &p_path) {
|
||||
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
|
||||
|
||||
// Read framework bundle to get executable name.
|
||||
NSURL *url = [NSURL fileURLWithPath:@(p_path.utf8().get_data())];
|
||||
NSBundle *bundle = [NSBundle bundleWithURL:url];
|
||||
if (bundle) {
|
||||
String exe_path = String::utf8([[bundle executablePath] UTF8String]);
|
||||
if (da->file_exists(exe_path)) {
|
||||
return exe_path;
|
||||
}
|
||||
}
|
||||
|
||||
// Try default executable name (invalid framework).
|
||||
if (da->dir_exists(p_path) && da->file_exists(p_path.path_join(p_path.get_file().get_basename()))) {
|
||||
return p_path.path_join(p_path.get_file().get_basename());
|
||||
}
|
||||
|
||||
// Not a framework, try loading as .dylib.
|
||||
return p_path;
|
||||
}
|
||||
|
||||
Error OS_AppleEmbedded::open_dynamic_library(const String &p_path, void *&p_library_handle, GDExtensionData *p_data) {
|
||||
if (p_path.length() == 0) {
|
||||
// Static xcframework.
|
||||
p_library_handle = RTLD_SELF;
|
||||
|
||||
if (p_data != nullptr && p_data->r_resolved_path != nullptr) {
|
||||
*p_data->r_resolved_path = p_path;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
String path = get_framework_executable(p_path);
|
||||
|
||||
if (!FileAccess::exists(path)) {
|
||||
// Load .dylib or framework from within the executable path.
|
||||
path = get_framework_executable(get_executable_path().get_base_dir().path_join(p_path.get_file()));
|
||||
}
|
||||
|
||||
if (!FileAccess::exists(path)) {
|
||||
// Load .dylib converted to framework from within the executable path.
|
||||
path = get_framework_executable(get_executable_path().get_base_dir().path_join(p_path.get_file().get_basename() + ".framework"));
|
||||
}
|
||||
|
||||
if (!FileAccess::exists(path)) {
|
||||
// Load .dylib or framework from a standard iOS location.
|
||||
path = get_framework_executable(get_executable_path().get_base_dir().path_join("Frameworks").path_join(p_path.get_file()));
|
||||
}
|
||||
|
||||
if (!FileAccess::exists(path)) {
|
||||
// Load .dylib converted to framework from a standard iOS location.
|
||||
path = get_framework_executable(get_executable_path().get_base_dir().path_join("Frameworks").path_join(p_path.get_file().get_basename() + ".framework"));
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V(!FileAccess::exists(path), ERR_FILE_NOT_FOUND);
|
||||
|
||||
p_library_handle = dlopen(path.utf8().get_data(), RTLD_NOW);
|
||||
ERR_FAIL_NULL_V_MSG(p_library_handle, ERR_CANT_OPEN, vformat("Can't open dynamic library: %s. Error: %s.", p_path, dlerror()));
|
||||
|
||||
if (p_data != nullptr && p_data->r_resolved_path != nullptr) {
|
||||
*p_data->r_resolved_path = path;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error OS_AppleEmbedded::close_dynamic_library(void *p_library_handle) {
|
||||
if (p_library_handle == RTLD_SELF) {
|
||||
return OK;
|
||||
}
|
||||
return OS_Unix::close_dynamic_library(p_library_handle);
|
||||
}
|
||||
|
||||
Error OS_AppleEmbedded::get_dynamic_library_symbol_handle(void *p_library_handle, const String &p_name, void *&p_symbol_handle, bool p_optional) {
|
||||
if (p_library_handle == RTLD_SELF) {
|
||||
void **ptr = OS_AppleEmbedded::dynamic_symbol_lookup_table.getptr(p_name);
|
||||
if (ptr) {
|
||||
p_symbol_handle = *ptr;
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
return OS_Unix::get_dynamic_library_symbol_handle(p_library_handle, p_name, p_symbol_handle, p_optional);
|
||||
}
|
||||
|
||||
String OS_AppleEmbedded::get_distribution_name() const {
|
||||
return get_name();
|
||||
}
|
||||
|
||||
String OS_AppleEmbedded::get_version() const {
|
||||
NSOperatingSystemVersion ver = [NSProcessInfo processInfo].operatingSystemVersion;
|
||||
return vformat("%d.%d.%d", (int64_t)ver.majorVersion, (int64_t)ver.minorVersion, (int64_t)ver.patchVersion);
|
||||
}
|
||||
|
||||
String OS_AppleEmbedded::get_model_name() const {
|
||||
String model = apple_embedded->get_model();
|
||||
if (model != "") {
|
||||
return model;
|
||||
}
|
||||
|
||||
return OS_Unix::get_model_name();
|
||||
}
|
||||
|
||||
Error OS_AppleEmbedded::shell_open(const String &p_uri) {
|
||||
NSString *urlPath = [[NSString alloc] initWithUTF8String:p_uri.utf8().get_data()];
|
||||
NSURL *url = [NSURL URLWithString:urlPath];
|
||||
|
||||
if (![[UIApplication sharedApplication] canOpenURL:url]) {
|
||||
return ERR_CANT_OPEN;
|
||||
}
|
||||
|
||||
print_verbose(vformat("Opening URL %s", p_uri));
|
||||
|
||||
[[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
String OS_AppleEmbedded::get_user_data_dir(const String &p_user_dir) const {
|
||||
static String ret;
|
||||
if (ret.is_empty()) {
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
||||
if (paths && [paths count] >= 1) {
|
||||
ret.append_utf8([[paths firstObject] UTF8String]);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
String OS_AppleEmbedded::get_cache_path() const {
|
||||
static String ret;
|
||||
if (ret.is_empty()) {
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
|
||||
if (paths && [paths count] >= 1) {
|
||||
ret.append_utf8([[paths firstObject] UTF8String]);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
String OS_AppleEmbedded::get_temp_path() const {
|
||||
static String ret;
|
||||
if (ret.is_empty()) {
|
||||
NSURL *url = [NSURL fileURLWithPath:NSTemporaryDirectory()
|
||||
isDirectory:YES];
|
||||
if (url) {
|
||||
ret = String::utf8([url.path UTF8String]);
|
||||
ret = ret.trim_prefix("file://");
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
String OS_AppleEmbedded::get_resource_dir() const {
|
||||
#ifdef TOOLS_ENABLED
|
||||
return OS_Unix::get_resource_dir();
|
||||
#else
|
||||
if (remote_fs_dir.is_empty()) {
|
||||
return OS_Unix::get_resource_dir();
|
||||
} else {
|
||||
return remote_fs_dir;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
String OS_AppleEmbedded::get_locale() const {
|
||||
NSString *preferredLanguage = [NSLocale preferredLanguages].firstObject;
|
||||
|
||||
if (preferredLanguage) {
|
||||
return String::utf8([preferredLanguage UTF8String]).replace_char('-', '_');
|
||||
}
|
||||
|
||||
NSString *localeIdentifier = [[NSLocale currentLocale] localeIdentifier];
|
||||
return String::utf8([localeIdentifier UTF8String]).replace_char('-', '_');
|
||||
}
|
||||
|
||||
String OS_AppleEmbedded::get_unique_id() const {
|
||||
NSString *uuid = [UIDevice currentDevice].identifierForVendor.UUIDString;
|
||||
return String::utf8([uuid UTF8String]);
|
||||
}
|
||||
|
||||
String OS_AppleEmbedded::get_processor_name() const {
|
||||
char buffer[256];
|
||||
size_t buffer_len = 256;
|
||||
if (sysctlbyname("machdep.cpu.brand_string", &buffer, &buffer_len, nullptr, 0) == 0) {
|
||||
return String::utf8(buffer, buffer_len);
|
||||
}
|
||||
ERR_FAIL_V_MSG("", String("Couldn't get the CPU model name. Returning an empty string."));
|
||||
}
|
||||
|
||||
Vector<String> OS_AppleEmbedded::get_system_fonts() const {
|
||||
HashSet<String> font_names;
|
||||
CFArrayRef fonts = CTFontManagerCopyAvailableFontFamilyNames();
|
||||
if (fonts) {
|
||||
for (CFIndex i = 0; i < CFArrayGetCount(fonts); i++) {
|
||||
CFStringRef cf_name = (CFStringRef)CFArrayGetValueAtIndex(fonts, i);
|
||||
if (cf_name && (CFStringGetLength(cf_name) > 0) && (CFStringCompare(cf_name, CFSTR("LastResort"), kCFCompareCaseInsensitive) != kCFCompareEqualTo) && (CFStringGetCharacterAtIndex(cf_name, 0) != '.')) {
|
||||
NSString *ns_name = (__bridge NSString *)cf_name;
|
||||
font_names.insert(String::utf8([ns_name UTF8String]));
|
||||
}
|
||||
}
|
||||
CFRelease(fonts);
|
||||
}
|
||||
|
||||
Vector<String> ret;
|
||||
for (const String &E : font_names) {
|
||||
ret.push_back(E);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
String OS_AppleEmbedded::_get_default_fontname(const String &p_font_name) const {
|
||||
String font_name = p_font_name;
|
||||
if (font_name.to_lower() == "sans-serif") {
|
||||
font_name = "Helvetica";
|
||||
} else if (font_name.to_lower() == "serif") {
|
||||
font_name = "Times";
|
||||
} else if (font_name.to_lower() == "monospace") {
|
||||
font_name = "Courier";
|
||||
} else if (font_name.to_lower() == "fantasy") {
|
||||
font_name = "Papyrus";
|
||||
} else if (font_name.to_lower() == "cursive") {
|
||||
font_name = "Apple Chancery";
|
||||
};
|
||||
return font_name;
|
||||
}
|
||||
|
||||
CGFloat OS_AppleEmbedded::_weight_to_ct(int p_weight) const {
|
||||
if (p_weight < 150) {
|
||||
return -0.80;
|
||||
} else if (p_weight < 250) {
|
||||
return -0.60;
|
||||
} else if (p_weight < 350) {
|
||||
return -0.40;
|
||||
} else if (p_weight < 450) {
|
||||
return 0.0;
|
||||
} else if (p_weight < 550) {
|
||||
return 0.23;
|
||||
} else if (p_weight < 650) {
|
||||
return 0.30;
|
||||
} else if (p_weight < 750) {
|
||||
return 0.40;
|
||||
} else if (p_weight < 850) {
|
||||
return 0.56;
|
||||
} else if (p_weight < 925) {
|
||||
return 0.62;
|
||||
} else {
|
||||
return 1.00;
|
||||
}
|
||||
}
|
||||
|
||||
CGFloat OS_AppleEmbedded::_stretch_to_ct(int p_stretch) const {
|
||||
if (p_stretch < 56) {
|
||||
return -0.5;
|
||||
} else if (p_stretch < 69) {
|
||||
return -0.37;
|
||||
} else if (p_stretch < 81) {
|
||||
return -0.25;
|
||||
} else if (p_stretch < 93) {
|
||||
return -0.13;
|
||||
} else if (p_stretch < 106) {
|
||||
return 0.0;
|
||||
} else if (p_stretch < 137) {
|
||||
return 0.13;
|
||||
} else if (p_stretch < 144) {
|
||||
return 0.25;
|
||||
} else if (p_stretch < 162) {
|
||||
return 0.37;
|
||||
} else {
|
||||
return 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
Vector<String> OS_AppleEmbedded::get_system_font_path_for_text(const String &p_font_name, const String &p_text, const String &p_locale, const String &p_script, int p_weight, int p_stretch, bool p_italic) const {
|
||||
Vector<String> ret;
|
||||
String font_name = _get_default_fontname(p_font_name);
|
||||
|
||||
CFStringRef name = CFStringCreateWithCString(kCFAllocatorDefault, font_name.utf8().get_data(), kCFStringEncodingUTF8);
|
||||
CTFontSymbolicTraits traits = 0;
|
||||
if (p_weight >= 700) {
|
||||
traits |= kCTFontBoldTrait;
|
||||
}
|
||||
if (p_italic) {
|
||||
traits |= kCTFontItalicTrait;
|
||||
}
|
||||
if (p_stretch < 100) {
|
||||
traits |= kCTFontCondensedTrait;
|
||||
} else if (p_stretch > 100) {
|
||||
traits |= kCTFontExpandedTrait;
|
||||
}
|
||||
|
||||
CFNumberRef sym_traits = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &traits);
|
||||
CFMutableDictionaryRef traits_dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr);
|
||||
CFDictionaryAddValue(traits_dict, kCTFontSymbolicTrait, sym_traits);
|
||||
|
||||
CGFloat weight = _weight_to_ct(p_weight);
|
||||
CFNumberRef font_weight = CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &weight);
|
||||
CFDictionaryAddValue(traits_dict, kCTFontWeightTrait, font_weight);
|
||||
|
||||
CGFloat stretch = _stretch_to_ct(p_stretch);
|
||||
CFNumberRef font_stretch = CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &stretch);
|
||||
CFDictionaryAddValue(traits_dict, kCTFontWidthTrait, font_stretch);
|
||||
|
||||
CFMutableDictionaryRef attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr);
|
||||
CFDictionaryAddValue(attributes, kCTFontFamilyNameAttribute, name);
|
||||
CFDictionaryAddValue(attributes, kCTFontTraitsAttribute, traits_dict);
|
||||
|
||||
CTFontDescriptorRef font = CTFontDescriptorCreateWithAttributes(attributes);
|
||||
if (font) {
|
||||
CTFontRef family = CTFontCreateWithFontDescriptor(font, 0, nullptr);
|
||||
if (family) {
|
||||
CFStringRef string = CFStringCreateWithCString(kCFAllocatorDefault, p_text.utf8().get_data(), kCFStringEncodingUTF8);
|
||||
CFRange range = CFRangeMake(0, CFStringGetLength(string));
|
||||
CTFontRef fallback_family = CTFontCreateForString(family, string, range);
|
||||
if (fallback_family) {
|
||||
CTFontDescriptorRef fallback_font = CTFontCopyFontDescriptor(fallback_family);
|
||||
if (fallback_font) {
|
||||
CFURLRef url = (CFURLRef)CTFontDescriptorCopyAttribute(fallback_font, kCTFontURLAttribute);
|
||||
if (url) {
|
||||
NSString *font_path = [NSString stringWithString:[(__bridge NSURL *)url path]];
|
||||
ret.push_back(String::utf8([font_path UTF8String]));
|
||||
CFRelease(url);
|
||||
}
|
||||
CFRelease(fallback_font);
|
||||
}
|
||||
CFRelease(fallback_family);
|
||||
}
|
||||
CFRelease(string);
|
||||
CFRelease(family);
|
||||
}
|
||||
CFRelease(font);
|
||||
}
|
||||
|
||||
CFRelease(attributes);
|
||||
CFRelease(traits_dict);
|
||||
CFRelease(sym_traits);
|
||||
CFRelease(font_stretch);
|
||||
CFRelease(font_weight);
|
||||
CFRelease(name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
String OS_AppleEmbedded::get_system_font_path(const String &p_font_name, int p_weight, int p_stretch, bool p_italic) const {
|
||||
String ret;
|
||||
String font_name = _get_default_fontname(p_font_name);
|
||||
|
||||
CFStringRef name = CFStringCreateWithCString(kCFAllocatorDefault, font_name.utf8().get_data(), kCFStringEncodingUTF8);
|
||||
|
||||
CTFontSymbolicTraits traits = 0;
|
||||
if (p_weight >= 700) {
|
||||
traits |= kCTFontBoldTrait;
|
||||
}
|
||||
if (p_italic) {
|
||||
traits |= kCTFontItalicTrait;
|
||||
}
|
||||
if (p_stretch < 100) {
|
||||
traits |= kCTFontCondensedTrait;
|
||||
} else if (p_stretch > 100) {
|
||||
traits |= kCTFontExpandedTrait;
|
||||
}
|
||||
|
||||
CFNumberRef sym_traits = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &traits);
|
||||
CFMutableDictionaryRef traits_dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr);
|
||||
CFDictionaryAddValue(traits_dict, kCTFontSymbolicTrait, sym_traits);
|
||||
|
||||
CGFloat weight = _weight_to_ct(p_weight);
|
||||
CFNumberRef font_weight = CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &weight);
|
||||
CFDictionaryAddValue(traits_dict, kCTFontWeightTrait, font_weight);
|
||||
|
||||
CGFloat stretch = _stretch_to_ct(p_stretch);
|
||||
CFNumberRef font_stretch = CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &stretch);
|
||||
CFDictionaryAddValue(traits_dict, kCTFontWidthTrait, font_stretch);
|
||||
|
||||
CFMutableDictionaryRef attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr);
|
||||
CFDictionaryAddValue(attributes, kCTFontFamilyNameAttribute, name);
|
||||
CFDictionaryAddValue(attributes, kCTFontTraitsAttribute, traits_dict);
|
||||
|
||||
CTFontDescriptorRef font = CTFontDescriptorCreateWithAttributes(attributes);
|
||||
if (font) {
|
||||
CFURLRef url = (CFURLRef)CTFontDescriptorCopyAttribute(font, kCTFontURLAttribute);
|
||||
if (url) {
|
||||
NSString *font_path = [NSString stringWithString:[(__bridge NSURL *)url path]];
|
||||
ret = String::utf8([font_path UTF8String]);
|
||||
CFRelease(url);
|
||||
}
|
||||
CFRelease(font);
|
||||
}
|
||||
|
||||
CFRelease(attributes);
|
||||
CFRelease(traits_dict);
|
||||
CFRelease(sym_traits);
|
||||
CFRelease(font_stretch);
|
||||
CFRelease(font_weight);
|
||||
CFRelease(name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void OS_AppleEmbedded::vibrate_handheld(int p_duration_ms, float p_amplitude) {
|
||||
if (apple_embedded->supports_haptic_engine()) {
|
||||
if (p_amplitude > 0.0) {
|
||||
p_amplitude = CLAMP(p_amplitude, 0.0, 1.0);
|
||||
}
|
||||
|
||||
apple_embedded->vibrate_haptic_engine((float)p_duration_ms / 1000.f, p_amplitude);
|
||||
} else {
|
||||
// iOS <13 does not support duration for vibration
|
||||
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
|
||||
}
|
||||
}
|
||||
|
||||
bool OS_AppleEmbedded::_check_internal_feature_support(const String &p_feature) {
|
||||
if (p_feature == "system_fonts") {
|
||||
return true;
|
||||
}
|
||||
if (p_feature == "mobile") {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Error OS_AppleEmbedded::setup_remote_filesystem(const String &p_server_host, int p_port, const String &p_password, String &r_project_path) {
|
||||
r_project_path = OS::get_user_data_dir();
|
||||
Error err = OS_Unix::setup_remote_filesystem(p_server_host, p_port, p_password, r_project_path);
|
||||
if (err == OK) {
|
||||
remote_fs_dir = r_project_path;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
void OS_AppleEmbedded::on_focus_out() {
|
||||
if (is_focused) {
|
||||
is_focused = false;
|
||||
|
||||
if (DisplayServerAppleEmbedded::get_singleton()) {
|
||||
DisplayServerAppleEmbedded::get_singleton()->send_window_event(DisplayServer::WINDOW_EVENT_FOCUS_OUT);
|
||||
}
|
||||
|
||||
if (OS::get_singleton()->get_main_loop()) {
|
||||
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT);
|
||||
}
|
||||
|
||||
[GDTAppDelegateService.viewController.godotView stopRendering];
|
||||
|
||||
audio_driver.stop();
|
||||
}
|
||||
}
|
||||
|
||||
void OS_AppleEmbedded::on_focus_in() {
|
||||
if (!is_focused) {
|
||||
is_focused = true;
|
||||
|
||||
if (DisplayServerAppleEmbedded::get_singleton()) {
|
||||
DisplayServerAppleEmbedded::get_singleton()->send_window_event(DisplayServer::WINDOW_EVENT_FOCUS_IN);
|
||||
}
|
||||
|
||||
if (OS::get_singleton()->get_main_loop()) {
|
||||
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN);
|
||||
}
|
||||
|
||||
[GDTAppDelegateService.viewController.godotView startRendering];
|
||||
|
||||
audio_driver.start();
|
||||
}
|
||||
}
|
||||
|
||||
void OS_AppleEmbedded::on_enter_background() {
|
||||
// Do not check for is_focused, because on_focus_out will always be fired first by applicationWillResignActive.
|
||||
|
||||
if (OS::get_singleton()->get_main_loop()) {
|
||||
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_PAUSED);
|
||||
}
|
||||
|
||||
on_focus_out();
|
||||
}
|
||||
|
||||
void OS_AppleEmbedded::on_exit_background() {
|
||||
if (!is_focused) {
|
||||
on_focus_in();
|
||||
|
||||
if (OS::get_singleton()->get_main_loop()) {
|
||||
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_RESUMED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rect2 OS_AppleEmbedded::calculate_boot_screen_rect(const Size2 &p_window_size, const Size2 &p_imgrect_size) const {
|
||||
String scalemodestr = GLOBAL_GET("ios/launch_screen_image_mode");
|
||||
|
||||
if (scalemodestr == "scaleAspectFit") {
|
||||
return fit_keep_aspect_centered(p_window_size, p_imgrect_size);
|
||||
} else if (scalemodestr == "scaleAspectFill") {
|
||||
return fit_keep_aspect_covered(p_window_size, p_imgrect_size);
|
||||
} else if (scalemodestr == "scaleToFill") {
|
||||
return Rect2(Point2(), p_window_size);
|
||||
} else if (scalemodestr == "center") {
|
||||
return OS_Unix::calculate_boot_screen_rect(p_window_size, p_imgrect_size);
|
||||
} else {
|
||||
WARN_PRINT(vformat("Boot screen scale mode mismatch between iOS and Godot: %s not supported", scalemodestr));
|
||||
return OS_Unix::calculate_boot_screen_rect(p_window_size, p_imgrect_size);
|
||||
}
|
||||
}
|
||||
|
||||
bool OS_AppleEmbedded::request_permission(const String &p_name) {
|
||||
if (p_name == "appleembedded.permission.AUDIO_RECORD") {
|
||||
if (@available(iOS 17.0, *)) {
|
||||
AVAudioApplicationRecordPermission permission = [AVAudioApplication sharedInstance].recordPermission;
|
||||
if (permission == AVAudioApplicationRecordPermissionGranted) {
|
||||
// Permission already granted, you can start recording.
|
||||
return true;
|
||||
} else if (permission == AVAudioApplicationRecordPermissionDenied) {
|
||||
// Permission denied, or not yet granted.
|
||||
return false;
|
||||
} else {
|
||||
// Request the permission, but for now return false as documented.
|
||||
[AVAudioApplication requestRecordPermissionWithCompletionHandler:^(BOOL granted) {
|
||||
get_main_loop()->emit_signal(SNAME("on_request_permissions_result"), p_name, granted);
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector<String> OS_AppleEmbedded::get_granted_permissions() const {
|
||||
Vector<String> ret;
|
||||
|
||||
if (@available(iOS 17.0, *)) {
|
||||
if ([AVAudioApplication sharedInstance].recordPermission == AVAudioApplicationRecordPermissionGranted) {
|
||||
ret.push_back("appleembedded.permission.AUDIO_RECORD");
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif // APPLE_EMBEDDED_ENABLED
|
44
drivers/apple_embedded/platform_config.h
Normal file
44
drivers/apple_embedded/platform_config.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/**************************************************************************/
|
||||
/* platform_config.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <alloca.h>
|
||||
|
||||
#define PLATFORM_THREAD_OVERRIDE
|
||||
|
||||
#define PTHREAD_RENAME_SELF
|
||||
|
||||
#define _weakify(var) __weak typeof(var) GDWeak_##var = var;
|
||||
#define _strongify(var) \
|
||||
_Pragma("clang diagnostic push") \
|
||||
_Pragma("clang diagnostic ignored \"-Wshadow\"") \
|
||||
__strong typeof(var) var = GDWeak_##var; \
|
||||
_Pragma("clang diagnostic pop")
|
@@ -0,0 +1,55 @@
|
||||
/**************************************************************************/
|
||||
/* rendering_context_driver_vulkan_apple_embedded.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef VULKAN_ENABLED
|
||||
|
||||
#include "drivers/vulkan/rendering_context_driver_vulkan.h"
|
||||
|
||||
#import <QuartzCore/CAMetalLayer.h>
|
||||
|
||||
class RenderingContextDriverVulkanAppleEmbedded : public RenderingContextDriverVulkan {
|
||||
private:
|
||||
virtual const char *_get_platform_surface_extension() const override final;
|
||||
|
||||
protected:
|
||||
SurfaceID surface_create(const void *p_platform_data) override final;
|
||||
|
||||
public:
|
||||
struct WindowPlatformData {
|
||||
CAMetalLayer *const *layer_ptr;
|
||||
};
|
||||
|
||||
RenderingContextDriverVulkanAppleEmbedded();
|
||||
~RenderingContextDriverVulkanAppleEmbedded();
|
||||
};
|
||||
|
||||
#endif // VULKAN_ENABLED
|
@@ -0,0 +1,69 @@
|
||||
/**************************************************************************/
|
||||
/* rendering_context_driver_vulkan_apple_embedded.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "rendering_context_driver_vulkan_apple_embedded.h"
|
||||
|
||||
#ifdef VULKAN_ENABLED
|
||||
|
||||
#ifdef USE_VOLK
|
||||
#include <volk.h>
|
||||
#else
|
||||
#include <vulkan/vulkan_metal.h>
|
||||
#endif
|
||||
|
||||
const char *RenderingContextDriverVulkanAppleEmbedded::_get_platform_surface_extension() const {
|
||||
return VK_EXT_METAL_SURFACE_EXTENSION_NAME;
|
||||
}
|
||||
|
||||
RenderingContextDriver::SurfaceID RenderingContextDriverVulkanAppleEmbedded::surface_create(const void *p_platform_data) {
|
||||
const WindowPlatformData *wpd = (const WindowPlatformData *)(p_platform_data);
|
||||
|
||||
VkMetalSurfaceCreateInfoEXT create_info = {};
|
||||
create_info.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT;
|
||||
create_info.pLayer = *wpd->layer_ptr;
|
||||
|
||||
VkSurfaceKHR vk_surface = VK_NULL_HANDLE;
|
||||
VkResult err = vkCreateMetalSurfaceEXT(instance_get(), &create_info, get_allocation_callbacks(VK_OBJECT_TYPE_SURFACE_KHR), &vk_surface);
|
||||
ERR_FAIL_COND_V(err != VK_SUCCESS, SurfaceID());
|
||||
|
||||
Surface *surface = memnew(Surface);
|
||||
surface->vk_surface = vk_surface;
|
||||
return SurfaceID(surface);
|
||||
}
|
||||
|
||||
RenderingContextDriverVulkanAppleEmbedded::RenderingContextDriverVulkanAppleEmbedded() {
|
||||
// Does nothing.
|
||||
}
|
||||
|
||||
RenderingContextDriverVulkanAppleEmbedded::~RenderingContextDriverVulkanAppleEmbedded() {
|
||||
// Does nothing.
|
||||
}
|
||||
|
||||
#endif // VULKAN_ENABLED
|
60
drivers/apple_embedded/tts_apple_embedded.h
Normal file
60
drivers/apple_embedded/tts_apple_embedded.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/**************************************************************************/
|
||||
/* tts_apple_embedded.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/string/ustring.h"
|
||||
#include "core/templates/hash_map.h"
|
||||
#include "core/templates/list.h"
|
||||
#include "core/variant/array.h"
|
||||
#include "servers/display_server.h"
|
||||
|
||||
#if __has_include(<AVFAudio/AVSpeechSynthesis.h>)
|
||||
#import <AVFAudio/AVSpeechSynthesis.h>
|
||||
#else
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#endif
|
||||
|
||||
@interface GDTTTS : NSObject <AVSpeechSynthesizerDelegate> {
|
||||
bool speaking;
|
||||
HashMap<id, int> ids;
|
||||
|
||||
AVSpeechSynthesizer *av_synth;
|
||||
List<DisplayServer::TTSUtterance> queue;
|
||||
}
|
||||
|
||||
- (void)pauseSpeaking;
|
||||
- (void)resumeSpeaking;
|
||||
- (void)stopSpeaking;
|
||||
- (bool)isSpeaking;
|
||||
- (bool)isPaused;
|
||||
- (void)speak:(const String &)text voice:(const String &)voice volume:(int)volume pitch:(float)pitch rate:(float)rate utterance_id:(int)utterance_id interrupt:(bool)interrupt;
|
||||
- (Array)getVoices;
|
||||
@end
|
164
drivers/apple_embedded/tts_apple_embedded.mm
Normal file
164
drivers/apple_embedded/tts_apple_embedded.mm
Normal file
@@ -0,0 +1,164 @@
|
||||
/**************************************************************************/
|
||||
/* tts_apple_embedded.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "tts_apple_embedded.h"
|
||||
|
||||
@implementation GDTTTS
|
||||
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
self->speaking = false;
|
||||
self->av_synth = [[AVSpeechSynthesizer alloc] init];
|
||||
[self->av_synth setDelegate:self];
|
||||
print_verbose("Text-to-Speech: AVSpeechSynthesizer initialized.");
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)speechSynthesizer:(AVSpeechSynthesizer *)av_synth willSpeakRangeOfSpeechString:(NSRange)characterRange utterance:(AVSpeechUtterance *)utterance {
|
||||
NSString *string = [utterance speechString];
|
||||
|
||||
// Convert from UTF-16 to UTF-32 position.
|
||||
int pos = 0;
|
||||
for (NSUInteger i = 0; i < MIN(characterRange.location, string.length); i++) {
|
||||
unichar c = [string characterAtIndex:i];
|
||||
if ((c & 0xfffffc00) == 0xd800) {
|
||||
i++;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
|
||||
DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServer::TTS_UTTERANCE_BOUNDARY, ids[utterance], pos);
|
||||
}
|
||||
|
||||
- (void)speechSynthesizer:(AVSpeechSynthesizer *)av_synth didCancelSpeechUtterance:(AVSpeechUtterance *)utterance {
|
||||
DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServer::TTS_UTTERANCE_CANCELED, ids[utterance]);
|
||||
ids.erase(utterance);
|
||||
speaking = false;
|
||||
[self update];
|
||||
}
|
||||
|
||||
- (void)speechSynthesizer:(AVSpeechSynthesizer *)av_synth didFinishSpeechUtterance:(AVSpeechUtterance *)utterance {
|
||||
DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServer::TTS_UTTERANCE_ENDED, ids[utterance]);
|
||||
ids.erase(utterance);
|
||||
speaking = false;
|
||||
[self update];
|
||||
}
|
||||
|
||||
- (void)update {
|
||||
if (!speaking && queue.size() > 0) {
|
||||
DisplayServer::TTSUtterance &message = queue.front()->get();
|
||||
|
||||
AVSpeechUtterance *new_utterance = [[AVSpeechUtterance alloc] initWithString:[NSString stringWithUTF8String:message.text.utf8().get_data()]];
|
||||
[new_utterance setVoice:[AVSpeechSynthesisVoice voiceWithIdentifier:[NSString stringWithUTF8String:message.voice.utf8().get_data()]]];
|
||||
if (message.rate > 1.f) {
|
||||
[new_utterance setRate:Math::remap(message.rate, 1.f, 10.f, AVSpeechUtteranceDefaultSpeechRate, AVSpeechUtteranceMaximumSpeechRate)];
|
||||
} else if (message.rate < 1.f) {
|
||||
[new_utterance setRate:Math::remap(message.rate, 0.1f, 1.f, AVSpeechUtteranceMinimumSpeechRate, AVSpeechUtteranceDefaultSpeechRate)];
|
||||
}
|
||||
[new_utterance setPitchMultiplier:message.pitch];
|
||||
[new_utterance setVolume:(Math::remap(message.volume, 0.f, 100.f, 0.f, 1.f))];
|
||||
|
||||
ids[new_utterance] = message.id;
|
||||
[av_synth speakUtterance:new_utterance];
|
||||
|
||||
queue.pop_front();
|
||||
|
||||
DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServer::TTS_UTTERANCE_STARTED, message.id);
|
||||
speaking = true;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)pauseSpeaking {
|
||||
[av_synth pauseSpeakingAtBoundary:AVSpeechBoundaryImmediate];
|
||||
}
|
||||
|
||||
- (void)resumeSpeaking {
|
||||
[av_synth continueSpeaking];
|
||||
}
|
||||
|
||||
- (void)stopSpeaking {
|
||||
for (DisplayServer::TTSUtterance &message : queue) {
|
||||
DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServer::TTS_UTTERANCE_CANCELED, message.id);
|
||||
}
|
||||
queue.clear();
|
||||
[av_synth stopSpeakingAtBoundary:AVSpeechBoundaryImmediate];
|
||||
speaking = false;
|
||||
}
|
||||
|
||||
- (bool)isSpeaking {
|
||||
return speaking || (queue.size() > 0);
|
||||
}
|
||||
|
||||
- (bool)isPaused {
|
||||
return [av_synth isPaused];
|
||||
}
|
||||
|
||||
- (void)speak:(const String &)text voice:(const String &)voice volume:(int)volume pitch:(float)pitch rate:(float)rate utterance_id:(int)utterance_id interrupt:(bool)interrupt {
|
||||
if (interrupt) {
|
||||
[self stopSpeaking];
|
||||
}
|
||||
|
||||
if (text.is_empty()) {
|
||||
DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServer::TTS_UTTERANCE_CANCELED, utterance_id);
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayServer::TTSUtterance message;
|
||||
message.text = text;
|
||||
message.voice = voice;
|
||||
message.volume = CLAMP(volume, 0, 100);
|
||||
message.pitch = CLAMP(pitch, 0.f, 2.f);
|
||||
message.rate = CLAMP(rate, 0.1f, 10.f);
|
||||
message.id = utterance_id;
|
||||
queue.push_back(message);
|
||||
|
||||
if ([self isPaused]) {
|
||||
[self resumeSpeaking];
|
||||
} else {
|
||||
[self update];
|
||||
}
|
||||
}
|
||||
|
||||
- (Array)getVoices {
|
||||
Array list;
|
||||
for (AVSpeechSynthesisVoice *voice in [AVSpeechSynthesisVoice speechVoices]) {
|
||||
NSString *voiceIdentifierString = [voice identifier];
|
||||
NSString *voiceLocaleIdentifier = [voice language];
|
||||
NSString *voiceName = [voice name];
|
||||
Dictionary voice_d;
|
||||
voice_d["name"] = String::utf8([voiceName UTF8String]);
|
||||
voice_d["id"] = String::utf8([voiceIdentifierString UTF8String]);
|
||||
voice_d["language"] = String::utf8([voiceLocaleIdentifier UTF8String]);
|
||||
list.push_back(voice_d);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@end
|
43
drivers/apple_embedded/view_controller.h
Normal file
43
drivers/apple_embedded/view_controller.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/**************************************************************************/
|
||||
/* view_controller.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@class GDTView;
|
||||
@class GDTKeyboardInputView;
|
||||
|
||||
@interface GDTViewController : UIViewController
|
||||
|
||||
@property(nonatomic, readonly, strong) GDTView *godotView;
|
||||
@property(nonatomic, readonly, strong) GDTKeyboardInputView *keyboardView;
|
||||
|
||||
@end
|
321
drivers/apple_embedded/view_controller.mm
Normal file
321
drivers/apple_embedded/view_controller.mm
Normal file
@@ -0,0 +1,321 @@
|
||||
/**************************************************************************/
|
||||
/* view_controller.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "view_controller.h"
|
||||
|
||||
#import "display_server_apple_embedded.h"
|
||||
#import "godot_view_apple_embedded.h"
|
||||
#import "godot_view_renderer.h"
|
||||
#import "key_mapping_apple_embedded.h"
|
||||
#import "keyboard_input_view.h"
|
||||
#import "os_apple_embedded.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <GameController/GameController.h>
|
||||
|
||||
@interface GDTViewController () <GDTViewDelegate>
|
||||
|
||||
@property(strong, nonatomic) GDTViewRenderer *renderer;
|
||||
@property(strong, nonatomic) GDTKeyboardInputView *keyboardView;
|
||||
|
||||
@property(strong, nonatomic) UIView *godotLoadingOverlay;
|
||||
|
||||
@end
|
||||
|
||||
@implementation GDTViewController
|
||||
|
||||
- (GDTView *)godotView {
|
||||
return (GDTView *)self.view;
|
||||
}
|
||||
|
||||
- (void)pressesBegan:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event {
|
||||
[super pressesBegan:presses withEvent:event];
|
||||
|
||||
if (!DisplayServerAppleEmbedded::get_singleton() || DisplayServerAppleEmbedded::get_singleton()->is_keyboard_active()) {
|
||||
return;
|
||||
}
|
||||
if (@available(iOS 13.4, *)) {
|
||||
for (UIPress *press in presses) {
|
||||
String u32lbl = String::utf8([press.key.charactersIgnoringModifiers UTF8String]);
|
||||
String u32text = String::utf8([press.key.characters UTF8String]);
|
||||
Key key = KeyMappingAppleEmbedded::remap_key(press.key.keyCode);
|
||||
|
||||
if (press.key.keyCode == 0 && u32text.is_empty() && u32lbl.is_empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char32_t us = 0;
|
||||
if (!u32lbl.is_empty() && !u32lbl.begins_with("UIKey")) {
|
||||
us = u32lbl[0];
|
||||
}
|
||||
|
||||
KeyLocation location = KeyMappingAppleEmbedded::key_location(press.key.keyCode);
|
||||
|
||||
if (!u32text.is_empty() && !u32text.begins_with("UIKey")) {
|
||||
for (int i = 0; i < u32text.length(); i++) {
|
||||
const char32_t c = u32text[i];
|
||||
DisplayServerAppleEmbedded::get_singleton()->key(fix_keycode(us, key), c, fix_key_label(us, key), key, press.key.modifierFlags, true, location);
|
||||
}
|
||||
} else {
|
||||
DisplayServerAppleEmbedded::get_singleton()->key(fix_keycode(us, key), 0, fix_key_label(us, key), key, press.key.modifierFlags, true, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event {
|
||||
[super pressesEnded:presses withEvent:event];
|
||||
|
||||
if (!DisplayServerAppleEmbedded::get_singleton() || DisplayServerAppleEmbedded::get_singleton()->is_keyboard_active()) {
|
||||
return;
|
||||
}
|
||||
if (@available(iOS 13.4, *)) {
|
||||
for (UIPress *press in presses) {
|
||||
String u32lbl = String::utf8([press.key.charactersIgnoringModifiers UTF8String]);
|
||||
Key key = KeyMappingAppleEmbedded::remap_key(press.key.keyCode);
|
||||
|
||||
if (press.key.keyCode == 0 && u32lbl.is_empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char32_t us = 0;
|
||||
if (!u32lbl.is_empty() && !u32lbl.begins_with("UIKey")) {
|
||||
us = u32lbl[0];
|
||||
}
|
||||
|
||||
KeyLocation location = KeyMappingAppleEmbedded::key_location(press.key.keyCode);
|
||||
|
||||
DisplayServerAppleEmbedded::get_singleton()->key(fix_keycode(us, key), 0, fix_key_label(us, key), key, press.key.modifierFlags, false, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)loadView {
|
||||
GDTView *view = GDTViewCreate();
|
||||
GDTViewRenderer *renderer = [[GDTViewRenderer alloc] init];
|
||||
|
||||
self.renderer = renderer;
|
||||
self.view = view;
|
||||
|
||||
view.renderer = self.renderer;
|
||||
view.delegate = self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
|
||||
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
|
||||
|
||||
if (self) {
|
||||
[self godot_commonInit];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)coder {
|
||||
self = [super initWithCoder:coder];
|
||||
|
||||
if (self) {
|
||||
[self godot_commonInit];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)godot_commonInit {
|
||||
// Initialize view controller values.
|
||||
}
|
||||
|
||||
- (void)didReceiveMemoryWarning {
|
||||
[super didReceiveMemoryWarning];
|
||||
print_verbose("Did receive memory warning!");
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
[self observeKeyboard];
|
||||
[self displayLoadingOverlay];
|
||||
|
||||
[self setNeedsUpdateOfScreenEdgesDeferringSystemGestures];
|
||||
}
|
||||
|
||||
- (void)observeKeyboard {
|
||||
print_verbose("Setting up keyboard input view.");
|
||||
self.keyboardView = [GDTKeyboardInputView new];
|
||||
[self.view addSubview:self.keyboardView];
|
||||
|
||||
print_verbose("Adding observer for keyboard show/hide.");
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
addObserver:self
|
||||
selector:@selector(keyboardOnScreen:)
|
||||
name:UIKeyboardDidShowNotification
|
||||
object:nil];
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
addObserver:self
|
||||
selector:@selector(keyboardHidden:)
|
||||
name:UIKeyboardDidHideNotification
|
||||
object:nil];
|
||||
}
|
||||
|
||||
- (void)displayLoadingOverlay {
|
||||
#if !defined(VISIONOS_ENABLED)
|
||||
NSBundle *bundle = [NSBundle mainBundle];
|
||||
NSString *storyboardName = @"Launch Screen";
|
||||
|
||||
if ([bundle pathForResource:storyboardName ofType:@"storyboardc"] == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
UIStoryboard *launchStoryboard = [UIStoryboard storyboardWithName:storyboardName bundle:bundle];
|
||||
|
||||
UIViewController *controller = [launchStoryboard instantiateInitialViewController];
|
||||
self.godotLoadingOverlay = controller.view;
|
||||
self.godotLoadingOverlay.frame = self.view.bounds;
|
||||
self.godotLoadingOverlay.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
|
||||
|
||||
[self.view addSubview:self.godotLoadingOverlay];
|
||||
#endif
|
||||
}
|
||||
|
||||
- (BOOL)godotViewFinishedSetup:(GDTView *)view {
|
||||
[self.godotLoadingOverlay removeFromSuperview];
|
||||
self.godotLoadingOverlay = nil;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
self.keyboardView = nil;
|
||||
|
||||
self.renderer = nil;
|
||||
|
||||
if (self.godotLoadingOverlay) {
|
||||
[self.godotLoadingOverlay removeFromSuperview];
|
||||
self.godotLoadingOverlay = nil;
|
||||
}
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
// MARK: Orientation
|
||||
|
||||
- (UIRectEdge)preferredScreenEdgesDeferringSystemGestures {
|
||||
if (GLOBAL_GET("display/window/ios/suppress_ui_gesture")) {
|
||||
return UIRectEdgeAll;
|
||||
} else {
|
||||
return UIRectEdgeNone;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)shouldAutorotate {
|
||||
if (!DisplayServerAppleEmbedded::get_singleton()) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
switch (DisplayServerAppleEmbedded::get_singleton()->screen_get_orientation(DisplayServer::SCREEN_OF_MAIN_WINDOW)) {
|
||||
case DisplayServer::SCREEN_SENSOR:
|
||||
case DisplayServer::SCREEN_SENSOR_LANDSCAPE:
|
||||
case DisplayServer::SCREEN_SENSOR_PORTRAIT:
|
||||
return YES;
|
||||
default:
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
|
||||
if (!DisplayServerAppleEmbedded::get_singleton()) {
|
||||
return UIInterfaceOrientationMaskAll;
|
||||
}
|
||||
|
||||
switch (DisplayServerAppleEmbedded::get_singleton()->screen_get_orientation(DisplayServer::SCREEN_OF_MAIN_WINDOW)) {
|
||||
case DisplayServer::SCREEN_PORTRAIT:
|
||||
return UIInterfaceOrientationMaskPortrait;
|
||||
case DisplayServer::SCREEN_REVERSE_LANDSCAPE:
|
||||
if (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad) {
|
||||
return UIInterfaceOrientationMaskLandscapeLeft;
|
||||
} else {
|
||||
return UIInterfaceOrientationMaskLandscapeRight;
|
||||
}
|
||||
case DisplayServer::SCREEN_REVERSE_PORTRAIT:
|
||||
return UIInterfaceOrientationMaskPortraitUpsideDown;
|
||||
case DisplayServer::SCREEN_SENSOR_LANDSCAPE:
|
||||
return UIInterfaceOrientationMaskLandscape;
|
||||
case DisplayServer::SCREEN_SENSOR_PORTRAIT:
|
||||
return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown;
|
||||
case DisplayServer::SCREEN_SENSOR:
|
||||
return UIInterfaceOrientationMaskAll;
|
||||
case DisplayServer::SCREEN_LANDSCAPE:
|
||||
if (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad) {
|
||||
return UIInterfaceOrientationMaskLandscapeRight;
|
||||
} else {
|
||||
return UIInterfaceOrientationMaskLandscapeLeft;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)prefersStatusBarHidden {
|
||||
if (GLOBAL_GET("display/window/ios/hide_status_bar")) {
|
||||
return YES;
|
||||
} else {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)prefersHomeIndicatorAutoHidden {
|
||||
if (GLOBAL_GET("display/window/ios/hide_home_indicator")) {
|
||||
return YES;
|
||||
} else {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Keyboard
|
||||
|
||||
- (void)keyboardOnScreen:(NSNotification *)notification {
|
||||
NSDictionary *info = notification.userInfo;
|
||||
NSValue *value = info[UIKeyboardFrameEndUserInfoKey];
|
||||
|
||||
CGRect rawFrame = [value CGRectValue];
|
||||
CGRect keyboardFrame = [self.view convertRect:rawFrame fromView:nil];
|
||||
|
||||
if (DisplayServerAppleEmbedded::get_singleton()) {
|
||||
DisplayServerAppleEmbedded::get_singleton()->virtual_keyboard_set_height(keyboardFrame.size.height);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)keyboardHidden:(NSNotification *)notification {
|
||||
if (DisplayServerAppleEmbedded::get_singleton()) {
|
||||
DisplayServerAppleEmbedded::get_singleton()->virtual_keyboard_set_height(0);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
Reference in New Issue
Block a user