Introduce 'drivers/apple_embedded' abstract platform for code reuse

This commit is contained in:
Ricardo Sanchez-Saez 2025-05-14 12:04:10 +01:00
parent 34f005d810
commit 457299449d
Failed to extract signature
84 changed files with 6107 additions and 5411 deletions

View file

@ -647,7 +647,11 @@ if env["scu_build"]:
# are actually handled to change compile options, etc.
detect.configure(env)
print(f'Building for platform "{env["platform"]}", architecture "{env["arch"]}", target "{env["target"]}".')
platform_string = env["platform"]
if env.get("simulator"):
platform_string += " (simulator)"
print(f'Building for platform "{platform_string}", architecture "{env["arch"]}", target "{env["target"]}".')
if env.dev_build:
print_info("Developer build, with debug optimization level and debug symbols (unless overridden).")

View file

@ -48,7 +48,7 @@ def make_default_controller_mappings(target, source, env):
"Windows": "WINDOWS",
"Mac OS X": "MACOS",
"Android": "ANDROID",
"iOS": "IOS",
"iOS": "APPLE_EMBEDDED",
"Web": "WEB",
}

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="EditorExportPlatformAppleEmbedded" inherits="EditorExportPlatform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
Base class for the Apple embedded platform exporters (iOS).
</brief_description>
<description>
The base class for Apple embedded platform exporters. These include iOS, but not macOS. See the classes inheriting from this one for more details.
</description>
<tutorials>
<link title="Exporting for iOS">$DOCS_URL/tutorials/export/exporting_for_ios.html</link>
<link title="iOS plugins documentation index">$DOCS_URL/tutorials/platform/ios/index.html</link>
</tutorials>
</class>

View file

@ -4,7 +4,7 @@
Base class for the desktop platform exporter (Windows and Linux/BSD).
</brief_description>
<description>
The base class for the desktop platform exporters. These include Windows and Linux/BSD, but not macOS. See the classes inheriting this one for more details.
The base class for the desktop platform exporters. These include Windows and Linux/BSD, but not macOS. See the classes inheriting from this one for more details.
</description>
<tutorials>
<link title="Exporting for Windows">$DOCS_URL/tutorials/export/exporting_for_windows.html</link>

View file

@ -251,6 +251,57 @@
If no modifications are needed, then an empty [PackedByteArray] should be returned.
</description>
</method>
<method name="add_apple_embedded_platform_bundle_file">
<return type="void" />
<param index="0" name="path" type="String" />
<description>
Adds an Apple embedded platform bundle file from the given [param path] to the exported project.
</description>
</method>
<method name="add_apple_embedded_platform_cpp_code">
<return type="void" />
<param index="0" name="code" type="String" />
<description>
Adds C++ code to the Apple embedded platform export. The final code is created from the code appended by each active export plugin.
</description>
</method>
<method name="add_apple_embedded_platform_embedded_framework">
<return type="void" />
<param index="0" name="path" type="String" />
<description>
Adds a dynamic library (*.dylib, *.framework) to the Linking Phase in the Apple embedded platform's Xcode project and embeds it into the resulting binary.
[b]Note:[/b] For static libraries (*.a), this works in the same way as [method add_apple_embedded_platform_framework].
[b]Note:[/b] This method should not be used for System libraries as they are already present on the device.
</description>
</method>
<method name="add_apple_embedded_platform_framework">
<return type="void" />
<param index="0" name="path" type="String" />
<description>
Adds a static library (*.a) or a dynamic library (*.dylib, *.framework) to the Linking Phase to the Apple embedded platform's Xcode project.
</description>
</method>
<method name="add_apple_embedded_platform_linker_flags">
<return type="void" />
<param index="0" name="flags" type="String" />
<description>
Adds linker flags for the Apple embedded platform export.
</description>
</method>
<method name="add_apple_embedded_platform_plist_content">
<return type="void" />
<param index="0" name="plist_content" type="String" />
<description>
Adds additional fields to the Apple embedded platform's project Info.plist file.
</description>
</method>
<method name="add_apple_embedded_platform_project_static_lib">
<return type="void" />
<param index="0" name="path" type="String" />
<description>
Adds a static library from the given [param path] to the Apple embedded platform project.
</description>
</method>
<method name="add_file">
<return type="void" />
<param index="0" name="path" type="String" />
@ -262,55 +313,55 @@
[param file] will not be imported, so consider using [method _customize_resource] to remap imported resources.
</description>
</method>
<method name="add_ios_bundle_file">
<method name="add_ios_bundle_file" deprecated="Use [method add_apple_embedded_platform_bundle_file] instead.">
<return type="void" />
<param index="0" name="path" type="String" />
<description>
Adds an iOS bundle file from the given [param path] to the exported project.
</description>
</method>
<method name="add_ios_cpp_code">
<method name="add_ios_cpp_code" deprecated="Use [method add_apple_embedded_platform_cpp_code] instead.">
<return type="void" />
<param index="0" name="code" type="String" />
<description>
Adds a C++ code to the iOS export. The final code is created from the code appended by each active export plugin.
Adds C++ code to the iOS export. The final code is created from the code appended by each active export plugin.
</description>
</method>
<method name="add_ios_embedded_framework">
<method name="add_ios_embedded_framework" deprecated="Use [method add_apple_embedded_platform_embedded_framework] instead.">
<return type="void" />
<param index="0" name="path" type="String" />
<description>
Adds a dynamic library (*.dylib, *.framework) to Linking Phase in iOS's Xcode project and embeds it into resulting binary.
[b]Note:[/b] For static libraries (*.a) works in same way as [method add_ios_framework].
[b]Note:[/b] For static libraries (*.a), this works the in same way as [method add_apple_embedded_platform_framework].
[b]Note:[/b] This method should not be used for System libraries as they are already present on the device.
</description>
</method>
<method name="add_ios_framework">
<method name="add_ios_framework" deprecated="Use [method add_apple_embedded_platform_framework] instead.">
<return type="void" />
<param index="0" name="path" type="String" />
<description>
Adds a static library (*.a) or dynamic library (*.dylib, *.framework) to Linking Phase in iOS's Xcode project.
Adds a static library (*.a) or a dynamic library (*.dylib, *.framework) to the Linking Phase to the iOS Xcode project.
</description>
</method>
<method name="add_ios_linker_flags">
<method name="add_ios_linker_flags" deprecated="Use [method add_apple_embedded_platform_linker_flags] instead.">
<return type="void" />
<param index="0" name="flags" type="String" />
<description>
Adds linker flags for the iOS export.
</description>
</method>
<method name="add_ios_plist_content">
<method name="add_ios_plist_content" deprecated="Use [method add_apple_embedded_platform_plist_content] instead.">
<return type="void" />
<param index="0" name="plist_content" type="String" />
<description>
Adds content for iOS Property List files.
Adds additional fields to the iOS project Info.plist file.
</description>
</method>
<method name="add_ios_project_static_lib">
<method name="add_ios_project_static_lib" deprecated="Use [method add_apple_embedded_platform_project_static_lib] instead.">
<return type="void" />
<param index="0" name="path" type="String" />
<description>
Adds a static lib from the given [param path] to the iOS project.
Adds a static library from the given [param path] to the iOS project.
</description>
</method>
<method name="add_macos_plugin_file">

View file

@ -14,8 +14,6 @@ SConscript("windows/SCsub")
# Sounds drivers
SConscript("alsa/SCsub")
if env["platform"] == "ios" or env["platform"] == "macos":
SConscript("coreaudio/SCsub")
SConscript("pulseaudio/SCsub")
if env["platform"] == "windows":
SConscript("wasapi/SCsub")
@ -30,6 +28,9 @@ if env["xaudio2"]:
# Shared Apple platform drivers
if env["platform"] in ["macos", "ios"]:
SConscript("apple/SCsub")
SConscript("coreaudio/SCsub")
if env["platform"] in ["ios"]:
SConscript("apple_embedded/SCsub")
# Accessibility
if env["accesskit"] and env["platform"] in ["macos", "windows", "linuxbsd"]:

View 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")

View file

@ -1,5 +1,5 @@
/**************************************************************************/
/* app_delegate.h */
/* app_delegate_service.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -32,18 +32,11 @@
#import <UIKit/UIKit.h>
@class ViewController;
@class GDTViewController;
// FIXME: Add support for both OpenGL and Vulkan when OpenGL is implemented again,
// so it can't be done with compilation time branching.
//#if defined(GLES3_ENABLED)
//@interface AppDelegate : NSObject <UIApplicationDelegate, GLViewDelegate> {
//#endif
//#if defined(VULKAN_ENABLED)
@interface AppDelegate : NSObject <UIApplicationDelegate>
//#endif
@interface GDTAppDelegateService : NSObject <UIApplicationDelegate>
@property(strong, nonatomic) UIWindow *window;
@property(strong, class, readonly, nonatomic) ViewController *viewController;
@property(strong, class, readonly, nonatomic) GDTViewController *viewController;
@end

View file

@ -1,5 +1,5 @@
/**************************************************************************/
/* app_delegate.mm */
/* app_delegate_service.mm */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -28,10 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#import "app_delegate.h"
#import "app_delegate_service.h"
#import "godot_view.h"
#import "os_ios.h"
#import "godot_view_apple_embedded.h"
#import "os_apple_embedded.h"
#import "view_controller.h"
#include "core/config/project_settings.h"
@ -46,10 +46,10 @@
extern int gargc;
extern char **gargv;
extern int ios_main(int, char **);
extern void ios_finish();
extern int apple_embedded_main(int, char **);
extern void apple_embedded_finish();
@implementation AppDelegate
@implementation GDTAppDelegateService
enum {
SESSION_CATEGORY_AMBIENT,
@ -60,9 +60,9 @@ enum {
SESSION_CATEGORY_SOLO_AMBIENT
};
static ViewController *mainViewController = nil;
static GDTViewController *mainViewController = nil;
+ (ViewController *)viewController {
+ (GDTViewController *)viewController {
return mainViewController;
}
@ -71,12 +71,11 @@ static ViewController *mainViewController = nil;
// TODO: logo screen is not displayed while shaders are compiling
// DummyViewController(Splash/LoadingViewController) -> setup -> GodotViewController
CGRect windowBounds = [[UIScreen mainScreen] bounds];
// Create a full-screen window
CGRect windowBounds = [[UIScreen mainScreen] bounds];
self.window = [[UIWindow alloc] initWithFrame:windowBounds];
int err = ios_main(gargc, gargv);
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...
@ -84,7 +83,7 @@ static ViewController *mainViewController = nil;
return NO;
}
ViewController *viewController = [[ViewController alloc] init];
GDTViewController *viewController = [[GDTViewController alloc] init];
viewController.godotView.useCADisplayLink = bool(GLOBAL_DEF("display.iOS/use_cadisplaylink", true)) ? YES : NO;
viewController.godotView.renderingInterval = 1.0 / kRenderingFrequency;
@ -135,10 +134,10 @@ static ViewController *mainViewController = nil;
if ([notification.name isEqualToString:AVAudioSessionInterruptionNotification]) {
if ([[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] isEqualToNumber:[NSNumber numberWithInt:AVAudioSessionInterruptionTypeBegan]]) {
NSLog(@"Audio interruption began");
OS_IOS::get_singleton()->on_focus_out();
OS_AppleEmbedded::get_singleton()->on_focus_out();
} else if ([[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] isEqualToNumber:[NSNumber numberWithInt:AVAudioSessionInterruptionTypeEnded]]) {
NSLog(@"Audio interruption ended");
OS_IOS::get_singleton()->on_focus_in();
OS_AppleEmbedded::get_singleton()->on_focus_in();
}
}
}
@ -150,7 +149,7 @@ static ViewController *mainViewController = nil;
}
- (void)applicationWillTerminate:(UIApplication *)application {
ios_finish();
apple_embedded_finish();
}
// When application goes to background (e.g. user switches to another app or presses Home),
@ -164,19 +163,19 @@ static ViewController *mainViewController = nil;
// notification panel by swiping from the upper part of the screen.
- (void)applicationWillResignActive:(UIApplication *)application {
OS_IOS::get_singleton()->on_focus_out();
OS_AppleEmbedded::get_singleton()->on_focus_out();
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
OS_IOS::get_singleton()->on_focus_in();
OS_AppleEmbedded::get_singleton()->on_focus_in();
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
OS_IOS::get_singleton()->on_enter_background();
OS_AppleEmbedded::get_singleton()->on_enter_background();
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
OS_IOS::get_singleton()->on_exit_background();
OS_AppleEmbedded::get_singleton()->on_exit_background();
}
- (void)dealloc {

View 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();
};

View file

@ -1,5 +1,5 @@
/**************************************************************************/
/* ios.mm */
/* apple_embedded.mm */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -28,23 +28,23 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#import "ios.h"
#import "apple_embedded.h"
#import "app_delegate.h"
#import "app_delegate_service.h"
#import "view_controller.h"
#import <CoreHaptics/CoreHaptics.h>
#import <UIKit/UIKit.h>
#include <sys/sysctl.h>
void iOS::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_rate_url", "app_id"), &iOS::get_rate_url);
ClassDB::bind_method(D_METHOD("supports_haptic_engine"), &iOS::supports_haptic_engine);
ClassDB::bind_method(D_METHOD("start_haptic_engine"), &iOS::start_haptic_engine);
ClassDB::bind_method(D_METHOD("stop_haptic_engine"), &iOS::stop_haptic_engine);
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 iOS::supports_haptic_engine() {
bool AppleEmbedded::supports_haptic_engine() {
if (@available(iOS 13, *)) {
id<CHHapticDeviceCapability> capabilities = [CHHapticEngine capabilitiesForHardware];
return capabilities.supportsHaptics;
@ -53,7 +53,7 @@ bool iOS::supports_haptic_engine() {
return false;
}
CHHapticEngine *iOS::get_haptic_engine_instance() API_AVAILABLE(ios(13)) {
CHHapticEngine *AppleEmbedded::get_haptic_engine_instance() API_AVAILABLE(ios(13)) {
if (haptic_engine == nullptr) {
NSError *error = nullptr;
haptic_engine = [[CHHapticEngine alloc] initAndReturnError:&error];
@ -69,7 +69,7 @@ CHHapticEngine *iOS::get_haptic_engine_instance() API_AVAILABLE(ios(13)) {
return haptic_engine;
}
void iOS::vibrate_haptic_engine(float p_duration_seconds, float p_amplitude) API_AVAILABLE(ios(13)) {
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();
@ -117,10 +117,10 @@ void iOS::vibrate_haptic_engine(float p_duration_seconds, float p_amplitude) API
}
}
NSLog(@"Haptic engine is not supported in this version of iOS");
NSLog(@"Haptic engine is not supported");
}
void iOS::start_haptic_engine() {
void AppleEmbedded::start_haptic_engine() {
if (@available(iOS 13, *)) {
if (supports_haptic_engine()) {
CHHapticEngine *cur_haptic_engine = get_haptic_engine_instance();
@ -136,10 +136,10 @@ void iOS::start_haptic_engine() {
}
}
NSLog(@"Haptic engine is not supported in this version of iOS");
NSLog(@"Haptic engine is not supported");
}
void iOS::stop_haptic_engine() {
void AppleEmbedded::stop_haptic_engine() {
if (@available(iOS 13, *)) {
if (supports_haptic_engine()) {
CHHapticEngine *cur_haptic_engine = get_haptic_engine_instance();
@ -155,10 +155,10 @@ void iOS::stop_haptic_engine() {
}
}
NSLog(@"Haptic engine is not supported in this version of iOS");
NSLog(@"Haptic engine is not supported");
}
void iOS::alert(const char *p_alert, const char *p_title) {
void AppleEmbedded::alert(const char *p_alert, const char *p_title) {
NSString *title = [NSString stringWithUTF8String:p_title];
NSString *message = [NSString stringWithUTF8String:p_alert];
@ -170,10 +170,10 @@ void iOS::alert(const char *p_alert, const char *p_title) {
[alert addAction:button];
[AppDelegate.viewController presentViewController:alert animated:YES completion:nil];
[GDTAppDelegateService.viewController presentViewController:alert animated:YES completion:nil];
}
String iOS::get_model() const {
String AppleEmbedded::get_model() const {
// [[UIDevice currentDevice] model] only returns "iPad" or "iPhone".
size_t size;
sysctlbyname("hw.machine", nullptr, &size, nullptr, 0);
@ -188,7 +188,7 @@ String iOS::get_model() const {
return String::utf8(str != nullptr ? str : "");
}
String iOS::get_rate_url(int p_app_id) const {
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));
@ -197,4 +197,4 @@ String iOS::get_rate_url(int p_app_id) const {
return ret;
}
iOS::iOS() {}
AppleEmbedded::AppleEmbedded() {}

View file

@ -1,5 +1,5 @@
/**************************************************************************/
/* main.m */
/* display_layer_apple_embedded.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -28,26 +28,15 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#import "godot_app_delegate.h"
#pragma once
#import <UIKit/UIKit.h>
#include <stdio.h>
#import <QuartzCore/CAMetalLayer.h>
int gargc;
char **gargv;
@protocol GDTDisplayLayer <NSObject>
int main(int argc, char *argv[]) {
#if defined(VULKAN_ENABLED)
//MoltenVK - enable full component swizzling support
setenv("MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE", "1", 1);
#endif
- (void)startRenderDisplayLayer;
- (void)stopRenderDisplayLayer;
- (void)initializeDisplayLayer;
- (void)layoutDisplayLayer;
gargc = argc;
gargv = argv;
@autoreleasepool {
NSString *className = NSStringFromClass([GodotApplicationDelegate class]);
UIApplicationMain(argc, argv, nil, className);
}
return 0;
}
@end

View 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 {}
};

View file

@ -0,0 +1,791 @@
/**************************************************************************/
/* 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 seem not 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::get_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 {
return Size2i();
}
Size2i DisplayServerAppleEmbedded::screen_get_size(int p_screen) const {
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 {
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 {
return SCREEN_OF_MAIN_WINDOW;
}
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) {
screen_orientation = p_orientation;
if (@available(iOS 16.0, *)) {
[GDTAppDelegateService.viewController setNeedsUpdateOfSupportedInterfaceOrientations];
} else {
[UIViewController attemptRotationToDeviceOrientation];
}
}
DisplayServer::ScreenOrientation DisplayServerAppleEmbedded::screen_get_orientation(int p_screen) const {
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;
}

View file

@ -32,12 +32,12 @@
#import <UIKit/UIKit.h>
typedef NSObject<UIApplicationDelegate> ApplicationDelegateService;
typedef NSObject<UIApplicationDelegate> GDTAppDelegateServiceProtocol;
@interface GodotApplicationDelegate : NSObject <UIApplicationDelegate>
@interface GDTApplicationDelegate : NSObject <UIApplicationDelegate>
@property(class, readonly, strong) NSArray<ApplicationDelegateService *> *services;
@property(class, readonly, strong) NSArray<GDTAppDelegateServiceProtocol *> *services;
+ (void)addService:(ApplicationDelegateService *)service;
+ (void)addService:(GDTAppDelegateServiceProtocol *)service;
@end

View file

@ -1,5 +1,5 @@
/**************************************************************************/
/* godot_app_delegate.m */
/* godot_app_delegate.mm */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -30,26 +30,22 @@
#import "godot_app_delegate.h"
#import "app_delegate.h"
#import "app_delegate_service.h"
@interface GodotApplicationDelegate ()
@implementation GDTApplicationDelegate
@end
static NSMutableArray<GDTAppDelegateServiceProtocol *> *services = nil;
@implementation GodotApplicationDelegate
static NSMutableArray<ApplicationDelegateService *> *services = nil;
+ (NSArray<ApplicationDelegateService *> *)services {
+ (NSArray<GDTAppDelegateServiceProtocol *> *)services {
return services;
}
+ (void)load {
services = [NSMutableArray new];
[services addObject:[AppDelegate new]];
[services addObject:[GDTAppDelegateService new]];
}
+ (void)addService:(ApplicationDelegateService *)service {
+ (void)addService:(GDTAppDelegateServiceProtocol *)service {
if (!services || !service) {
return;
}
@ -63,7 +59,7 @@ static NSMutableArray<ApplicationDelegateService *> *services = nil;
- (UIWindow *)window {
UIWindow *result = nil;
for (ApplicationDelegateService *service in services) {
for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@ -83,7 +79,7 @@ static NSMutableArray<ApplicationDelegateService *> *services = nil;
- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey, id> *)launchOptions {
BOOL result = NO;
for (ApplicationDelegateService *service in services) {
for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@ -99,7 +95,7 @@ static NSMutableArray<ApplicationDelegateService *> *services = nil;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey, id> *)launchOptions {
BOOL result = NO;
for (ApplicationDelegateService *service in services) {
for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@ -125,7 +121,7 @@ static NSMutableArray<ApplicationDelegateService *> *services = nil;
// MARK: Life-Cycle
- (void)applicationDidBecomeActive:(UIApplication *)application {
for (ApplicationDelegateService *service in services) {
for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@ -135,7 +131,7 @@ static NSMutableArray<ApplicationDelegateService *> *services = nil;
}
- (void)applicationWillResignActive:(UIApplication *)application {
for (ApplicationDelegateService *service in services) {
for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@ -145,7 +141,7 @@ static NSMutableArray<ApplicationDelegateService *> *services = nil;
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
for (ApplicationDelegateService *service in services) {
for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@ -155,7 +151,7 @@ static NSMutableArray<ApplicationDelegateService *> *services = nil;
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
for (ApplicationDelegateService *service in services) {
for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@ -165,7 +161,7 @@ static NSMutableArray<ApplicationDelegateService *> *services = nil;
}
- (void)applicationWillTerminate:(UIApplication *)application {
for (ApplicationDelegateService *service in services) {
for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@ -177,7 +173,7 @@ static NSMutableArray<ApplicationDelegateService *> *services = nil;
// MARK: Environment Changes
- (void)applicationProtectedDataDidBecomeAvailable:(UIApplication *)application {
for (ApplicationDelegateService *service in services) {
for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@ -187,7 +183,7 @@ static NSMutableArray<ApplicationDelegateService *> *services = nil;
}
- (void)applicationProtectedDataWillBecomeUnavailable:(UIApplication *)application {
for (ApplicationDelegateService *service in services) {
for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@ -197,7 +193,7 @@ static NSMutableArray<ApplicationDelegateService *> *services = nil;
}
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
for (ApplicationDelegateService *service in services) {
for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@ -207,7 +203,7 @@ static NSMutableArray<ApplicationDelegateService *> *services = nil;
}
- (void)applicationSignificantTimeChange:(UIApplication *)application {
for (ApplicationDelegateService *service in services) {
for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@ -221,7 +217,7 @@ static NSMutableArray<ApplicationDelegateService *> *services = nil;
- (BOOL)application:(UIApplication *)application shouldSaveSecureApplicationState:(NSCoder *)coder API_AVAILABLE(ios(13.2)) {
BOOL result = NO;
for (ApplicationDelegateService *service in services) {
for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@ -237,7 +233,7 @@ static NSMutableArray<ApplicationDelegateService *> *services = nil;
- (BOOL)application:(UIApplication *)application shouldRestoreSecureApplicationState:(NSCoder *)coder API_AVAILABLE(ios(13.2)) {
BOOL result = NO;
for (ApplicationDelegateService *service in services) {
for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@ -251,7 +247,7 @@ static NSMutableArray<ApplicationDelegateService *> *services = nil;
}
- (UIViewController *)application:(UIApplication *)application viewControllerWithRestorationIdentifierPath:(NSArray<NSString *> *)identifierComponents coder:(NSCoder *)coder {
for (ApplicationDelegateService *service in services) {
for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@ -267,7 +263,7 @@ static NSMutableArray<ApplicationDelegateService *> *services = nil;
}
- (void)application:(UIApplication *)application willEncodeRestorableStateWithCoder:(NSCoder *)coder {
for (ApplicationDelegateService *service in services) {
for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@ -277,7 +273,7 @@ static NSMutableArray<ApplicationDelegateService *> *services = nil;
}
- (void)application:(UIApplication *)application didDecodeRestorableStateWithCoder:(NSCoder *)coder {
for (ApplicationDelegateService *service in services) {
for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@ -289,7 +285,7 @@ static NSMutableArray<ApplicationDelegateService *> *services = nil;
// MARK: Download Data in Background
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler {
for (ApplicationDelegateService *service in services) {
for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@ -309,7 +305,7 @@ static NSMutableArray<ApplicationDelegateService *> *services = nil;
- (BOOL)application:(UIApplication *)application willContinueUserActivityWithType:(NSString *)userActivityType {
BOOL result = NO;
for (ApplicationDelegateService *service in services) {
for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@ -325,7 +321,7 @@ static NSMutableArray<ApplicationDelegateService *> *services = nil;
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> *restorableObjects))restorationHandler {
BOOL result = NO;
for (ApplicationDelegateService *service in services) {
for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@ -339,7 +335,7 @@ static NSMutableArray<ApplicationDelegateService *> *services = nil;
}
- (void)application:(UIApplication *)application didUpdateUserActivity:(NSUserActivity *)userActivity {
for (ApplicationDelegateService *service in services) {
for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@ -349,7 +345,7 @@ static NSMutableArray<ApplicationDelegateService *> *services = nil;
}
- (void)application:(UIApplication *)application didFailToContinueUserActivityWithType:(NSString *)userActivityType error:(NSError *)error {
for (ApplicationDelegateService *service in services) {
for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@ -359,7 +355,7 @@ static NSMutableArray<ApplicationDelegateService *> *services = nil;
}
- (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL succeeded))completionHandler {
for (ApplicationDelegateService *service in services) {
for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@ -371,7 +367,7 @@ static NSMutableArray<ApplicationDelegateService *> *services = nil;
// MARK: WatchKit
- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void (^)(NSDictionary *replyInfo))reply {
for (ApplicationDelegateService *service in services) {
for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@ -383,7 +379,7 @@ static NSMutableArray<ApplicationDelegateService *> *services = nil;
// MARK: HealthKit
- (void)applicationShouldRequestHealthAuthorization:(UIApplication *)application {
for (ApplicationDelegateService *service in services) {
for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@ -395,7 +391,7 @@ static NSMutableArray<ApplicationDelegateService *> *services = nil;
// MARK: Opening an URL
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options {
for (ApplicationDelegateService *service in services) {
for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@ -413,7 +409,7 @@ static NSMutableArray<ApplicationDelegateService *> *services = nil;
- (BOOL)application:(UIApplication *)application shouldAllowExtensionPointIdentifier:(UIApplicationExtensionPointIdentifier)extensionPointIdentifier {
BOOL result = NO;
for (ApplicationDelegateService *service in services) {
for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@ -429,7 +425,7 @@ static NSMutableArray<ApplicationDelegateService *> *services = nil;
// MARK: SiriKit
- (id)application:(UIApplication *)application handlerForIntent:(INIntent *)intent API_AVAILABLE(ios(14.0)) {
for (ApplicationDelegateService *service in services) {
for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}
@ -447,7 +443,7 @@ static NSMutableArray<ApplicationDelegateService *> *services = nil;
// MARK: CloudKit
- (void)application:(UIApplication *)application userDidAcceptCloudKitShareWithMetadata:(CKShareMetadata *)cloudKitShareMetadata {
for (ApplicationDelegateService *service in services) {
for (GDTAppDelegateServiceProtocol *service in services) {
if (![service respondsToSelector:_cmd]) {
continue;
}

View file

@ -1,5 +1,5 @@
/**************************************************************************/
/* godot_view.h */
/* godot_view_apple_embedded.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -34,31 +34,39 @@
class String;
@class GodotView;
@protocol DisplayLayer;
@protocol GodotViewRendererProtocol;
@class GDTView;
@protocol GDTDisplayLayer;
@protocol GDTViewRendererProtocol;
@protocol GodotViewDelegate
@protocol GDTViewDelegate
- (BOOL)godotViewFinishedSetup:(GodotView *)view;
- (BOOL)godotViewFinishedSetup:(GDTView *)view;
@end
@interface GodotView : UIView
@interface GDTView : UIView
@property(assign, nonatomic) id<GodotViewRendererProtocol> renderer;
@property(assign, nonatomic) id<GodotViewDelegate> delegate;
@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<DisplayLayer> *renderingLayer;
@property(strong, readonly, nonatomic) CALayer<GDTDisplayLayer> *renderingLayer;
@property(assign, readonly, nonatomic) BOOL canRender;
@property(assign, nonatomic) NSTimeInterval renderingInterval;
- (CALayer<DisplayLayer> *)initializeRenderingForDriver:(NSString *)driverName;
// 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();

View file

@ -1,5 +1,5 @@
/**************************************************************************/
/* godot_view.mm */
/* godot_view_apple_embedded.mm */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -28,10 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#import "godot_view.h"
#import "godot_view_apple_embedded.h"
#import "display_layer.h"
#import "display_server_ios.h"
#import "display_layer_apple_embedded.h"
#import "display_server_apple_embedded.h"
#import "godot_view_renderer.h"
#include "core/config/project_settings.h"
@ -43,7 +43,7 @@
static const int max_touches = 32;
static const float earth_gravity = 9.80665;
@interface GodotView () {
@interface GDTView () {
UITouch *godot_touches[max_touches];
}
@ -56,48 +56,17 @@ static const float earth_gravity = 9.80665;
// Only used if CADisplayLink is not
@property(strong, nonatomic) NSTimer *animationTimer;
@property(strong, nonatomic) CALayer<DisplayLayer> *renderingLayer;
@property(strong, nonatomic) CALayer<GDTDisplayLayer> *renderingLayer;
@property(strong, nonatomic) CMMotionManager *motionManager;
@end
@implementation GodotView
@implementation GDTView
- (CALayer<DisplayLayer> *)initializeRenderingForDriver:(NSString *)driverName {
if (self.renderingLayer) {
return self.renderingLayer;
}
CALayer<DisplayLayer> *layer;
if ([driverName isEqualToString:@"vulkan"] || [driverName isEqualToString:@"metal"]) {
#if defined(TARGET_OS_SIMULATOR) && TARGET_OS_SIMULATOR
if (@available(iOS 13, *)) {
layer = [GodotMetalLayer layer];
} else {
return nil;
}
#else
layer = [GodotMetalLayer layer];
#endif
} else if ([driverName isEqualToString:@"opengl3"]) {
GODOT_CLANG_WARNING_PUSH_AND_IGNORE("-Wdeprecated-declarations") // OpenGL is deprecated in iOS 12.0.
layer = [GodotOpenGLLayer layer];
GODOT_CLANG_WARNING_POP
} else {
return nil;
}
layer.frame = self.bounds;
layer.contentsScale = self.contentScaleFactor;
[self.layer addSublayer:layer];
self.renderingLayer = layer;
[layer initializeDisplayLayer];
return self.renderingLayer;
// Implemented in subclasses
- (CALayer<GDTDisplayLayer> *)initializeRenderingForDriver:(NSString *)driverName {
return nil;
}
- (instancetype)initWithCoder:(NSCoder *)coder {
@ -150,6 +119,10 @@ static const float earth_gravity = 9.80665;
- (void)godot_commonInit {
self.contentScaleFactor = [UIScreen mainScreen].scale;
if (@available(iOS 17.0, *)) {
[self registerForTraitChanges:@[ [UITraitUserInterfaceStyle class] ] withTarget:self action:@selector(traitCollectionDidChangeWithView:previousTraitCollection:)];
}
[self initTouches];
self.multipleTouchEnabled = YES;
@ -167,7 +140,7 @@ static const float earth_gravity = 9.80665;
}
- (void)system_theme_changed {
DisplayServerIOS *ds = (DisplayServerIOS *)DisplayServer::get_singleton();
DisplayServerAppleEmbedded *ds = (DisplayServerAppleEmbedded *)DisplayServer::get_singleton();
if (ds) {
ds->emit_system_theme_changed();
}
@ -176,7 +149,13 @@ static const float earth_gravity = 9.80665;
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
if (@available(iOS 13.0, *)) {
[super traitCollectionDidChange:previousTraitCollection];
[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];
}
@ -293,8 +272,8 @@ static const float earth_gravity = 9.80665;
self.renderingLayer.frame = self.bounds;
[self.renderingLayer layoutDisplayLayer];
if (DisplayServerIOS::get_singleton()) {
DisplayServerIOS::get_singleton()->resize_window(self.bounds.size);
if (DisplayServerAppleEmbedded::get_singleton()) {
DisplayServerAppleEmbedded::get_singleton()->resize_window(self.bounds.size);
}
}
@ -357,7 +336,7 @@ static const float earth_gravity = 9.80665;
int tid = [self getTouchIDForTouch:touch];
ERR_FAIL_COND(tid == -1);
CGPoint touchPoint = [touch locationInView:self];
DisplayServerIOS::get_singleton()->touch_press(tid, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, true, touch.tapCount > 1);
DisplayServerAppleEmbedded::get_singleton()->touch_press(tid, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, true, touch.tapCount > 1);
}
}
@ -369,7 +348,7 @@ static const float earth_gravity = 9.80665;
CGPoint prev_point = [touch previousLocationInView:self];
CGFloat alt = [touch altitudeAngle];
CGVector azim = [touch azimuthUnitVectorInView:self];
DisplayServerIOS::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));
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));
}
}
@ -379,7 +358,7 @@ static const float earth_gravity = 9.80665;
ERR_FAIL_COND(tid == -1);
[self removeTouch:touch];
CGPoint touchPoint = [touch locationInView:self];
DisplayServerIOS::get_singleton()->touch_press(tid, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, false, false);
DisplayServerAppleEmbedded::get_singleton()->touch_press(tid, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, false, false);
}
}
@ -387,7 +366,7 @@ static const float earth_gravity = 9.80665;
for (UITouch *touch in touches) {
int tid = [self getTouchIDForTouch:touch];
ERR_FAIL_COND(tid == -1);
DisplayServerIOS::get_singleton()->touches_canceled(tid);
DisplayServerAppleEmbedded::get_singleton()->touches_canceled(tid);
}
[self clearTouches];
}
@ -454,28 +433,28 @@ static const float earth_gravity = 9.80665;
switch (interfaceOrientation) {
case UIInterfaceOrientationLandscapeLeft: {
DisplayServerIOS::get_singleton()->update_gravity(Vector3(gravity.x, gravity.y, gravity.z).rotated(Vector3(0, 0, 1), -Math::PI * 0.5));
DisplayServerIOS::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));
DisplayServerIOS::get_singleton()->update_magnetometer(Vector3(magnetic.x, magnetic.y, magnetic.z).rotated(Vector3(0, 0, 1), -Math::PI * 0.5));
DisplayServerIOS::get_singleton()->update_gyroscope(Vector3(rotation.x, rotation.y, rotation.z).rotated(Vector3(0, 0, 1), -Math::PI * 0.5));
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: {
DisplayServerIOS::get_singleton()->update_gravity(Vector3(gravity.x, gravity.y, gravity.z).rotated(Vector3(0, 0, 1), Math::PI * 0.5));
DisplayServerIOS::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));
DisplayServerIOS::get_singleton()->update_magnetometer(Vector3(magnetic.x, magnetic.y, magnetic.z).rotated(Vector3(0, 0, 1), Math::PI * 0.5));
DisplayServerIOS::get_singleton()->update_gyroscope(Vector3(rotation.x, rotation.y, rotation.z).rotated(Vector3(0, 0, 1), Math::PI * 0.5));
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: {
DisplayServerIOS::get_singleton()->update_gravity(Vector3(gravity.x, gravity.y, gravity.z).rotated(Vector3(0, 0, 1), Math::PI));
DisplayServerIOS::get_singleton()->update_accelerometer(Vector3(acceleration.x + gravity.x, acceleration.y + gravity.y, acceleration.z + gravity.z).rotated(Vector3(0, 0, 1), Math::PI));
DisplayServerIOS::get_singleton()->update_magnetometer(Vector3(magnetic.x, magnetic.y, magnetic.z).rotated(Vector3(0, 0, 1), Math::PI));
DisplayServerIOS::get_singleton()->update_gyroscope(Vector3(rotation.x, rotation.y, rotation.z).rotated(Vector3(0, 0, 1), Math::PI));
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
DisplayServerIOS::get_singleton()->update_gravity(Vector3(gravity.x, gravity.y, gravity.z));
DisplayServerIOS::get_singleton()->update_accelerometer(Vector3(acceleration.x + gravity.x, acceleration.y + gravity.y, acceleration.z + gravity.z));
DisplayServerIOS::get_singleton()->update_magnetometer(Vector3(magnetic.x, magnetic.y, magnetic.z));
DisplayServerIOS::get_singleton()->update_gyroscope(Vector3(rotation.x, rotation.y, rotation.z));
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;
}
}

View file

@ -32,7 +32,7 @@
#import <UIKit/UIKit.h>
@protocol GodotViewRendererProtocol <NSObject>
@protocol GDTViewRendererProtocol <NSObject>
@property(assign, readonly, nonatomic) BOOL hasFinishedSetup;
@ -41,6 +41,6 @@
@end
@interface GodotViewRenderer : NSObject <GodotViewRendererProtocol>
@interface GDTViewRenderer : NSObject <GDTViewRendererProtocol>
@end

View file

@ -30,8 +30,8 @@
#import "godot_view_renderer.h"
#import "display_server_ios.h"
#import "os_ios.h"
#import "display_server_apple_embedded.h"
#import "os_apple_embedded.h"
#include "core/config/project_settings.h"
#include "core/os/keyboard.h"
@ -44,7 +44,7 @@
#import <QuartzCore/QuartzCore.h>
#import <UIKit/UIKit.h>
@interface GodotViewRenderer ()
@interface GDTViewRenderer ()
@property(assign, nonatomic) BOOL hasFinishedProjectDataSetup;
@property(assign, nonatomic) BOOL hasStartedMain;
@ -52,7 +52,7 @@
@end
@implementation GodotViewRenderer
@implementation GDTViewRenderer
- (BOOL)setupView:(UIView *)view {
if (self.hasFinishedSetup) {
@ -70,7 +70,7 @@
if (!self.hasStartedMain) {
self.hasStartedMain = YES;
OS_IOS::get_singleton()->start();
OS_AppleEmbedded::get_singleton()->start();
return YES;
}
@ -109,11 +109,11 @@
}
- (void)renderOnView:(UIView *)view {
if (!OS_IOS::get_singleton()) {
if (!OS_AppleEmbedded::get_singleton()) {
return;
}
OS_IOS::get_singleton()->iterate();
OS_AppleEmbedded::get_singleton()->iterate();
}
@end

View file

@ -1,5 +1,5 @@
/**************************************************************************/
/* key_mapping_ios.h */
/* key_mapping_apple_embedded.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -34,8 +34,8 @@
#import <UIKit/UIKit.h>
class KeyMappingIOS {
KeyMappingIOS() {}
class KeyMappingAppleEmbedded {
KeyMappingAppleEmbedded() {}
public:
static void initialize();

View file

@ -1,5 +1,5 @@
/**************************************************************************/
/* key_mapping_ios.mm */
/* key_mapping_apple_embedded.mm */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#import "key_mapping_ios.h"
#import "key_mapping_apple_embedded.h"
#include "core/templates/hash_map.h"
@ -40,7 +40,7 @@ struct HashMapHasherKeys {
HashMap<CFIndex, Key, HashMapHasherKeys> keyusage_map;
HashMap<CFIndex, KeyLocation, HashMapHasherKeys> location_map;
void KeyMappingIOS::initialize() {
void KeyMappingAppleEmbedded::initialize() {
if (@available(iOS 13.4, *)) {
keyusage_map[UIKeyboardHIDUsageKeyboardA] = Key::A;
keyusage_map[UIKeyboardHIDUsageKeyboardB] = Key::B;
@ -185,7 +185,7 @@ void KeyMappingIOS::initialize() {
}
}
Key KeyMappingIOS::remap_key(CFIndex p_keycode) {
Key KeyMappingAppleEmbedded::remap_key(CFIndex p_keycode) {
if (@available(iOS 13.4, *)) {
const Key *key = keyusage_map.getptr(p_keycode);
if (key) {
@ -195,7 +195,7 @@ Key KeyMappingIOS::remap_key(CFIndex p_keycode) {
return Key::NONE;
}
KeyLocation KeyMappingIOS::key_location(CFIndex p_keycode) {
KeyLocation KeyMappingAppleEmbedded::key_location(CFIndex p_keycode) {
if (@available(iOS 13.4, *)) {
const KeyLocation *location = location_map.getptr(p_keycode);
if (location) {

View file

@ -32,7 +32,7 @@
#import <UIKit/UIKit.h>
@interface GodotKeyboardInputView : UITextView
@interface GDTKeyboardInputView : UITextView
- (BOOL)becomeFirstResponderWithString:(NSString *)existingString cursorStart:(NSInteger)start cursorEnd:(NSInteger)end;

View file

@ -30,19 +30,19 @@
#import "keyboard_input_view.h"
#import "display_server_ios.h"
#import "os_ios.h"
#import "display_server_apple_embedded.h"
#import "os_apple_embedded.h"
#include "core/os/keyboard.h"
@interface GodotKeyboardInputView () <UITextViewDelegate>
@interface GDTKeyboardInputView () <UITextViewDelegate>
@property(nonatomic, copy) NSString *previousText;
@property(nonatomic, assign) NSRange previousSelectedRange;
@end
@implementation GodotKeyboardInputView
@implementation GDTKeyboardInputView
- (instancetype)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
@ -116,8 +116,8 @@
- (void)deleteText:(NSInteger)charactersToDelete {
for (int i = 0; i < charactersToDelete; i++) {
DisplayServerIOS::get_singleton()->key(Key::BACKSPACE, 0, Key::BACKSPACE, Key::NONE, 0, true, KeyLocation::UNSPECIFIED);
DisplayServerIOS::get_singleton()->key(Key::BACKSPACE, 0, Key::BACKSPACE, Key::NONE, 0, false, KeyLocation::UNSPECIFIED);
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);
}
}
@ -136,8 +136,8 @@
key = Key::SPACE;
}
DisplayServerIOS::get_singleton()->key(key, character, key, Key::NONE, 0, true, KeyLocation::UNSPECIFIED);
DisplayServerIOS::get_singleton()->key(key, character, key, Key::NONE, 0, false, KeyLocation::UNSPECIFIED);
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);
}
}

View 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);

View file

@ -1,5 +1,5 @@
/**************************************************************************/
/* godot_ios.mm */
/* main_utilities.mm */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -28,15 +28,29 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#import "os_ios.h"
#include "core/string/ustring.h"
#include "main/main.h"
#import <UIKit/UIKit.h>
#include <unistd.h>
#include <cstdio>
static OS_IOS *os = nullptr;
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"];
@ -70,50 +84,12 @@ int add_cmdline(int p_argc, char **p_args) {
return p_argc;
}
int ios_main(int argc, char **argv) {
size_t len = strlen(argv[0]);
while (len--) {
if (argv[0][len] == '/') {
break;
}
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];
}
if (len >= 0) {
char path[512];
memcpy(path, argv[0], len > sizeof(path) ? sizeof(path) : len);
path[len] = 0;
chdir(path);
}
os = new OS_IOS();
// We must override main when testing is enabled
TEST_MAIN_OVERRIDE
char *fargv[64];
for (int i = 0; i < argc; i++) {
fargv[i] = argv[i];
}
fargv[argc] = nullptr;
argc = add_path(argc, fargv);
argc = add_cmdline(argc, fargv);
Error err = Main::setup(fargv[0], argc - 1, &fargv[1], false);
if (err != OK) {
if (err == ERR_HELP) { // Returned by --help and --version, so success.
return EXIT_SUCCESS;
}
return EXIT_FAILURE;
}
os->initialize_modules();
return os->get_exit_code();
}
void ios_finish() {
Main::cleanup();
delete os;
r_args[p_argc] = nullptr;
p_argc = add_path(p_argc, r_args);
p_argc = add_cmdline(p_argc, r_args);
return p_argc;
}

View file

@ -0,0 +1,137 @@
/**************************************************************************/
/* 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();
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_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;
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;
};
#endif // APPLE_EMBEDDED_ENABLED

View file

@ -0,0 +1,717 @@
/**************************************************************************/
/* 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 "terminal_logger_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"
#include "core/io/file_access_pack.h"
#include "drivers/unix/syslog_logger.h"
#include "main/main.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(TerminalLoggerAppleEmbedded));
_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_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;
}
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);
}
}
#endif // APPLE_EMBEDDED_ENABLED

View 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")

View file

@ -1,5 +1,5 @@
/**************************************************************************/
/* rendering_context_driver_vulkan_ios.h */
/* rendering_context_driver_vulkan_apple_embedded.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -36,7 +36,7 @@
#import <QuartzCore/CAMetalLayer.h>
class RenderingContextDriverVulkanIOS : public RenderingContextDriverVulkan {
class RenderingContextDriverVulkanAppleEmbedded : public RenderingContextDriverVulkan {
private:
virtual const char *_get_platform_surface_extension() const override final;
@ -48,8 +48,8 @@ public:
CAMetalLayer *const *layer_ptr;
};
RenderingContextDriverVulkanIOS();
~RenderingContextDriverVulkanIOS();
RenderingContextDriverVulkanAppleEmbedded();
~RenderingContextDriverVulkanAppleEmbedded();
};
#endif // VULKAN_ENABLED

View file

@ -1,5 +1,5 @@
/**************************************************************************/
/* rendering_context_driver_vulkan_ios.mm */
/* rendering_context_driver_vulkan_apple_embedded.mm */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#import "rendering_context_driver_vulkan_ios.h"
#import "rendering_context_driver_vulkan_apple_embedded.h"
#ifdef VULKAN_ENABLED
@ -38,11 +38,11 @@
#include <vulkan/vulkan_metal.h>
#endif
const char *RenderingContextDriverVulkanIOS::_get_platform_surface_extension() const {
const char *RenderingContextDriverVulkanAppleEmbedded::_get_platform_surface_extension() const {
return VK_EXT_METAL_SURFACE_EXTENSION_NAME;
}
RenderingContextDriver::SurfaceID RenderingContextDriverVulkanIOS::surface_create(const void *p_platform_data) {
RenderingContextDriver::SurfaceID RenderingContextDriverVulkanAppleEmbedded::surface_create(const void *p_platform_data) {
const WindowPlatformData *wpd = (const WindowPlatformData *)(p_platform_data);
VkMetalSurfaceCreateInfoEXT create_info = {};
@ -58,11 +58,11 @@ RenderingContextDriver::SurfaceID RenderingContextDriverVulkanIOS::surface_creat
return SurfaceID(surface);
}
RenderingContextDriverVulkanIOS::RenderingContextDriverVulkanIOS() {
RenderingContextDriverVulkanAppleEmbedded::RenderingContextDriverVulkanAppleEmbedded() {
// Does nothing.
}
RenderingContextDriverVulkanIOS::~RenderingContextDriverVulkanIOS() {
RenderingContextDriverVulkanAppleEmbedded::~RenderingContextDriverVulkanAppleEmbedded() {
// Does nothing.
}

View file

@ -1,5 +1,5 @@
/**************************************************************************/
/* ios_terminal_logger.h */
/* terminal_logger_apple_embedded.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -30,13 +30,13 @@
#pragma once
#ifdef IOS_ENABLED
#ifdef APPLE_EMBEDDED_ENABLED
#include "core/io/logger.h"
class IOSTerminalLogger : public StdLogger {
class TerminalLoggerAppleEmbedded : public StdLogger {
public:
virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify = false, ErrorType p_type = ERR_ERROR, const Vector<Ref<ScriptBacktrace>> &p_script_backtraces = {}) override;
};
#endif // IOS_ENABLED
#endif // APPLE_EMBEDDED_ENABLED

View file

@ -1,5 +1,5 @@
/**************************************************************************/
/* ios_terminal_logger.mm */
/* terminal_logger_apple_embedded.mm */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -28,13 +28,13 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#import "ios_terminal_logger.h"
#import "terminal_logger_apple_embedded.h"
#ifdef IOS_ENABLED
#ifdef APPLE_EMBEDDED_ENABLED
#import <os/log.h>
void IOSTerminalLogger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type, const Vector<Ref<ScriptBacktrace>> &p_script_backtraces) {
void TerminalLoggerAppleEmbedded::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type, const Vector<Ref<ScriptBacktrace>> &p_script_backtraces) {
if (!should_log(true)) {
return;
}
@ -77,4 +77,4 @@ void IOSTerminalLogger::log_error(const char *p_function, const char *p_file, in
}
}
#endif // IOS_ENABLED
#endif // APPLE_EMBEDDED_ENABLED

View file

@ -1,5 +1,5 @@
/**************************************************************************/
/* tts_ios.h */
/* tts_apple_embedded.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -42,7 +42,7 @@
#import <AVFoundation/AVFoundation.h>
#endif
@interface TTS_IOS : NSObject <AVSpeechSynthesizerDelegate> {
@interface GDTTTS : NSObject <AVSpeechSynthesizerDelegate> {
bool speaking;
HashMap<id, int> ids;

View file

@ -1,5 +1,5 @@
/**************************************************************************/
/* tts_ios.mm */
/* tts_apple_embedded.mm */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -28,9 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#import "tts_ios.h"
#import "tts_apple_embedded.h"
@implementation TTS_IOS
@implementation GDTTTS
- (id)init {
self = [super init];

View file

@ -32,13 +32,12 @@
#import <UIKit/UIKit.h>
@class GodotView;
@class GodotNativeVideoView;
@class GodotKeyboardInputView;
@class GDTView;
@class GDTKeyboardInputView;
@interface ViewController : UIViewController
@interface GDTViewController : UIViewController
@property(nonatomic, readonly, strong) GodotView *godotView;
@property(nonatomic, readonly, strong) GodotKeyboardInputView *keyboardView;
@property(nonatomic, readonly, strong) GDTView *godotView;
@property(nonatomic, readonly, strong) GDTKeyboardInputView *keyboardView;
@end

View file

@ -30,44 +30,44 @@
#import "view_controller.h"
#import "display_server_ios.h"
#import "godot_view.h"
#import "display_server_apple_embedded.h"
#import "godot_view_apple_embedded.h"
#import "godot_view_renderer.h"
#import "key_mapping_ios.h"
#import "key_mapping_apple_embedded.h"
#import "keyboard_input_view.h"
#import "os_ios.h"
#import "os_apple_embedded.h"
#include "core/config/project_settings.h"
#import <AVFoundation/AVFoundation.h>
#import <GameController/GameController.h>
@interface ViewController () <GodotViewDelegate>
@interface GDTViewController () <GDTViewDelegate>
@property(strong, nonatomic) GodotViewRenderer *renderer;
@property(strong, nonatomic) GodotKeyboardInputView *keyboardView;
@property(strong, nonatomic) GDTViewRenderer *renderer;
@property(strong, nonatomic) GDTKeyboardInputView *keyboardView;
@property(strong, nonatomic) UIView *godotLoadingOverlay;
@end
@implementation ViewController
@implementation GDTViewController
- (GodotView *)godotView {
return (GodotView *)self.view;
- (GDTView *)godotView {
return (GDTView *)self.view;
}
- (void)pressesBegan:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event {
[super pressesBegan:presses withEvent:event];
if (!DisplayServerIOS::get_singleton() || DisplayServerIOS::get_singleton()->is_keyboard_active()) {
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 = KeyMappingIOS::remap_key(press.key.keyCode);
Key key = KeyMappingAppleEmbedded::remap_key(press.key.keyCode);
if (press.key.keyCode == 0 && u32text.is_empty() && u32lbl.is_empty()) {
continue;
@ -78,15 +78,15 @@
us = u32lbl[0];
}
KeyLocation location = KeyMappingIOS::key_location(press.key.keyCode);
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];
DisplayServerIOS::get_singleton()->key(fix_keycode(us, key), c, fix_key_label(us, key), key, press.key.modifierFlags, true, location);
DisplayServerAppleEmbedded::get_singleton()->key(fix_keycode(us, key), c, fix_key_label(us, key), key, press.key.modifierFlags, true, location);
}
} else {
DisplayServerIOS::get_singleton()->key(fix_keycode(us, key), 0, fix_key_label(us, key), key, press.key.modifierFlags, true, location);
DisplayServerAppleEmbedded::get_singleton()->key(fix_keycode(us, key), 0, fix_key_label(us, key), key, press.key.modifierFlags, true, location);
}
}
}
@ -95,13 +95,13 @@
- (void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event {
[super pressesEnded:presses withEvent:event];
if (!DisplayServerIOS::get_singleton() || DisplayServerIOS::get_singleton()->is_keyboard_active()) {
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 = KeyMappingIOS::remap_key(press.key.keyCode);
Key key = KeyMappingAppleEmbedded::remap_key(press.key.keyCode);
if (press.key.keyCode == 0 && u32lbl.is_empty()) {
continue;
@ -112,16 +112,16 @@
us = u32lbl[0];
}
KeyLocation location = KeyMappingIOS::key_location(press.key.keyCode);
KeyLocation location = KeyMappingAppleEmbedded::key_location(press.key.keyCode);
DisplayServerIOS::get_singleton()->key(fix_keycode(us, key), 0, fix_key_label(us, key), key, press.key.modifierFlags, false, location);
DisplayServerAppleEmbedded::get_singleton()->key(fix_keycode(us, key), 0, fix_key_label(us, key), key, press.key.modifierFlags, false, location);
}
}
}
- (void)loadView {
GodotView *view = [[GodotView alloc] init];
GodotViewRenderer *renderer = [[GodotViewRenderer alloc] init];
GDTView *view = GDTViewCreate();
GDTViewRenderer *renderer = [[GDTViewRenderer alloc] init];
self.renderer = renderer;
self.view = view;
@ -170,7 +170,7 @@
- (void)observeKeyboard {
print_verbose("Setting up keyboard input view.");
self.keyboardView = [GodotKeyboardInputView new];
self.keyboardView = [GDTKeyboardInputView new];
[self.view addSubview:self.keyboardView];
print_verbose("Adding observer for keyboard show/hide.");
@ -204,7 +204,7 @@
[self.view addSubview:self.godotLoadingOverlay];
}
- (BOOL)godotViewFinishedSetup:(GodotView *)view {
- (BOOL)godotViewFinishedSetup:(GDTView *)view {
[self.godotLoadingOverlay removeFromSuperview];
self.godotLoadingOverlay = nil;
@ -235,11 +235,11 @@
}
- (BOOL)shouldAutorotate {
if (!DisplayServerIOS::get_singleton()) {
if (!DisplayServerAppleEmbedded::get_singleton()) {
return NO;
}
switch (DisplayServerIOS::get_singleton()->screen_get_orientation(DisplayServer::SCREEN_OF_MAIN_WINDOW)) {
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:
@ -250,11 +250,11 @@
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
if (!DisplayServerIOS::get_singleton()) {
if (!DisplayServerAppleEmbedded::get_singleton()) {
return UIInterfaceOrientationMaskAll;
}
switch (DisplayServerIOS::get_singleton()->screen_get_orientation(DisplayServer::SCREEN_OF_MAIN_WINDOW)) {
switch (DisplayServerAppleEmbedded::get_singleton()->screen_get_orientation(DisplayServer::SCREEN_OF_MAIN_WINDOW)) {
case DisplayServer::SCREEN_PORTRAIT:
return UIInterfaceOrientationMaskPortrait;
case DisplayServer::SCREEN_REVERSE_LANDSCAPE:
@ -305,14 +305,14 @@
CGRect rawFrame = [value CGRectValue];
CGRect keyboardFrame = [self.view convertRect:rawFrame fromView:nil];
if (DisplayServerIOS::get_singleton()) {
DisplayServerIOS::get_singleton()->virtual_keyboard_set_height(keyboardFrame.size.height);
if (DisplayServerAppleEmbedded::get_singleton()) {
DisplayServerAppleEmbedded::get_singleton()->virtual_keyboard_set_height(keyboardFrame.size.height);
}
}
- (void)keyboardHidden:(NSNotification *)notification {
if (DisplayServerIOS::get_singleton()) {
DisplayServerIOS::get_singleton()->virtual_keyboard_set_height(0);
if (DisplayServerAppleEmbedded::get_singleton()) {
DisplayServerAppleEmbedded::get_singleton()->virtual_keyboard_set_height(0);
}
}

View file

@ -34,7 +34,6 @@
#include "core/string/print_string.h"
#import <CoreAudio/HostTime.h>
#import <CoreServices/CoreServices.h>
Mutex MIDIDriverCoreMidi::mutex;

View file

@ -39,6 +39,9 @@ if "-std=gnu++17" in env_metal["CXXFLAGS"]:
env_metal["CXXFLAGS"].remove("-std=gnu++17")
env_metal.Append(CXXFLAGS=["-std=c++20"])
# Enable module support
env_metal.Append(CCFLAGS=["-fmodules", "-fcxx-modules"])
# Driver source files
driver_obj = []

View file

@ -4184,8 +4184,8 @@ Error RenderingDeviceDriverMetal::initialize(uint32_t p_device_index, uint32_t p
error_string += "- No support for image cube arrays.\n";
}
#if defined(IOS_ENABLED)
// iOS platform ports currently don't exit themselves when this method returns `ERR_CANT_CREATE`.
#if defined(APPLE_EMBEDDED_ENABLED)
// Apple Embedded platforms exports currently don't exit themselves when this method returns `ERR_CANT_CREATE`.
OS::get_singleton()->alert(error_string + "\nClick OK to exit (black screen will be visible).");
#else
OS::get_singleton()->alert(error_string + "\nClick OK to exit.");

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,316 @@
/**************************************************************************/
/* editor_export_platform_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 "plugin_config_apple_embedded.h"
#include "core/config/project_settings.h"
#include "core/io/file_access.h"
#include "core/io/image_loader.h"
#include "core/io/marshalls.h"
#include "core/io/resource_saver.h"
#include "core/io/zip_io.h"
#include "core/os/os.h"
#include "core/templates/safe_refcount.h"
#include "editor/editor_settings.h"
#include "editor/export/editor_export_platform.h"
#include "main/splash.gen.h"
#include "scene/resources/image_texture.h"
#include <sys/stat.h>
// Optional environment variables for defining confidential information. If any
// of these is set, they will override the values set in the credentials file.
const String ENV_APPLE_PLATFORM_PROFILE_UUID_DEBUG = "GODOT_APPLE_PLATFORM_PROVISIONING_PROFILE_UUID_DEBUG";
const String ENV_APPLE_PLATFORM_PROFILE_UUID_RELEASE = "GODOT_APPLE_PLATFORM_PROVISIONING_PROFILE_UUID_RELEASE";
const String ENV_APPLE_PLATFORM_PROFILE_SPECIFIER_DEBUG = "GODOT_APPLE_PLATFORM_PROFILE_SPECIFIER_DEBUG";
const String ENV_APPLE_PLATFORM_PROFILE_SPECIFIER_RELEASE = "GODOT_APPLE_PLATFORM_PROFILE_SPECIFIER_RELEASE";
static const String storyboard_image_scale_mode[] = {
"center",
"scaleAspectFit",
"scaleAspectFill",
"scaleToFill",
};
class EditorExportPlatformAppleEmbedded : public EditorExportPlatform {
GDCLASS(EditorExportPlatformAppleEmbedded, EditorExportPlatform);
Ref<ImageTexture> logo;
Ref<ImageTexture> run_icon;
// Plugins
mutable SafeFlag plugins_changed;
SafeFlag devices_changed;
struct Device {
String id;
String name;
bool wifi = false;
bool use_ios_deploy = false;
};
Vector<Device> devices;
Mutex device_lock;
Mutex plugins_lock;
mutable Vector<PluginConfigAppleEmbedded> plugins;
#ifdef MACOS_ENABLED
Thread check_for_changes_thread;
SafeFlag quit_request;
SafeFlag has_runnable_preset;
static bool _check_xcode_install();
static void _check_for_changes_poll_thread(void *ud);
void _update_preset_status();
#endif
typedef Error (*FileHandler)(String p_file, void *p_userdata);
static Error _walk_dir_recursive(Ref<DirAccess> &p_da, FileHandler p_handler, void *p_userdata);
static Error _codesign(String p_file, void *p_userdata);
struct AppleEmbeddedConfigData {
String pkg_name;
String binary_name;
String plist_content;
String architectures;
String linker_flags;
String cpp_code;
String modules_buildfile;
String modules_fileref;
String modules_buildphase;
String modules_buildgrp;
Vector<String> capabilities;
bool use_swift_runtime;
};
struct ExportArchitecture {
String name;
bool is_default = false;
ExportArchitecture() {}
ExportArchitecture(String p_name, bool p_is_default) {
name = p_name;
is_default = p_is_default;
}
};
struct AppleEmbeddedExportAsset {
String exported_path;
bool is_framework = false; // framework is anything linked to the binary, otherwise it's a resource
bool should_embed = false;
};
String _get_additional_plist_content();
String _get_linker_flags();
String _get_cpp_code();
void _fix_config_file(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &pfile, const AppleEmbeddedConfigData &p_config, bool p_debug);
Vector<ExportArchitecture> _get_supported_architectures() const;
Vector<String> _get_preset_architectures(const Ref<EditorExportPreset> &p_preset) const;
void _check_xcframework_content(const String &p_path, int &r_total_libs, int &r_static_libs, int &r_dylibs, int &r_frameworks) const;
Error _convert_to_framework(const String &p_source, const String &p_destination, const String &p_id) const;
void _add_assets_to_project(const String &p_out_dir, const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_project_data, const Vector<AppleEmbeddedExportAsset> &p_additional_assets);
Error _export_additional_assets(const Ref<EditorExportPreset> &p_preset, const String &p_out_dir, const Vector<String> &p_assets, bool p_is_framework, bool p_should_embed, Vector<AppleEmbeddedExportAsset> &r_exported_assets);
Error _copy_asset(const Ref<EditorExportPreset> &p_preset, const String &p_out_dir, const String &p_asset, const String *p_custom_file_name, bool p_is_framework, bool p_should_embed, Vector<AppleEmbeddedExportAsset> &r_exported_assets);
Error _export_additional_assets(const Ref<EditorExportPreset> &p_preset, const String &p_out_dir, const Vector<SharedObject> &p_libraries, Vector<AppleEmbeddedExportAsset> &r_exported_assets);
Error _export_apple_embedded_plugins(const Ref<EditorExportPreset> &p_preset, AppleEmbeddedConfigData &p_config_data, const String &dest_dir, Vector<AppleEmbeddedExportAsset> &r_exported_assets, bool p_debug);
Error _export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags, bool p_oneclick);
bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const;
protected:
struct IconInfo {
const char *preset_key;
const char *idiom;
const char *export_name;
const char *actual_size_side;
const char *scale;
const char *unscaled_size;
bool force_opaque;
};
void _blend_and_rotate(Ref<Image> &p_dst, Ref<Image> &p_src, bool p_rot);
virtual Error _export_loading_screen_file(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir) { return OK; }
virtual Error _export_icons(const Ref<EditorExportPreset> &p_preset, const String &p_iconset_dir) { return OK; }
virtual String get_platform_name() const = 0;
virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const override;
virtual void get_export_options(List<ExportOption> *r_options) const override;
virtual bool get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option) const override;
virtual String get_export_option_warning(const EditorExportPreset *p_preset, const StringName &p_name) const override;
virtual Vector<IconInfo> get_icon_infos() const = 0;
void _notification(int p_what);
virtual void get_platform_features(List<String> *r_features) const override {
r_features->push_back("mobile");
r_features->push_back("apple_embedded");
}
public:
virtual Ref<Texture2D> get_logo() const override { return logo; }
virtual Ref<Texture2D> get_run_icon() const override { return run_icon; }
virtual int get_options_count() const override;
virtual String get_options_tooltip() const override;
virtual Ref<ImageTexture> get_option_icon(int p_index) const override;
virtual String get_option_label(int p_index) const override;
virtual String get_option_tooltip(int p_index) const override;
virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) override;
virtual bool poll_export() override {
bool dc = devices_changed.is_set();
if (dc) {
// don't clear unless we're reporting true, to avoid race
devices_changed.clear();
}
return dc;
}
virtual bool should_update_export_options() override {
bool export_options_changed = plugins_changed.is_set();
if (export_options_changed) {
// don't clear unless we're reporting true, to avoid race
plugins_changed.clear();
}
return export_options_changed;
}
virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override {
List<String> list;
if (p_preset.is_valid()) {
bool project_only = p_preset->get("application/export_project_only");
if (project_only) {
list.push_back("xcodeproj");
} else {
list.push_back("ipa");
}
}
return list;
}
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override;
virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const override;
virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const override;
virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, HashSet<String> &p_features) override {
}
EditorExportPlatformAppleEmbedded(const char *p_platform_logo_svg, const char *p_run_icon_svg);
~EditorExportPlatformAppleEmbedded();
/// List the gdip files in the directory specified by the p_path parameter.
static Vector<String> list_plugin_config_files(const String &p_path, bool p_check_directories) {
Vector<String> dir_files;
Ref<DirAccess> da = DirAccess::open(p_path);
if (da.is_valid()) {
da->list_dir_begin();
while (true) {
String file = da->get_next();
if (file.is_empty()) {
break;
}
if (file == "." || file == "..") {
continue;
}
if (da->current_is_hidden()) {
continue;
}
if (da->current_is_dir()) {
if (p_check_directories) {
Vector<String> directory_files = list_plugin_config_files(p_path.path_join(file), false);
for (int i = 0; i < directory_files.size(); ++i) {
dir_files.push_back(file.path_join(directory_files[i]));
}
}
continue;
}
if (file.ends_with(PluginConfigAppleEmbedded::PLUGIN_CONFIG_EXT)) {
dir_files.push_back(file);
}
}
da->list_dir_end();
}
return dir_files;
}
static Vector<PluginConfigAppleEmbedded> get_plugins(const String &p_platform_name) {
Vector<PluginConfigAppleEmbedded> loaded_plugins;
String plugins_dir = ProjectSettings::get_singleton()->get_resource_path().path_join(p_platform_name + "/plugins");
if (DirAccess::exists(plugins_dir)) {
Vector<String> plugins_filenames = list_plugin_config_files(plugins_dir, true);
if (!plugins_filenames.is_empty()) {
Ref<ConfigFile> config_file;
for (int i = 0; i < plugins_filenames.size(); i++) {
PluginConfigAppleEmbedded config = PluginConfigAppleEmbedded::load_plugin_config(config_file, plugins_dir.path_join(plugins_filenames[i]));
if (config.valid_config) {
loaded_plugins.push_back(config);
} else {
print_error("Invalid plugin config file " + plugins_filenames[i]);
}
}
}
}
return loaded_plugins;
}
static Vector<PluginConfigAppleEmbedded> get_enabled_plugins(const String &p_platform_name, const Ref<EditorExportPreset> &p_presets) {
Vector<PluginConfigAppleEmbedded> enabled_plugins;
Vector<PluginConfigAppleEmbedded> all_plugins = get_plugins(p_platform_name);
for (int i = 0; i < all_plugins.size(); i++) {
PluginConfigAppleEmbedded plugin = all_plugins[i];
bool enabled = p_presets->get("plugins/" + plugin.name);
if (enabled) {
enabled_plugins.push_back(plugin);
}
}
return enabled_plugins;
}
};

View file

@ -67,55 +67,55 @@ void EditorExportPlugin::_add_shared_object(const SharedObject &p_shared_object)
shared_objects.push_back(p_shared_object);
}
void EditorExportPlugin::add_ios_framework(const String &p_path) {
ios_frameworks.push_back(p_path);
void EditorExportPlugin::add_apple_embedded_platform_framework(const String &p_path) {
apple_embedded_platform_frameworks.push_back(p_path);
}
void EditorExportPlugin::add_ios_embedded_framework(const String &p_path) {
ios_embedded_frameworks.push_back(p_path);
void EditorExportPlugin::add_apple_embedded_platform_embedded_framework(const String &p_path) {
apple_embedded_platform_embedded_frameworks.push_back(p_path);
}
Vector<String> EditorExportPlugin::get_ios_frameworks() const {
return ios_frameworks;
Vector<String> EditorExportPlugin::get_apple_embedded_platform_frameworks() const {
return apple_embedded_platform_frameworks;
}
Vector<String> EditorExportPlugin::get_ios_embedded_frameworks() const {
return ios_embedded_frameworks;
Vector<String> EditorExportPlugin::get_apple_embedded_platform_embedded_frameworks() const {
return apple_embedded_platform_embedded_frameworks;
}
void EditorExportPlugin::add_ios_plist_content(const String &p_plist_content) {
ios_plist_content += p_plist_content + "\n";
void EditorExportPlugin::add_apple_embedded_platform_plist_content(const String &p_plist_content) {
apple_embedded_platform_plist_content += p_plist_content + "\n";
}
String EditorExportPlugin::get_ios_plist_content() const {
return ios_plist_content;
String EditorExportPlugin::get_apple_embedded_platform_plist_content() const {
return apple_embedded_platform_plist_content;
}
void EditorExportPlugin::add_ios_linker_flags(const String &p_flags) {
if (ios_linker_flags.length() > 0) {
ios_linker_flags += ' ';
void EditorExportPlugin::add_apple_embedded_platform_linker_flags(const String &p_flags) {
if (apple_embedded_platform_linker_flags.length() > 0) {
apple_embedded_platform_linker_flags += ' ';
}
ios_linker_flags += p_flags;
apple_embedded_platform_linker_flags += p_flags;
}
String EditorExportPlugin::get_ios_linker_flags() const {
return ios_linker_flags;
String EditorExportPlugin::get_apple_embedded_platform_linker_flags() const {
return apple_embedded_platform_linker_flags;
}
void EditorExportPlugin::add_ios_bundle_file(const String &p_path) {
ios_bundle_files.push_back(p_path);
void EditorExportPlugin::add_apple_embedded_platform_bundle_file(const String &p_path) {
apple_embedded_platform_bundle_files.push_back(p_path);
}
Vector<String> EditorExportPlugin::get_ios_bundle_files() const {
return ios_bundle_files;
Vector<String> EditorExportPlugin::get_apple_embedded_platform_bundle_files() const {
return apple_embedded_platform_bundle_files;
}
void EditorExportPlugin::add_ios_cpp_code(const String &p_code) {
ios_cpp_code += p_code;
void EditorExportPlugin::add_apple_embedded_platform_cpp_code(const String &p_code) {
apple_embedded_platform_cpp_code += p_code;
}
String EditorExportPlugin::get_ios_cpp_code() const {
return ios_cpp_code;
String EditorExportPlugin::get_apple_embedded_platform_cpp_code() const {
return apple_embedded_platform_cpp_code;
}
void EditorExportPlugin::add_macos_plugin_file(const String &p_path) {
@ -126,12 +126,12 @@ const Vector<String> &EditorExportPlugin::get_macos_plugin_files() const {
return macos_plugin_files;
}
void EditorExportPlugin::add_ios_project_static_lib(const String &p_path) {
ios_project_static_libs.push_back(p_path);
void EditorExportPlugin::add_apple_embedded_platform_project_static_lib(const String &p_path) {
apple_embedded_platform_project_static_libs.push_back(p_path);
}
Vector<String> EditorExportPlugin::get_ios_project_static_libs() const {
return ios_project_static_libs;
Vector<String> EditorExportPlugin::get_apple_embedded_platform_project_static_libs() const {
return apple_embedded_platform_project_static_libs;
}
Variant EditorExportPlugin::get_option(const StringName &p_name) const {
@ -328,14 +328,26 @@ void EditorExportPlugin::skip() {
void EditorExportPlugin::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_shared_object", "path", "tags", "target"), &EditorExportPlugin::add_shared_object);
ClassDB::bind_method(D_METHOD("add_ios_project_static_lib", "path"), &EditorExportPlugin::add_ios_project_static_lib);
ClassDB::bind_method(D_METHOD("add_file", "path", "file", "remap"), &EditorExportPlugin::add_file);
ClassDB::bind_method(D_METHOD("add_ios_framework", "path"), &EditorExportPlugin::add_ios_framework);
ClassDB::bind_method(D_METHOD("add_ios_embedded_framework", "path"), &EditorExportPlugin::add_ios_embedded_framework);
ClassDB::bind_method(D_METHOD("add_ios_plist_content", "plist_content"), &EditorExportPlugin::add_ios_plist_content);
ClassDB::bind_method(D_METHOD("add_ios_linker_flags", "flags"), &EditorExportPlugin::add_ios_linker_flags);
ClassDB::bind_method(D_METHOD("add_ios_bundle_file", "path"), &EditorExportPlugin::add_ios_bundle_file);
ClassDB::bind_method(D_METHOD("add_ios_cpp_code", "code"), &EditorExportPlugin::add_ios_cpp_code);
ClassDB::bind_method(D_METHOD("add_apple_embedded_platform_project_static_lib", "path"), &EditorExportPlugin::add_apple_embedded_platform_project_static_lib);
ClassDB::bind_method(D_METHOD("add_apple_embedded_platform_framework", "path"), &EditorExportPlugin::add_apple_embedded_platform_framework);
ClassDB::bind_method(D_METHOD("add_apple_embedded_platform_embedded_framework", "path"), &EditorExportPlugin::add_apple_embedded_platform_embedded_framework);
ClassDB::bind_method(D_METHOD("add_apple_embedded_platform_plist_content", "plist_content"), &EditorExportPlugin::add_apple_embedded_platform_plist_content);
ClassDB::bind_method(D_METHOD("add_apple_embedded_platform_linker_flags", "flags"), &EditorExportPlugin::add_apple_embedded_platform_linker_flags);
ClassDB::bind_method(D_METHOD("add_apple_embedded_platform_bundle_file", "path"), &EditorExportPlugin::add_apple_embedded_platform_bundle_file);
ClassDB::bind_method(D_METHOD("add_apple_embedded_platform_cpp_code", "code"), &EditorExportPlugin::add_apple_embedded_platform_cpp_code);
#ifndef DISABLE_DEPRECATED
ClassDB::bind_method(D_METHOD("add_ios_project_static_lib", "path"), &EditorExportPlugin::add_apple_embedded_platform_project_static_lib);
ClassDB::bind_method(D_METHOD("add_ios_framework", "path"), &EditorExportPlugin::add_apple_embedded_platform_framework);
ClassDB::bind_method(D_METHOD("add_ios_embedded_framework", "path"), &EditorExportPlugin::add_apple_embedded_platform_embedded_framework);
ClassDB::bind_method(D_METHOD("add_ios_plist_content", "plist_content"), &EditorExportPlugin::add_apple_embedded_platform_plist_content);
ClassDB::bind_method(D_METHOD("add_ios_linker_flags", "flags"), &EditorExportPlugin::add_apple_embedded_platform_linker_flags);
ClassDB::bind_method(D_METHOD("add_ios_bundle_file", "path"), &EditorExportPlugin::add_apple_embedded_platform_bundle_file);
ClassDB::bind_method(D_METHOD("add_ios_cpp_code", "code"), &EditorExportPlugin::add_apple_embedded_platform_cpp_code);
#endif
ClassDB::bind_method(D_METHOD("add_macos_plugin_file", "path"), &EditorExportPlugin::add_macos_plugin_file);
ClassDB::bind_method(D_METHOD("skip"), &EditorExportPlugin::skip);
ClassDB::bind_method(D_METHOD("get_option", "name"), &EditorExportPlugin::get_option);

View file

@ -53,13 +53,13 @@ class EditorExportPlugin : public RefCounted {
Vector<ExtraFile> extra_files;
bool skipped = false;
Vector<String> ios_frameworks;
Vector<String> ios_embedded_frameworks;
Vector<String> ios_project_static_libs;
String ios_plist_content;
String ios_linker_flags;
Vector<String> ios_bundle_files;
String ios_cpp_code;
Vector<String> apple_embedded_platform_frameworks;
Vector<String> apple_embedded_platform_embedded_frameworks;
Vector<String> apple_embedded_platform_project_static_libs;
String apple_embedded_platform_plist_content;
String apple_embedded_platform_linker_flags;
Vector<String> apple_embedded_platform_bundle_files;
String apple_embedded_platform_cpp_code;
Vector<String> macos_plugin_files;
@ -70,12 +70,12 @@ class EditorExportPlugin : public RefCounted {
}
_FORCE_INLINE_ void _export_end_clear() {
ios_frameworks.clear();
ios_embedded_frameworks.clear();
ios_bundle_files.clear();
ios_plist_content = "";
ios_linker_flags = "";
ios_cpp_code = "";
apple_embedded_platform_frameworks.clear();
apple_embedded_platform_embedded_frameworks.clear();
apple_embedded_platform_bundle_files.clear();
apple_embedded_platform_plist_content = "";
apple_embedded_platform_linker_flags = "";
apple_embedded_platform_cpp_code = "";
macos_plugin_files.clear();
}
@ -95,13 +95,13 @@ protected:
void add_shared_object(const String &p_path, const Vector<String> &tags, const String &p_target = String());
void _add_shared_object(const SharedObject &p_shared_object);
void add_ios_framework(const String &p_path);
void add_ios_embedded_framework(const String &p_path);
void add_ios_project_static_lib(const String &p_path);
void add_ios_plist_content(const String &p_plist_content);
void add_ios_linker_flags(const String &p_flags);
void add_ios_bundle_file(const String &p_path);
void add_ios_cpp_code(const String &p_code);
void add_apple_embedded_platform_framework(const String &p_path);
void add_apple_embedded_platform_embedded_framework(const String &p_path);
void add_apple_embedded_platform_project_static_lib(const String &p_path);
void add_apple_embedded_platform_plist_content(const String &p_plist_content);
void add_apple_embedded_platform_linker_flags(const String &p_flags);
void add_apple_embedded_platform_bundle_file(const String &p_path);
void add_apple_embedded_platform_cpp_code(const String &p_code);
void add_macos_plugin_file(const String &p_path);
void skip();
@ -177,13 +177,13 @@ public:
virtual String get_android_manifest_element_contents(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const;
virtual PackedByteArray update_android_prebuilt_manifest(const Ref<EditorExportPlatform> &p_export_platform, const PackedByteArray &p_manifest_data) const;
Vector<String> get_ios_frameworks() const;
Vector<String> get_ios_embedded_frameworks() const;
Vector<String> get_ios_project_static_libs() const;
String get_ios_plist_content() const;
String get_ios_linker_flags() const;
Vector<String> get_ios_bundle_files() const;
String get_ios_cpp_code() const;
Vector<String> get_apple_embedded_platform_frameworks() const;
Vector<String> get_apple_embedded_platform_embedded_frameworks() const;
Vector<String> get_apple_embedded_platform_project_static_libs() const;
String get_apple_embedded_platform_plist_content() const;
String get_apple_embedded_platform_linker_flags() const;
Vector<String> get_apple_embedded_platform_bundle_files() const;
String get_apple_embedded_platform_cpp_code() const;
const Vector<String> &get_macos_plugin_files() const;
Variant get_option(const StringName &p_name) const;
};

View file

@ -1,5 +1,5 @@
/**************************************************************************/
/* godot_plugin_config.cpp */
/* plugin_config_apple_embedded.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -28,13 +28,13 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "godot_plugin_config.h"
#include "plugin_config_apple_embedded.h"
#include "core/config/project_settings.h"
#include "core/io/dir_access.h"
#include "core/io/file_access.h"
String PluginConfigIOS::resolve_local_dependency_path(String plugin_config_dir, String dependency_path) {
String PluginConfigAppleEmbedded::resolve_local_dependency_path(String plugin_config_dir, String dependency_path) {
String absolute_path;
if (dependency_path.is_empty()) {
@ -51,7 +51,7 @@ String PluginConfigIOS::resolve_local_dependency_path(String plugin_config_dir,
return absolute_path.replace(res_path, "res://");
}
String PluginConfigIOS::resolve_system_dependency_path(String dependency_path) {
String PluginConfigAppleEmbedded::resolve_system_dependency_path(String dependency_path) {
String absolute_path;
if (dependency_path.is_empty()) {
@ -67,7 +67,7 @@ String PluginConfigIOS::resolve_system_dependency_path(String dependency_path) {
return system_path.path_join(dependency_path);
}
Vector<String> PluginConfigIOS::resolve_local_dependencies(String plugin_config_dir, Vector<String> p_paths) {
Vector<String> PluginConfigAppleEmbedded::resolve_local_dependencies(String plugin_config_dir, Vector<String> p_paths) {
Vector<String> paths;
for (int i = 0; i < p_paths.size(); i++) {
@ -83,7 +83,7 @@ Vector<String> PluginConfigIOS::resolve_local_dependencies(String plugin_config_
return paths;
}
Vector<String> PluginConfigIOS::resolve_system_dependencies(Vector<String> p_paths) {
Vector<String> PluginConfigAppleEmbedded::resolve_system_dependencies(Vector<String> p_paths) {
Vector<String> paths;
for (int i = 0; i < p_paths.size(); i++) {
@ -99,7 +99,7 @@ Vector<String> PluginConfigIOS::resolve_system_dependencies(Vector<String> p_pat
return paths;
}
bool PluginConfigIOS::validate_plugin(PluginConfigIOS &plugin_config) {
bool PluginConfigAppleEmbedded::validate_plugin(PluginConfigAppleEmbedded &plugin_config) {
bool valid_name = !plugin_config.name.is_empty();
bool valid_binary_name = !plugin_config.binary.is_empty();
bool valid_initialize = !plugin_config.initialization_method.is_empty();
@ -134,7 +134,7 @@ bool PluginConfigIOS::validate_plugin(PluginConfigIOS &plugin_config) {
return plugin_config.valid_config;
}
String PluginConfigIOS::get_plugin_main_binary(PluginConfigIOS &plugin_config, bool p_debug) {
String PluginConfigAppleEmbedded::get_plugin_main_binary(PluginConfigAppleEmbedded &plugin_config, bool p_debug) {
if (!plugin_config.supports_targets) {
return plugin_config.binary;
}
@ -147,7 +147,7 @@ String PluginConfigIOS::get_plugin_main_binary(PluginConfigIOS &plugin_config, b
return plugin_binary_dir.path_join(plugin_file);
}
uint64_t PluginConfigIOS::get_plugin_modification_time(const PluginConfigIOS &plugin_config, const String &config_path) {
uint64_t PluginConfigAppleEmbedded::get_plugin_modification_time(const PluginConfigAppleEmbedded &plugin_config, const String &config_path) {
uint64_t last_updated = FileAccess::get_modified_time(config_path);
if (!plugin_config.supports_targets) {
@ -166,8 +166,8 @@ uint64_t PluginConfigIOS::get_plugin_modification_time(const PluginConfigIOS &pl
return last_updated;
}
PluginConfigIOS PluginConfigIOS::load_plugin_config(Ref<ConfigFile> config_file, const String &path) {
PluginConfigIOS plugin_config = {};
PluginConfigAppleEmbedded PluginConfigAppleEmbedded::load_plugin_config(Ref<ConfigFile> config_file, const String &path) {
PluginConfigAppleEmbedded plugin_config = {};
if (config_file.is_null()) {
return plugin_config;
@ -183,19 +183,19 @@ PluginConfigIOS PluginConfigIOS::load_plugin_config(Ref<ConfigFile> config_file,
String config_base_dir = path.get_base_dir();
plugin_config.name = config_file->get_value(PluginConfigIOS::CONFIG_SECTION, PluginConfigIOS::CONFIG_NAME_KEY, String());
plugin_config.use_swift_runtime = config_file->get_value(PluginConfigIOS::CONFIG_SECTION, PluginConfigIOS::CONFIG_USE_SWIFT_KEY, false);
plugin_config.initialization_method = config_file->get_value(PluginConfigIOS::CONFIG_SECTION, PluginConfigIOS::CONFIG_INITIALIZE_KEY, String());
plugin_config.deinitialization_method = config_file->get_value(PluginConfigIOS::CONFIG_SECTION, PluginConfigIOS::CONFIG_DEINITIALIZE_KEY, String());
plugin_config.name = config_file->get_value(PluginConfigAppleEmbedded::CONFIG_SECTION, PluginConfigAppleEmbedded::CONFIG_NAME_KEY, String());
plugin_config.use_swift_runtime = config_file->get_value(PluginConfigAppleEmbedded::CONFIG_SECTION, PluginConfigAppleEmbedded::CONFIG_USE_SWIFT_KEY, false);
plugin_config.initialization_method = config_file->get_value(PluginConfigAppleEmbedded::CONFIG_SECTION, PluginConfigAppleEmbedded::CONFIG_INITIALIZE_KEY, String());
plugin_config.deinitialization_method = config_file->get_value(PluginConfigAppleEmbedded::CONFIG_SECTION, PluginConfigAppleEmbedded::CONFIG_DEINITIALIZE_KEY, String());
String binary_path = config_file->get_value(PluginConfigIOS::CONFIG_SECTION, PluginConfigIOS::CONFIG_BINARY_KEY, String());
String binary_path = config_file->get_value(PluginConfigAppleEmbedded::CONFIG_SECTION, PluginConfigAppleEmbedded::CONFIG_BINARY_KEY, String());
plugin_config.binary = resolve_local_dependency_path(config_base_dir, binary_path);
if (config_file->has_section(PluginConfigIOS::DEPENDENCIES_SECTION)) {
Vector<String> linked_dependencies = config_file->get_value(PluginConfigIOS::DEPENDENCIES_SECTION, PluginConfigIOS::DEPENDENCIES_LINKED_KEY, Vector<String>());
Vector<String> embedded_dependencies = config_file->get_value(PluginConfigIOS::DEPENDENCIES_SECTION, PluginConfigIOS::DEPENDENCIES_EMBEDDED_KEY, Vector<String>());
Vector<String> system_dependencies = config_file->get_value(PluginConfigIOS::DEPENDENCIES_SECTION, PluginConfigIOS::DEPENDENCIES_SYSTEM_KEY, Vector<String>());
Vector<String> files = config_file->get_value(PluginConfigIOS::DEPENDENCIES_SECTION, PluginConfigIOS::DEPENDENCIES_FILES_KEY, Vector<String>());
if (config_file->has_section(PluginConfigAppleEmbedded::DEPENDENCIES_SECTION)) {
Vector<String> linked_dependencies = config_file->get_value(PluginConfigAppleEmbedded::DEPENDENCIES_SECTION, PluginConfigAppleEmbedded::DEPENDENCIES_LINKED_KEY, Vector<String>());
Vector<String> embedded_dependencies = config_file->get_value(PluginConfigAppleEmbedded::DEPENDENCIES_SECTION, PluginConfigAppleEmbedded::DEPENDENCIES_EMBEDDED_KEY, Vector<String>());
Vector<String> system_dependencies = config_file->get_value(PluginConfigAppleEmbedded::DEPENDENCIES_SECTION, PluginConfigAppleEmbedded::DEPENDENCIES_SYSTEM_KEY, Vector<String>());
Vector<String> files = config_file->get_value(PluginConfigAppleEmbedded::DEPENDENCIES_SECTION, PluginConfigAppleEmbedded::DEPENDENCIES_FILES_KEY, Vector<String>());
plugin_config.linked_dependencies = resolve_local_dependencies(config_base_dir, linked_dependencies);
plugin_config.embedded_dependencies = resolve_local_dependencies(config_base_dir, embedded_dependencies);
@ -203,77 +203,77 @@ PluginConfigIOS PluginConfigIOS::load_plugin_config(Ref<ConfigFile> config_file,
plugin_config.files_to_copy = resolve_local_dependencies(config_base_dir, files);
plugin_config.capabilities = config_file->get_value(PluginConfigIOS::DEPENDENCIES_SECTION, PluginConfigIOS::DEPENDENCIES_CAPABILITIES_KEY, Vector<String>());
plugin_config.capabilities = config_file->get_value(PluginConfigAppleEmbedded::DEPENDENCIES_SECTION, PluginConfigAppleEmbedded::DEPENDENCIES_CAPABILITIES_KEY, Vector<String>());
plugin_config.linker_flags = config_file->get_value(PluginConfigIOS::DEPENDENCIES_SECTION, PluginConfigIOS::DEPENDENCIES_LINKER_FLAGS, Vector<String>());
plugin_config.linker_flags = config_file->get_value(PluginConfigAppleEmbedded::DEPENDENCIES_SECTION, PluginConfigAppleEmbedded::DEPENDENCIES_LINKER_FLAGS, Vector<String>());
}
if (config_file->has_section(PluginConfigIOS::PLIST_SECTION)) {
Vector<String> keys = config_file->get_section_keys(PluginConfigIOS::PLIST_SECTION);
if (config_file->has_section(PluginConfigAppleEmbedded::PLIST_SECTION)) {
Vector<String> keys = config_file->get_section_keys(PluginConfigAppleEmbedded::PLIST_SECTION);
for (const String &key : keys) {
Vector<String> key_components = key.split(":");
String key_value = "";
PluginConfigIOS::PlistItemType key_type = PluginConfigIOS::PlistItemType::UNKNOWN;
PluginConfigAppleEmbedded::PlistItemType key_type = PluginConfigAppleEmbedded::PlistItemType::UNKNOWN;
if (key_components.size() == 1) {
key_value = key_components[0];
key_type = PluginConfigIOS::PlistItemType::STRING;
key_type = PluginConfigAppleEmbedded::PlistItemType::STRING;
} else if (key_components.size() == 2) {
key_value = key_components[0];
if (key_components[1].to_lower() == "string") {
key_type = PluginConfigIOS::PlistItemType::STRING;
key_type = PluginConfigAppleEmbedded::PlistItemType::STRING;
} else if (key_components[1].to_lower() == "integer") {
key_type = PluginConfigIOS::PlistItemType::INTEGER;
key_type = PluginConfigAppleEmbedded::PlistItemType::INTEGER;
} else if (key_components[1].to_lower() == "boolean") {
key_type = PluginConfigIOS::PlistItemType::BOOLEAN;
key_type = PluginConfigAppleEmbedded::PlistItemType::BOOLEAN;
} else if (key_components[1].to_lower() == "raw") {
key_type = PluginConfigIOS::PlistItemType::RAW;
key_type = PluginConfigAppleEmbedded::PlistItemType::RAW;
} else if (key_components[1].to_lower() == "string_input") {
key_type = PluginConfigIOS::PlistItemType::STRING_INPUT;
key_type = PluginConfigAppleEmbedded::PlistItemType::STRING_INPUT;
}
}
if (key_value.is_empty() || key_type == PluginConfigIOS::PlistItemType::UNKNOWN) {
if (key_value.is_empty() || key_type == PluginConfigAppleEmbedded::PlistItemType::UNKNOWN) {
continue;
}
String value;
switch (key_type) {
case PluginConfigIOS::PlistItemType::STRING: {
String raw_value = config_file->get_value(PluginConfigIOS::PLIST_SECTION, key, String());
case PluginConfigAppleEmbedded::PlistItemType::STRING: {
String raw_value = config_file->get_value(PluginConfigAppleEmbedded::PLIST_SECTION, key, String());
value = "<string>" + raw_value + "</string>";
} break;
case PluginConfigIOS::PlistItemType::INTEGER: {
int raw_value = config_file->get_value(PluginConfigIOS::PLIST_SECTION, key, 0);
case PluginConfigAppleEmbedded::PlistItemType::INTEGER: {
int raw_value = config_file->get_value(PluginConfigAppleEmbedded::PLIST_SECTION, key, 0);
Dictionary value_dictionary;
String value_format = "<integer>$value</integer>";
value_dictionary["value"] = raw_value;
value = value_format.format(value_dictionary, "$_");
} break;
case PluginConfigIOS::PlistItemType::BOOLEAN:
if (config_file->get_value(PluginConfigIOS::PLIST_SECTION, key, false)) {
case PluginConfigAppleEmbedded::PlistItemType::BOOLEAN:
if (config_file->get_value(PluginConfigAppleEmbedded::PLIST_SECTION, key, false)) {
value = "<true/>";
} else {
value = "<false/>";
}
break;
case PluginConfigIOS::PlistItemType::RAW: {
String raw_value = config_file->get_value(PluginConfigIOS::PLIST_SECTION, key, String());
case PluginConfigAppleEmbedded::PlistItemType::RAW: {
String raw_value = config_file->get_value(PluginConfigAppleEmbedded::PLIST_SECTION, key, String());
value = raw_value;
} break;
case PluginConfigIOS::PlistItemType::STRING_INPUT: {
String raw_value = config_file->get_value(PluginConfigIOS::PLIST_SECTION, key, String());
case PluginConfigAppleEmbedded::PlistItemType::STRING_INPUT: {
String raw_value = config_file->get_value(PluginConfigAppleEmbedded::PLIST_SECTION, key, String());
value = raw_value;
} break;
default:
continue;
}
plugin_config.plist[key_value] = PluginConfigIOS::PlistItem{ key_type, value };
plugin_config.plist[key_value] = PluginConfigAppleEmbedded::PlistItem{ key_type, value };
}
}

View file

@ -1,5 +1,5 @@
/**************************************************************************/
/* godot_plugin_config.h */
/* plugin_config_apple_embedded.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -50,7 +50,7 @@ The `plist` section are optional.
- **key**: key and value that would be added in Info.plist file.
*/
struct PluginConfigIOS {
struct PluginConfigAppleEmbedded {
inline static const char *PLUGIN_CONFIG_EXT = ".gdip";
inline static const char *CONFIG_SECTION = "config";
@ -121,11 +121,11 @@ struct PluginConfigIOS {
static Vector<String> resolve_system_dependencies(Vector<String> p_paths);
static bool validate_plugin(PluginConfigIOS &plugin_config);
static bool validate_plugin(PluginConfigAppleEmbedded &plugin_config);
static String get_plugin_main_binary(PluginConfigIOS &plugin_config, bool p_debug);
static String get_plugin_main_binary(PluginConfigAppleEmbedded &plugin_config, bool p_debug);
static uint64_t get_plugin_modification_time(const PluginConfigIOS &plugin_config, const String &config_path);
static uint64_t get_plugin_modification_time(const PluginConfigAppleEmbedded &plugin_config, const String &config_path);
static PluginConfigIOS load_plugin_config(Ref<ConfigFile> config_file, const String &path);
static PluginConfigAppleEmbedded load_plugin_config(Ref<ConfigFile> config_file, const String &path);
};

View file

@ -115,9 +115,9 @@ void GDExtensionExportPlugin::_export_file(const String &p_path, const String &p
libs_added.insert(library_path);
add_shared_object(library_path, tags);
if (p_features.has("ios") && (library_path.ends_with(".a") || library_path.ends_with(".xcframework"))) {
if (p_features.has("apple_embedded") && (library_path.ends_with(".a") || library_path.ends_with(".xcframework"))) {
String additional_code = "extern void register_dynamic_symbol(char *name, void *address);\n"
"extern void add_ios_init_callback(void (*cb)());\n"
"extern void add_apple_embedded_platform_init_callback(void (*cb)());\n"
"\n"
"extern \"C\" void $ENTRY();\n"
"void $ENTRY_init() {\n"
@ -125,15 +125,15 @@ void GDExtensionExportPlugin::_export_file(const String &p_path, const String &p
"}\n"
"struct $ENTRY_struct {\n"
" $ENTRY_struct() {\n"
" add_ios_init_callback($ENTRY_init);\n"
" add_apple_embedded_platform_init_callback($ENTRY_init);\n"
" }\n"
"};\n"
"$ENTRY_struct $ENTRY_struct_instance;\n\n";
additional_code = additional_code.replace("$ENTRY", entry_symbol);
add_ios_cpp_code(additional_code);
add_apple_embedded_platform_cpp_code(additional_code);
String linker_flags = "-Wl,-U,_" + entry_symbol;
add_ios_linker_flags(linker_flags);
add_apple_embedded_platform_linker_flags(linker_flags);
}
// Update found library info.

View file

@ -195,7 +195,7 @@ LightmapGIEditorPlugin::LightmapGIEditorPlugin() {
#else
// Disable lightmap baking if the module is disabled at compile-time.
bake->set_disabled(true);
#if defined(ANDROID_ENABLED) || defined(IOS_ENABLED)
#if defined(ANDROID_ENABLED) || defined(APPLE_EMBEDDED_ENABLED)
bake->set_tooltip_text(vformat(TTR("Lightmaps cannot be baked on %s."), OS::get_singleton()->get_name()));
#else
bake->set_tooltip_text(TTR("Lightmaps cannot be baked, as the `lightmapper_rd` module was disabled at compile-time."));

View file

@ -47,6 +47,7 @@
#include "editor/editor_undo_redo_manager.h"
#include "editor/editor_vcs_interface.h"
#include "editor/export/editor_export_platform.h"
#include "editor/export/editor_export_platform_apple_embedded.h"
#include "editor/export/editor_export_platform_extension.h"
#include "editor/export/editor_export_platform_pc.h"
#include "editor/export/editor_export_plugin.h"
@ -163,6 +164,7 @@ void register_editor_types() {
GDREGISTER_CLASS(EditorExportPlugin);
GDREGISTER_ABSTRACT_CLASS(EditorExportPlatform);
GDREGISTER_ABSTRACT_CLASS(EditorExportPlatformPC);
GDREGISTER_ABSTRACT_CLASS(EditorExportPlatformAppleEmbedded);
GDREGISTER_CLASS(EditorExportPlatformExtension);
GDREGISTER_ABSTRACT_CLASS(EditorExportPreset);

View file

@ -3797,7 +3797,7 @@ static MainTimerSync main_timer_sync;
int Main::start() {
OS::get_singleton()->benchmark_begin_measure("Startup", "Main::Start");
ERR_FAIL_COND_V(!_start_success, false);
ERR_FAIL_COND_V(!_start_success, EXIT_FAILURE);
bool has_icon = false;
String positional_arg;

View file

@ -609,17 +609,33 @@ def Run(env, function):
return Action(function, "$GENCOMSTR")
def detect_darwin_toolchain_path(env):
var_name = "APPLE_TOOLCHAIN_PATH"
if not env[var_name]:
try:
xcode_path = subprocess.check_output(["xcode-select", "-p"]).strip().decode("utf-8")
if xcode_path:
env[var_name] = xcode_path + "/Toolchains/XcodeDefault.xctoolchain"
except (subprocess.CalledProcessError, OSError):
print_error("Failed to find SDK path while running 'xcode-select -p'.")
raise
def detect_darwin_sdk_path(platform, env):
sdk_name = ""
if platform == "macos":
sdk_name = "macosx"
var_name = "MACOS_SDK_PATH"
elif platform == "ios":
sdk_name = "iphoneos"
var_name = "IOS_SDK_PATH"
elif platform == "iossimulator":
sdk_name = "iphonesimulator"
var_name = "IOS_SDK_PATH"
else:
raise Exception("Invalid platform argument passed to detect_darwin_sdk_path")
@ -629,7 +645,7 @@ def detect_darwin_sdk_path(platform, env):
if sdk_path:
env[var_name] = sdk_path
except (subprocess.CalledProcessError, OSError):
print_error("Failed to find SDK path while running xcrun --sdk {} --show-sdk-path.".format(sdk_name))
print_error("Failed to find SDK path while running 'xcrun --sdk {} --show-sdk-path'.".format(sdk_name))
raise

View file

@ -7,7 +7,6 @@
objects = {
/* Begin PBXBuildFile section */
054F8BE62D38852F00B81423 /* MetalFX.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 054F8BE52D38852F00B81423 /* MetalFX.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
1F1575721F582BE20003B888 /* dylibs in Resources */ = {isa = PBXBuildFile; fileRef = 1F1575711F582BE20003B888 /* dylibs */; };
DEADBEEF2F582BE20003B888 /* $binary.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = DEADBEEF1F582BE20003B888 /* $binary.xcframework */; };
$modules_buildfile
@ -43,7 +42,6 @@
1FF4C1881F584E6300A41E41 /* $binary.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "$binary.entitlements"; sourceTree = "<group>"; };
1FF8DBB01FBA9DE1009DE660 /* dummy.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = dummy.cpp; sourceTree = "<group>"; };
9039D3BD24C093AC0020482C /* MoltenVK.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = MoltenVK; path = MoltenVK.xcframework; sourceTree = "<group>"; };
054F8BE52D38852F00B81423 /* MetalFX.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MetalFX.framework; path = System/Library/Frameworks/MetalFX.framework; sourceTree = SDKROOT; };
D07CD44D1C5D589C00B7FB28 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
D0BCFE3418AEBDA2004A7AAE /* $binary.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "$binary.app"; sourceTree = BUILT_PRODUCTS_DIR; };
D0BCFE4318AEBDA2004A7AAE /* $binary-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "$binary-Info.plist"; sourceTree = "<group>"; };
@ -62,7 +60,6 @@
buildActionMask = 2147483647;
files = (
9039D3BE24C093AC0020482C /* MoltenVK.xcframework in Frameworks */,
054F8BE62D38852F00B81423 /* MetalFX.framework in Frameworks */,
DEADBEEF2F582BE20003B888 /* $binary.xcframework */,
$modules_buildphase
$additional_pbx_frameworks_build
@ -97,7 +94,6 @@
isa = PBXGroup;
children = (
9039D3BD24C093AC0020482C /* MoltenVK.xcframework */,
054F8BE52D38852F00B81423 /* MetalFX.framework */,
DEADBEEF1F582BE20003B888 /* $binary.xcframework */,
$modules_buildgrp
$additional_pbx_frameworks_refs

View file

@ -406,7 +406,7 @@ namespace GodotTools.Export
{
if (platform == OS.Platforms.iOS && path.EndsWith(".dat", StringComparison.OrdinalIgnoreCase))
{
AddIosBundleFile(path);
AddAppleEmbeddedPlatformBundleFile(path);
}
else
{
@ -453,7 +453,7 @@ namespace GodotTools.Export
throw new InvalidOperationException("Failed to generate xcframework.");
}
AddIosEmbeddedFramework(xcFrameworkPath);
AddAppleEmbeddedPlatformEmbeddedFramework(xcFrameworkPath);
}
}

View file

@ -82,7 +82,7 @@ namespace {
String get_hostfxr_file_name() {
#if defined(WINDOWS_ENABLED)
return "hostfxr.dll";
#elif defined(MACOS_ENABLED) || defined(IOS_ENABLED)
#elif defined(MACOS_ENABLED) || defined(APPLE_EMBEDDED_ENABLED)
return "libhostfxr.dylib";
#else
return "libhostfxr.so";

View file

@ -450,7 +450,7 @@ godot_plugins_initialize_fn try_load_native_aot_library(void *&r_aot_dll_handle)
#if defined(WINDOWS_ENABLED)
String native_aot_so_path = GodotSharpDirs::get_api_assemblies_dir().path_join(assembly_name + ".dll");
#elif defined(MACOS_ENABLED) || defined(IOS_ENABLED)
#elif defined(MACOS_ENABLED) || defined(APPLE_EMBEDDED_ENABLED)
String native_aot_so_path = GodotSharpDirs::get_api_assemblies_dir().path_join(assembly_name + ".dylib");
#elif defined(ANDROID_ENABLED)
String native_aot_so_path = "lib" + assembly_name + ".so";
@ -599,7 +599,7 @@ void GDMono::initialize() {
godot_plugins_initialize_fn godot_plugins_initialize = nullptr;
#if !defined(IOS_ENABLED)
#if !defined(APPLE_EMBEDDED_ENABLED)
// Check that the .NET assemblies directory exists before trying to use it.
if (!DirAccess::exists(GodotSharpDirs::get_api_assemblies_dir())) {
OS::get_singleton()->alert(vformat(RTR("Unable to find the .NET assemblies directory.\nMake sure the '%s' directory exists and contains the .NET assemblies."), GodotSharpDirs::get_api_assemblies_dir()), RTR(".NET assemblies not found"));
@ -639,7 +639,7 @@ void GDMono::initialize() {
void *godot_dll_handle = nullptr;
#if defined(UNIX_ENABLED) && !defined(MACOS_ENABLED) && !defined(IOS_ENABLED)
#if defined(UNIX_ENABLED) && !defined(MACOS_ENABLED) && !defined(APPLE_EMBEDDED_ENABLED)
// Managed code can access it on its own on other platforms
godot_dll_handle = dlopen(nullptr, RTLD_NOW);
#endif

View file

@ -713,7 +713,7 @@ Vector<PluginConfigAndroid> EditorExportPlatformAndroid::get_plugins() {
Vector<String> plugins_filenames = list_gdap_files(plugins_dir);
if (!plugins_filenames.is_empty()) {
Ref<ConfigFile> config_file = memnew(ConfigFile);
Ref<ConfigFile> config_file;
for (int i = 0; i < plugins_filenames.size(); i++) {
PluginConfigAndroid config = PluginConfigAndroid::load_plugin_config(config_file, plugins_dir.path_join(plugins_filenames[i]));
if (config.valid_config) {

View file

@ -3,6 +3,10 @@
This folder contains the C++, Objective-C and Objective-C++ code for the iOS
platform port.
This platform derives from the Apple embedded abstract platform ([`drivers/apple_embedded`](/drivers/apple_embedded)).
This platform uses shared Apple code ([`drivers/apple`](/drivers/apple)).
See also [`misc/dist/ios_xcode`](/misc/dist/ios_xcode) folder for the Xcode
project template used for packaging the iOS export templates.

View file

@ -1,28 +1,19 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
import platform_ios_builders
from platform_ios_builders import generate_bundle
from platform_methods import combine_libs_apple_embedded
Import("env")
ios_lib = [
"godot_ios.mm",
"os_ios.mm",
"main.m",
"app_delegate.mm",
"view_controller.mm",
"ios.mm",
"rendering_context_driver_vulkan_ios.mm",
"device_metrics.mm",
"display_layer_ios.mm",
"display_server_ios.mm",
"godot_view.mm",
"tts_ios.mm",
"display_layer.mm",
"godot_app_delegate.m",
"godot_view_renderer.mm",
"device_metrics.m",
"keyboard_input_view.mm",
"key_mapping_ios.mm",
"ios_terminal_logger.mm",
"godot_view_ios.mm",
"main_ios.mm",
"os_ios.mm",
]
env_ios = env.Clone()
@ -31,12 +22,9 @@ ios_lib = env_ios.add_library("ios", ios_lib)
# (iOS) Enable module support
env_ios.Append(CCFLAGS=["-fmodules", "-fcxx-modules"])
combine_command = env_ios.CommandNoCache(
"#bin/libgodot" + env_ios["LIBSUFFIX"], [ios_lib] + env_ios["LIBS"], env.Run(platform_ios_builders.combine_libs)
"#bin/libgodot" + env_ios["LIBSUFFIX"], [ios_lib] + env_ios["LIBS"], env.Run(combine_libs_apple_embedded)
)
if env["generate_bundle"]:
env.AlwaysBuild(
env.CommandNoCache("generate_bundle", combine_command, env.Run(platform_ios_builders.generate_bundle))
)
env.AlwaysBuild(env.CommandNoCache("generate_bundle", combine_command, env.Run(generate_bundle)))

View file

@ -33,11 +33,11 @@
#if defined(IOS_ENABLED)
void register_ios_api() {
godot_ios_plugins_initialize();
godot_apple_embedded_plugins_initialize();
}
void unregister_ios_api() {
godot_ios_plugins_deinitialize();
godot_apple_embedded_plugins_deinitialize();
}
#else

View file

@ -31,8 +31,8 @@
#pragma once
#if defined(IOS_ENABLED)
extern void godot_ios_plugins_initialize();
extern void godot_ios_plugins_deinitialize();
extern void godot_apple_embedded_plugins_initialize();
extern void godot_apple_embedded_plugins_deinitialize();
#endif
void register_ios_api();

View file

@ -2,7 +2,7 @@ import os
import sys
from typing import TYPE_CHECKING
from methods import detect_darwin_sdk_path, print_error, print_warning
from methods import detect_darwin_sdk_path, detect_darwin_toolchain_path, print_error, print_warning
from platform_methods import validate_arch
if TYPE_CHECKING:
@ -25,14 +25,11 @@ def get_opts():
return [
("vulkan_sdk_path", "Path to the Vulkan SDK", ""),
(
"IOS_TOOLCHAIN_PATH",
"Path to iOS toolchain",
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain",
),
# APPLE_TOOLCHAIN_PATH Example: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain
(("APPLE_TOOLCHAIN_PATH", "IOS_TOOLCHAIN_PATH"), "Path to the Apple toolchain", ""),
("IOS_SDK_PATH", "Path to the iOS SDK", ""),
BoolVariable("ios_simulator", "Build for iOS Simulator", False),
("ios_triple", "Triple for ios toolchain", ""),
(("apple_target_triple", "ios_triple"), "Triple for the corresponding target Apple platform toolchain", ""),
BoolVariable(("simulator", "ios_simulator"), "Build for Simulator", False),
BoolVariable("generate_bundle", "Generate an APP bundle after building iOS/macOS binaries", False),
]
@ -62,6 +59,7 @@ def configure(env: "SConsEnvironment"):
# Validate arch.
supported_arches = ["x86_64", "arm64"]
validate_arch(env["arch"], get_name(), supported_arches)
detect_darwin_toolchain_path(env)
## LTO
@ -82,9 +80,9 @@ def configure(env: "SConsEnvironment"):
if "OSXCROSS_IOS" in os.environ:
env["osxcross"] = True
env["ENV"]["PATH"] = env["IOS_TOOLCHAIN_PATH"] + "/Developer/usr/bin/:" + env["ENV"]["PATH"]
env["ENV"]["PATH"] = env["APPLE_TOOLCHAIN_PATH"] + "/Developer/usr/bin/:" + env["ENV"]["PATH"]
compiler_path = "$IOS_TOOLCHAIN_PATH/usr/bin/${ios_triple}"
compiler_path = "$APPLE_TOOLCHAIN_PATH/usr/bin/${apple_target_triple}"
ccache_path = os.environ.get("CCACHE")
if ccache_path is None:
@ -102,7 +100,7 @@ def configure(env: "SConsEnvironment"):
## Compile flags
if env["ios_simulator"]:
if env["simulator"]:
detect_darwin_sdk_path("iossimulator", env)
env.Append(ASFLAGS=["-mios-simulator-version-min=12.0"])
env.Append(CCFLAGS=["-mios-simulator-version-min=12.0"])
@ -114,8 +112,8 @@ def configure(env: "SConsEnvironment"):
env.Append(CCFLAGS=["-miphoneos-version-min=12.0"])
if env["arch"] == "x86_64":
if not env["ios_simulator"]:
print_error("Building for iOS with 'arch=x86_64' requires 'ios_simulator=yes'.")
if not env["simulator"]:
print_error("Building for iOS with 'arch=x86_64' requires 'simulator=yes'.")
sys.exit(255)
env["ENV"]["MACOSX_DEPLOYMENT_TARGET"] = "10.9"
@ -149,10 +147,10 @@ def configure(env: "SConsEnvironment"):
)
env.Prepend(CPPPATH=["#platform/ios"])
env.Append(CPPDEFINES=["IOS_ENABLED", "UNIX_ENABLED", "COREAUDIO_ENABLED"])
env.Append(CPPDEFINES=["IOS_ENABLED", "APPLE_EMBEDDED_ENABLED", "UNIX_ENABLED", "COREAUDIO_ENABLED"])
if env["metal"] and env["ios_simulator"]:
print_warning("iOS simulator does not support the Metal rendering driver")
if env["metal"] and env["simulator"]:
print_warning("iOS Simulator does not support the Metal rendering driver")
env["metal"] = False
if env["metal"]:
@ -166,8 +164,8 @@ def configure(env: "SConsEnvironment"):
)
env.Prepend(CPPEXTPATH=["#thirdparty/spirv-cross"])
if env["vulkan"] and env["ios_simulator"]:
print_warning("iOS simulator does not support the Vulkan rendering driver")
if env["vulkan"] and env["simulator"]:
print_warning("iOS Simulator does not support the Vulkan rendering driver")
env["vulkan"] = False
if env["vulkan"]:

View file

@ -32,7 +32,7 @@
#import <Foundation/Foundation.h>
@interface GodotDeviceMetrics : NSObject
@interface GDTDeviceMetrics : NSObject
@property(nonatomic, class, readonly, strong) NSDictionary<NSArray *, NSNumber *> *dpiList;

View file

@ -1,5 +1,5 @@
/**************************************************************************/
/* device_metrics.m */
/* device_metrics.mm */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -30,7 +30,7 @@
#import "device_metrics.h"
@implementation GodotDeviceMetrics
@implementation GDTDeviceMetrics
+ (NSDictionary *)dpiList {
return @{

View file

@ -1,5 +1,5 @@
/**************************************************************************/
/* display_layer.h */
/* display_layer_ios.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -30,32 +30,25 @@
#pragma once
#include "drivers/apple_embedded/display_layer_apple_embedded.h"
#import <OpenGLES/EAGLDrawable.h>
#import <QuartzCore/QuartzCore.h>
@protocol DisplayLayer <NSObject>
- (void)startRenderDisplayLayer;
- (void)stopRenderDisplayLayer;
- (void)initializeDisplayLayer;
- (void)layoutDisplayLayer;
@end
// An ugly workaround for iOS simulator
#if defined(TARGET_OS_SIMULATOR) && TARGET_OS_SIMULATOR
#if defined(__IPHONE_13_0)
API_AVAILABLE(ios(13.0))
@interface GodotMetalLayer : CAMetalLayer <DisplayLayer>
@interface GDTMetalLayer : CAMetalLayer <GDTDisplayLayer>
#else
@interface GodotMetalLayer : CALayer <DisplayLayer>
@interface GDTMetalLayer : CALayer <GDTDisplayLayer>
#endif
#else
@interface GodotMetalLayer : CAMetalLayer <DisplayLayer>
@interface GDTMetalLayer : CAMetalLayer <GDTDisplayLayer>
#endif
@end
API_DEPRECATED("OpenGLES is deprecated", ios(2.0, 12.0))
@interface GodotOpenGLLayer : CAEAGLLayer <DisplayLayer>
@interface GDTOpenGLLayer : CAEAGLLayer <GDTDisplayLayer>
@end

View file

@ -1,5 +1,5 @@
/**************************************************************************/
/* display_layer.mm */
/* display_layer_ios.mm */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#import "display_layer.h"
#import "display_layer_ios.h"
#import "display_server_ios.h"
#import "os_ios.h"
@ -46,7 +46,7 @@
#import <QuartzCore/QuartzCore.h>
#import <UIKit/UIKit.h>
@implementation GodotMetalLayer
@implementation GDTMetalLayer
- (void)initializeDisplayLayer {
#if defined(TARGET_OS_SIMULATOR) && TARGET_OS_SIMULATOR
@ -69,7 +69,7 @@
@end
@implementation GodotOpenGLLayer {
@implementation GDTOpenGLLayer {
// The pixel dimensions of the backbuffer
GLint backingWidth;
GLint backingHeight;

View file

@ -30,209 +30,25 @@
#pragma once
#include "core/input/input.h"
#include "servers/display_server.h"
#include "drivers/apple_embedded/display_server_apple_embedded.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_ios.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 DisplayServerIOS : public DisplayServer {
GDSOFTCLASS(DisplayServerIOS, DisplayServer);
class DisplayServerIOS : public DisplayServerAppleEmbedded {
GDSOFTCLASS(DisplayServerIOS, DisplayServerAppleEmbedded);
_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;
DisplayServerIOS(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);
~DisplayServerIOS();
public:
String rendering_driver;
static DisplayServerIOS *get_singleton();
static void register_ios_driver();
static DisplayServer *create_func(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);
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 String get_name() 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 int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual float screen_get_scale(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual float screen_get_refresh_rate(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 {}
};

View file

@ -30,492 +30,33 @@
#import "display_server_ios.h"
#import "app_delegate.h"
#import "device_metrics.h"
#import "godot_view.h"
#import "ios.h"
#import "key_mapping_ios.h"
#import "keyboard_input_view.h"
#import "os_ios.h"
#import "tts_ios.h"
#import "view_controller.h"
#include "core/config/project_settings.h"
#include "core/io/file_access_pack.h"
#import <UIKit/UIKit.h>
#import <sys/utsname.h>
#import <GameController/GameController.h>
static const float kDisplayServerIOSAcceleration = 1.f;
DisplayServerIOS *DisplayServerIOS::get_singleton() {
return (DisplayServerIOS *)DisplayServer::get_singleton();
return (DisplayServerIOS *)DisplayServerAppleEmbedded::get_singleton();
}
DisplayServerIOS::DisplayServerIOS(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) {
KeyMappingIOS::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);
#if defined(RD_ENABLED)
rendering_context = nullptr;
rendering_device = nullptr;
CALayer *layer = nullptr;
union {
#ifdef VULKAN_ENABLED
RenderingContextDriverVulkanIOS::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 = [AppDelegate.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(RenderingContextDriverVulkanIOS);
}
#endif
#ifdef METAL_ENABLED
if (rendering_driver == "metal") {
if (@available(iOS 14.0, *)) {
layer = [AppDelegate.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 seem not 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();
}
#endif
#if defined(GLES3_ENABLED)
if (rendering_driver == "opengl3") {
CALayer *layer = [AppDelegate.viewController.godotView initializeRenderingForDriver:@"opengl3"];
if (!layer) {
ERR_FAIL_MSG("Failed to create iOS OpenGLES rendering layer.");
}
RasterizerGLES3::make_current(false);
}
#endif
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;
DisplayServerIOS::DisplayServerIOS(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) :
DisplayServerAppleEmbedded(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, p_parent_window, r_error) {
}
DisplayServerIOS::~DisplayServerIOS() {
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
}
DisplayServer *DisplayServerIOS::create_func(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) {
return memnew(DisplayServerIOS(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, p_parent_window, r_error));
}
Vector<String> DisplayServerIOS::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;
}
void DisplayServerIOS::register_ios_driver() {
register_create_function("iOS", create_func, get_rendering_drivers_func);
}
// MARK: Events
void DisplayServerIOS::window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window) {
window_resize_callback = p_callable;
}
void DisplayServerIOS::window_set_window_event_callback(const Callable &p_callable, WindowID p_window) {
window_event_callback = p_callable;
}
void DisplayServerIOS::window_set_input_event_callback(const Callable &p_callable, WindowID p_window) {
input_event_callback = p_callable;
}
void DisplayServerIOS::window_set_input_text_callback(const Callable &p_callable, WindowID p_window) {
input_text_callback = p_callable;
}
void DisplayServerIOS::window_set_drop_files_callback(const Callable &p_callable, WindowID p_window) {
// Probably not supported for iOS
}
void DisplayServerIOS::process_events() {
Input::get_singleton()->flush_buffered_events();
}
void DisplayServerIOS::_dispatch_input_events(const Ref<InputEvent> &p_event) {
DisplayServerIOS::get_singleton()->send_input_event(p_event);
}
void DisplayServerIOS::send_input_event(const Ref<InputEvent> &p_event) const {
_window_callback(input_event_callback, p_event);
}
void DisplayServerIOS::send_input_text(const String &p_text) const {
_window_callback(input_text_callback, p_text);
}
void DisplayServerIOS::send_window_event(DisplayServer::WindowEvent p_event) const {
_window_callback(window_event_callback, int(p_event));
}
void DisplayServerIOS::_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 DisplayServerIOS::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 DisplayServerIOS::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 DisplayServerIOS::perform_event(const Ref<InputEvent> &p_event) {
Input::get_singleton()->parse_input_event(p_event);
}
void DisplayServerIOS::touches_canceled(int p_idx) {
touch_press(p_idx, -1, -1, false, false);
}
// MARK: Keyboard
void DisplayServerIOS::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 DisplayServerIOS::update_gravity(const Vector3 &p_gravity) {
Input::get_singleton()->set_gravity(p_gravity);
}
void DisplayServerIOS::update_accelerometer(const Vector3 &p_accelerometer) {
Input::get_singleton()->set_accelerometer(p_accelerometer / kDisplayServerIOSAcceleration);
}
void DisplayServerIOS::update_magnetometer(const Vector3 &p_magnetometer) {
Input::get_singleton()->set_magnetometer(p_magnetometer);
}
void DisplayServerIOS::update_gyroscope(const Vector3 &p_gyroscope) {
Input::get_singleton()->set_gyroscope(p_gyroscope);
}
// MARK: -
bool DisplayServerIOS::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;
}
}
String DisplayServerIOS::get_name() const {
return "iOS";
}
void DisplayServerIOS::initialize_tts() const {
const_cast<DisplayServerIOS *>(this)->tts = [[TTS_IOS alloc] init];
}
bool DisplayServerIOS::tts_is_speaking() const {
if (unlikely(!tts)) {
initialize_tts();
}
ERR_FAIL_NULL_V(tts, false);
return [tts isSpeaking];
}
bool DisplayServerIOS::tts_is_paused() const {
if (unlikely(!tts)) {
initialize_tts();
}
ERR_FAIL_NULL_V(tts, false);
return [tts isPaused];
}
TypedArray<Dictionary> DisplayServerIOS::tts_get_voices() const {
if (unlikely(!tts)) {
initialize_tts();
}
ERR_FAIL_NULL_V(tts, TypedArray<Dictionary>());
return [tts getVoices];
}
void DisplayServerIOS::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 DisplayServerIOS::tts_pause() {
if (unlikely(!tts)) {
initialize_tts();
}
ERR_FAIL_NULL(tts);
[tts pauseSpeaking];
}
void DisplayServerIOS::tts_resume() {
if (unlikely(!tts)) {
initialize_tts();
}
ERR_FAIL_NULL(tts);
[tts resumeSpeaking];
}
void DisplayServerIOS::tts_stop() {
if (unlikely(!tts)) {
initialize_tts();
}
ERR_FAIL_NULL(tts);
[tts stopSpeaking];
}
bool DisplayServerIOS::is_dark_mode_supported() const {
if (@available(iOS 13.0, *)) {
return true;
} else {
return false;
}
}
bool DisplayServerIOS::is_dark_mode() const {
if (@available(iOS 13.0, *)) {
return [UITraitCollection currentTraitCollection].userInterfaceStyle == UIUserInterfaceStyleDark;
} else {
return false;
}
}
void DisplayServerIOS::set_system_theme_change_callback(const Callable &p_callable) {
system_theme_changed = p_callable;
}
void DisplayServerIOS::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 DisplayServerIOS::get_display_safe_area() const {
UIEdgeInsets insets = UIEdgeInsetsZero;
UIView *view = AppDelegate.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 DisplayServerIOS::get_screen_count() const {
return 1;
}
int DisplayServerIOS::get_primary_screen() const {
return 0;
}
Point2i DisplayServerIOS::screen_get_position(int p_screen) const {
return Size2i();
}
Size2i DisplayServerIOS::screen_get_size(int p_screen) const {
CALayer *layer = AppDelegate.viewController.godotView.renderingLayer;
if (!layer) {
return Size2i();
}
return Size2i(layer.bounds.size.width, layer.bounds.size.height) * screen_get_scale(p_screen);
}
Rect2i DisplayServerIOS::screen_get_usable_rect(int p_screen) const {
return Rect2i(screen_get_position(p_screen), screen_get_size(p_screen));
}
int DisplayServerIOS::screen_get_dpi(int p_screen) const {
struct utsname systemInfo;
@ -523,7 +64,7 @@ int DisplayServerIOS::screen_get_dpi(int p_screen) const {
NSString *string = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];
NSDictionary *iOSModelToDPI = [GodotDeviceMetrics dpiList];
NSDictionary *iOSModelToDPI = [GDTDeviceMetrics dpiList];
for (NSArray *keyArray in iOSModelToDPI) {
if ([keyArray containsObject:string]) {
@ -565,283 +106,3 @@ float DisplayServerIOS::screen_get_refresh_rate(int p_screen) const {
float DisplayServerIOS::screen_get_scale(int p_screen) const {
return [UIScreen mainScreen].scale;
}
Vector<DisplayServer::WindowID> DisplayServerIOS::get_window_list() const {
Vector<DisplayServer::WindowID> list;
list.push_back(MAIN_WINDOW_ID);
return list;
}
DisplayServer::WindowID DisplayServerIOS::get_window_at_screen_position(const Point2i &p_position) const {
return MAIN_WINDOW_ID;
}
int64_t DisplayServerIOS::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)AppDelegate.viewController;
}
case WINDOW_VIEW: {
return (int64_t)AppDelegate.viewController.godotView;
}
default: {
return 0;
}
}
}
void DisplayServerIOS::window_attach_instance_id(ObjectID p_instance, WindowID p_window) {
window_attached_instance_id = p_instance;
}
ObjectID DisplayServerIOS::window_get_attached_instance_id(WindowID p_window) const {
return window_attached_instance_id;
}
void DisplayServerIOS::window_set_title(const String &p_title, WindowID p_window) {
// Probably not supported for iOS
}
int DisplayServerIOS::window_get_current_screen(WindowID p_window) const {
return SCREEN_OF_MAIN_WINDOW;
}
void DisplayServerIOS::window_set_current_screen(int p_screen, WindowID p_window) {
// Probably not supported for iOS
}
Point2i DisplayServerIOS::window_get_position(WindowID p_window) const {
return Point2i();
}
Point2i DisplayServerIOS::window_get_position_with_decorations(WindowID p_window) const {
return Point2i();
}
void DisplayServerIOS::window_set_position(const Point2i &p_position, WindowID p_window) {
// Probably not supported for single window iOS app
}
void DisplayServerIOS::window_set_transient(WindowID p_window, WindowID p_parent) {
// Probably not supported for iOS
}
void DisplayServerIOS::window_set_max_size(const Size2i p_size, WindowID p_window) {
// Probably not supported for iOS
}
Size2i DisplayServerIOS::window_get_max_size(WindowID p_window) const {
return Size2i();
}
void DisplayServerIOS::window_set_min_size(const Size2i p_size, WindowID p_window) {
// Probably not supported for iOS
}
Size2i DisplayServerIOS::window_get_min_size(WindowID p_window) const {
return Size2i();
}
void DisplayServerIOS::window_set_size(const Size2i p_size, WindowID p_window) {
// Probably not supported for iOS
}
Size2i DisplayServerIOS::window_get_size(WindowID p_window) const {
CGRect screenBounds = [UIScreen mainScreen].bounds;
return Size2i(screenBounds.size.width, screenBounds.size.height) * screen_get_max_scale();
}
Size2i DisplayServerIOS::window_get_size_with_decorations(WindowID p_window) const {
return window_get_size(p_window);
}
void DisplayServerIOS::window_set_mode(WindowMode p_mode, WindowID p_window) {
// Probably not supported for iOS
}
DisplayServer::WindowMode DisplayServerIOS::window_get_mode(WindowID p_window) const {
return WindowMode::WINDOW_MODE_FULLSCREEN;
}
bool DisplayServerIOS::window_is_maximize_allowed(WindowID p_window) const {
return false;
}
void DisplayServerIOS::window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window) {
// Probably not supported for iOS
}
bool DisplayServerIOS::window_get_flag(WindowFlags p_flag, WindowID p_window) const {
return false;
}
void DisplayServerIOS::window_request_attention(WindowID p_window) {
// Probably not supported for iOS
}
void DisplayServerIOS::window_move_to_foreground(WindowID p_window) {
// Probably not supported for iOS
}
bool DisplayServerIOS::window_is_focused(WindowID p_window) const {
return true;
}
float DisplayServerIOS::screen_get_max_scale() const {
return screen_get_scale(SCREEN_OF_MAIN_WINDOW);
}
void DisplayServerIOS::screen_set_orientation(DisplayServer::ScreenOrientation p_orientation, int p_screen) {
screen_orientation = p_orientation;
if (@available(iOS 16.0, *)) {
[AppDelegate.viewController setNeedsUpdateOfSupportedInterfaceOrientations];
} else {
[UIViewController attemptRotationToDeviceOrientation];
}
}
DisplayServer::ScreenOrientation DisplayServerIOS::screen_get_orientation(int p_screen) const {
return screen_orientation;
}
bool DisplayServerIOS::window_can_draw(WindowID p_window) const {
return true;
}
bool DisplayServerIOS::can_any_window_draw() const {
return true;
}
bool DisplayServerIOS::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 DisplayServerIOS::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()];
AppDelegate.viewController.keyboardView.keyboardType = UIKeyboardTypeDefault;
AppDelegate.viewController.keyboardView.textContentType = nil;
switch (p_type) {
case KEYBOARD_TYPE_DEFAULT: {
AppDelegate.viewController.keyboardView.keyboardType = UIKeyboardTypeDefault;
} break;
case KEYBOARD_TYPE_MULTILINE: {
AppDelegate.viewController.keyboardView.keyboardType = UIKeyboardTypeDefault;
} break;
case KEYBOARD_TYPE_NUMBER: {
AppDelegate.viewController.keyboardView.keyboardType = UIKeyboardTypeNumberPad;
} break;
case KEYBOARD_TYPE_NUMBER_DECIMAL: {
AppDelegate.viewController.keyboardView.keyboardType = UIKeyboardTypeDecimalPad;
} break;
case KEYBOARD_TYPE_PHONE: {
AppDelegate.viewController.keyboardView.keyboardType = UIKeyboardTypePhonePad;
AppDelegate.viewController.keyboardView.textContentType = UITextContentTypeTelephoneNumber;
} break;
case KEYBOARD_TYPE_EMAIL_ADDRESS: {
AppDelegate.viewController.keyboardView.keyboardType = UIKeyboardTypeEmailAddress;
AppDelegate.viewController.keyboardView.textContentType = UITextContentTypeEmailAddress;
} break;
case KEYBOARD_TYPE_PASSWORD: {
AppDelegate.viewController.keyboardView.keyboardType = UIKeyboardTypeDefault;
AppDelegate.viewController.keyboardView.textContentType = UITextContentTypePassword;
} break;
case KEYBOARD_TYPE_URL: {
AppDelegate.viewController.keyboardView.keyboardType = UIKeyboardTypeWebSearch;
AppDelegate.viewController.keyboardView.textContentType = UITextContentTypeURL;
} break;
}
[AppDelegate.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 DisplayServerIOS::is_keyboard_active() const {
return [AppDelegate.viewController.keyboardView isFirstResponder];
}
void DisplayServerIOS::virtual_keyboard_hide() {
[AppDelegate.viewController.keyboardView resignFirstResponder];
}
void DisplayServerIOS::virtual_keyboard_set_height(int height) {
virtual_keyboard_height = height * screen_get_max_scale();
}
int DisplayServerIOS::virtual_keyboard_get_height() const {
return virtual_keyboard_height;
}
bool DisplayServerIOS::has_hardware_keyboard() const {
if (@available(iOS 14.0, *)) {
return [GCKeyboard coalescedKeyboard];
} else {
return false;
}
}
void DisplayServerIOS::clipboard_set(const String &p_text) {
[UIPasteboard generalPasteboard].string = [NSString stringWithUTF8String:p_text.utf8().get_data()];
}
String DisplayServerIOS::clipboard_get() const {
NSString *text = [UIPasteboard generalPasteboard].string;
return String::utf8([text UTF8String]);
}
void DisplayServerIOS::screen_set_keep_on(bool p_enable) {
[UIApplication sharedApplication].idleTimerDisabled = p_enable;
}
bool DisplayServerIOS::screen_is_kept_on() const {
return [UIApplication sharedApplication].idleTimerDisabled;
}
void DisplayServerIOS::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 DisplayServerIOS::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 DisplayServerIOS::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;
}

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="EditorExportPlatformIOS" inherits="EditorExportPlatform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<class name="EditorExportPlatformIOS" inherits="EditorExportPlatformAppleEmbedded" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
Exporter for iOS.
</brief_description>
@ -24,10 +24,10 @@
Unique application identifier in a reverse-DNS format, can only contain alphanumeric characters ([code]A-Z[/code], [code]a-z[/code], and [code]0-9[/code]), hyphens ([code]-[/code]), and periods ([code].[/code]).
</member>
<member name="application/code_sign_identity_debug" type="String" setter="" getter="">
The "Full Name", "Common Name" or SHA-1 hash of the signing identity used for debug export.
The "Full Name", "Common Name", or SHA-1 hash of the signing identity used for debug export.
</member>
<member name="application/code_sign_identity_release" type="String" setter="" getter="">
The "Full Name", "Common Name" or SHA-1 hash of the signing identity used for release export.
The "Full Name", "Common Name", or SHA-1 hash of the signing identity used for release export.
</member>
<member name="application/delete_old_export_files_unconditionally" type="bool" setter="" getter="">
If [code]true[/code], existing "project name" and "project name.xcodeproj" in the export destination directory will be unconditionally deleted during export.
@ -48,20 +48,20 @@
Minimum version of iOS required for this application to run in the [code]major.minor.patch[/code] or [code]major.minor[/code] format, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]).
</member>
<member name="application/provisioning_profile_specifier_debug" type="String" setter="" getter="">
Name of the provisioning profile. Sets XCode PROVISIONING_PROFILE_SPECIFIER for debug. [url=https://developer.apple.com/documentation/xcode/build-settings-reference#Provisioning-Profile]Used for manual provisioning[/url].
Can be overridden with the environment variable [code]GODOT_IOS_PROFILE_SPECIFIER_DEBUG[/code].
Name of the provisioning profile. Sets Xcode PROVISIONING_PROFILE_SPECIFIER for debug. [url=https://developer.apple.com/documentation/xcode/build-settings-reference#Provisioning-Profile]Used for manual provisioning[/url].
Can be overridden with the environment variable [code]GODOT_APPLE_PLATFORM_PROFILE_SPECIFIER_DEBUG[/code].
</member>
<member name="application/provisioning_profile_specifier_release" type="String" setter="" getter="">
Name of the provisioning profile. Sets XCode PROVISIONING_PROFILE_SPECIFIER for release. [url=https://developer.apple.com/documentation/xcode/build-settings-reference#Provisioning-Profile]Used for manual provisioning[/url].
Can be overridden with the environment variable [code]GODOT_IOS_PROFILE_SPECIFIER_RELEASE[/code].
Name of the provisioning profile. Sets Xcode PROVISIONING_PROFILE_SPECIFIER for release. [url=https://developer.apple.com/documentation/xcode/build-settings-reference#Provisioning-Profile]Used for manual provisioning[/url].
Can be overridden with the environment variable [code]GODOT_APPLE_PLATFORM_PROFILE_SPECIFIER_RELEASE[/code].
</member>
<member name="application/provisioning_profile_uuid_debug" type="String" setter="" getter="">
UUID of the provisioning profile. If left empty, Xcode will download or create a provisioning profile automatically. See [url=https://developer.apple.com/help/account/manage-profiles/edit-download-or-delete-profiles]Edit, download, or delete provisioning profiles[/url].
Can be overridden with the environment variable [code]GODOT_IOS_PROVISIONING_PROFILE_UUID_DEBUG[/code].
Can be overridden with the environment variable [code]GODOT_APPLE_PLATFORM_PROVISIONING_PROFILE_UUID_DEBUG[/code].
</member>
<member name="application/provisioning_profile_uuid_release" type="String" setter="" getter="">
UUID of the provisioning profile. If left empty, Xcode will download or create a provisioning profile automatically. See [url=https://developer.apple.com/help/account/manage-profiles/edit-download-or-delete-profiles]Edit, download, or delete provisioning profiles[/url].
Can be overridden with the environment variable [code]GODOT_IOS_PROVISIONING_PROFILE_UUID_RELEASE[/code].
Can be overridden with the environment variable [code]GODOT_APPLE_PLATFORM_PROVISIONING_PROFILE_UUID_RELEASE[/code].
</member>
<member name="application/short_version" type="String" setter="" getter="">
Application version visible to the user, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]). Falls back to [member ProjectSettings.application/config/version] if left empty.
@ -86,7 +86,7 @@
</member>
<member name="capabilities/performance_a12" type="bool" setter="" getter="">
Requires the graphics performance and features of the A12 Bionic and later chips (devices supporting all Vulkan renderer features).
Enabling this option limits supported devices to: iPhone XS, iPhone XR, iPad Mini (5th gen.), iPad Air (3rd gen.), iPad (8th gen) and newer.
Enabling this option limits supported devices to: iPhone XS, iPhone XR, iPad Mini (5th gen.), iPad Air (3rd gen.), iPad (8th gen), and newer.
</member>
<member name="capabilities/performance_gaming_tier" type="bool" setter="" getter="">
Requires the graphics performance and features of the A17 Pro and later chips.

File diff suppressed because it is too large Load diff

View file

@ -30,268 +30,31 @@
#pragma once
#include "godot_plugin_config.h"
#include "editor/export/editor_export_platform_apple_embedded.h"
#include "core/config/project_settings.h"
#include "core/io/file_access.h"
#include "core/io/image_loader.h"
#include "core/io/marshalls.h"
#include "core/io/resource_saver.h"
#include "core/io/zip_io.h"
#include "core/os/os.h"
#include "core/templates/safe_refcount.h"
#include "editor/editor_settings.h"
#include "editor/export/editor_export_platform.h"
#include "main/splash.gen.h"
#include "scene/resources/image_texture.h"
class EditorExportPlatformIOS : public EditorExportPlatformAppleEmbedded {
GDCLASS(EditorExportPlatformIOS, EditorExportPlatformAppleEmbedded);
#include <sys/stat.h>
virtual String get_platform_name() const override { return "ios"; }
// Optional environment variables for defining confidential information. If any
// of these is set, they will override the values set in the credentials file.
const String ENV_IOS_PROFILE_UUID_DEBUG = "GODOT_IOS_PROVISIONING_PROFILE_UUID_DEBUG";
const String ENV_IOS_PROFILE_UUID_RELEASE = "GODOT_IOS_PROVISIONING_PROFILE_UUID_RELEASE";
const String ENV_IOS_PROFILE_SPECIFIER_DEBUG = "GODOT_IOS_PROFILE_SPECIFIER_DEBUG";
const String ENV_IOS_PROFILE_SPECIFIER_RELEASE = "GODOT_IOS_PROFILE_SPECIFIER_RELEASE";
virtual Vector<IconInfo> get_icon_infos() const override;
class EditorExportPlatformIOS : public EditorExportPlatform {
GDCLASS(EditorExportPlatformIOS, EditorExportPlatform);
Ref<ImageTexture> logo;
Ref<ImageTexture> run_icon;
// Plugins
mutable SafeFlag plugins_changed;
SafeFlag devices_changed;
struct Device {
String id;
String name;
bool wifi = false;
bool use_ios_deploy = false;
};
Vector<Device> devices;
Mutex device_lock;
Mutex plugins_lock;
mutable Vector<PluginConfigIOS> plugins;
#ifdef MACOS_ENABLED
Thread check_for_changes_thread;
SafeFlag quit_request;
SafeFlag has_runnable_preset;
static bool _check_xcode_install();
static void _check_for_changes_poll_thread(void *ud);
void _update_preset_status();
#endif
typedef Error (*FileHandler)(String p_file, void *p_userdata);
static Error _walk_dir_recursive(Ref<DirAccess> &p_da, FileHandler p_handler, void *p_userdata);
static Error _codesign(String p_file, void *p_userdata);
void _blend_and_rotate(Ref<Image> &p_dst, Ref<Image> &p_src, bool p_rot);
struct IOSConfigData {
String pkg_name;
String binary_name;
String plist_content;
String architectures;
String linker_flags;
String cpp_code;
String modules_buildfile;
String modules_fileref;
String modules_buildphase;
String modules_buildgrp;
Vector<String> capabilities;
bool use_swift_runtime;
};
struct ExportArchitecture {
String name;
bool is_default = false;
ExportArchitecture() {}
ExportArchitecture(String p_name, bool p_is_default) {
name = p_name;
is_default = p_is_default;
}
};
struct IOSExportAsset {
String exported_path;
bool is_framework = false; // framework is anything linked to the binary, otherwise it's a resource
bool should_embed = false;
};
String _get_additional_plist_content();
String _get_linker_flags();
String _get_cpp_code();
void _fix_config_file(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &pfile, const IOSConfigData &p_config, bool p_debug);
Error _export_loading_screen_file(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir);
Error _export_icons(const Ref<EditorExportPreset> &p_preset, const String &p_iconset_dir);
Vector<ExportArchitecture> _get_supported_architectures() const;
Vector<String> _get_preset_architectures(const Ref<EditorExportPreset> &p_preset) const;
void _check_xcframework_content(const String &p_path, int &r_total_libs, int &r_static_libs, int &r_dylibs, int &r_frameworks) const;
Error _convert_to_framework(const String &p_source, const String &p_destination, const String &p_id) const;
void _add_assets_to_project(const String &p_out_dir, const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_project_data, const Vector<IOSExportAsset> &p_additional_assets);
Error _export_additional_assets(const Ref<EditorExportPreset> &p_preset, const String &p_out_dir, const Vector<String> &p_assets, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets);
Error _copy_asset(const Ref<EditorExportPreset> &p_preset, const String &p_out_dir, const String &p_asset, const String *p_custom_file_name, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets);
Error _export_additional_assets(const Ref<EditorExportPreset> &p_preset, const String &p_out_dir, const Vector<SharedObject> &p_libraries, Vector<IOSExportAsset> &r_exported_assets);
Error _export_ios_plugins(const Ref<EditorExportPreset> &p_preset, IOSConfigData &p_config_data, const String &dest_dir, Vector<IOSExportAsset> &r_exported_assets, bool p_debug);
Error _export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags, bool p_oneclick);
bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const;
protected:
virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const override;
virtual void get_export_options(List<ExportOption> *r_options) const override;
virtual bool get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option) const override;
virtual String get_export_option_warning(const EditorExportPreset *p_preset, const StringName &p_name) const override;
virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const override;
void _notification(int p_what);
virtual Error _export_loading_screen_file(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir) override;
virtual Error _export_icons(const Ref<EditorExportPreset> &p_preset, const String &p_iconset_dir) override;
virtual HashMap<String, Variant> get_custom_project_settings(const Ref<EditorExportPreset> &p_preset) const override;
public:
virtual String get_name() const override { return "iOS"; }
virtual String get_os_name() const override { return "iOS"; }
virtual Ref<Texture2D> get_logo() const override { return logo; }
virtual Ref<Texture2D> get_run_icon() const override { return run_icon; }
virtual int get_options_count() const override;
virtual String get_options_tooltip() const override;
virtual Ref<ImageTexture> get_option_icon(int p_index) const override;
virtual String get_option_label(int p_index) const override;
virtual String get_option_tooltip(int p_index) const override;
virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) override;
virtual bool poll_export() override {
bool dc = devices_changed.is_set();
if (dc) {
// don't clear unless we're reporting true, to avoid race
devices_changed.clear();
}
return dc;
}
virtual bool should_update_export_options() override {
bool export_options_changed = plugins_changed.is_set();
if (export_options_changed) {
// don't clear unless we're reporting true, to avoid race
plugins_changed.clear();
}
return export_options_changed;
}
virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override {
List<String> list;
if (p_preset.is_valid()) {
bool project_only = p_preset->get("application/export_project_only");
if (project_only) {
list.push_back("xcodeproj");
} else {
list.push_back("ipa");
}
}
return list;
}
virtual HashMap<String, Variant> get_custom_project_settings(const Ref<EditorExportPreset> &p_preset) const override;
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override;
virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const override;
virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const override;
virtual void get_platform_features(List<String> *r_features) const override {
r_features->push_back("mobile");
EditorExportPlatformAppleEmbedded::get_platform_features(r_features);
r_features->push_back("ios");
}
virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, HashSet<String> &p_features) override {
}
EditorExportPlatformIOS();
~EditorExportPlatformIOS();
/// List the gdip files in the directory specified by the p_path parameter.
static Vector<String> list_plugin_config_files(const String &p_path, bool p_check_directories) {
Vector<String> dir_files;
Ref<DirAccess> da = DirAccess::open(p_path);
if (da.is_valid()) {
da->list_dir_begin();
while (true) {
String file = da->get_next();
if (file.is_empty()) {
break;
}
if (file == "." || file == "..") {
continue;
}
if (da->current_is_hidden()) {
continue;
}
if (da->current_is_dir()) {
if (p_check_directories) {
Vector<String> directory_files = list_plugin_config_files(p_path.path_join(file), false);
for (int i = 0; i < directory_files.size(); ++i) {
dir_files.push_back(file.path_join(directory_files[i]));
}
}
continue;
}
if (file.ends_with(PluginConfigIOS::PLUGIN_CONFIG_EXT)) {
dir_files.push_back(file);
}
}
da->list_dir_end();
}
return dir_files;
}
static Vector<PluginConfigIOS> get_plugins() {
Vector<PluginConfigIOS> loaded_plugins;
String plugins_dir = ProjectSettings::get_singleton()->get_resource_path().path_join("ios/plugins");
if (DirAccess::exists(plugins_dir)) {
Vector<String> plugins_filenames = list_plugin_config_files(plugins_dir, true);
if (!plugins_filenames.is_empty()) {
Ref<ConfigFile> config_file = memnew(ConfigFile);
for (int i = 0; i < plugins_filenames.size(); i++) {
PluginConfigIOS config = PluginConfigIOS::load_plugin_config(config_file, plugins_dir.path_join(plugins_filenames[i]));
if (config.valid_config) {
loaded_plugins.push_back(config);
} else {
print_error("Invalid plugin config file " + plugins_filenames[i]);
}
}
}
}
return loaded_plugins;
}
static Vector<PluginConfigIOS> get_enabled_plugins(const Ref<EditorExportPreset> &p_presets) {
Vector<PluginConfigIOS> enabled_plugins;
Vector<PluginConfigIOS> all_plugins = get_plugins();
for (int i = 0; i < all_plugins.size(); i++) {
PluginConfigIOS plugin = all_plugins[i];
bool enabled = p_presets->get("plugins/" + plugin.name);
if (enabled) {
enabled_plugins.push_back(plugin);
}
}
return enabled_plugins;
}
};

View file

@ -0,0 +1,37 @@
/**************************************************************************/
/* godot_view_ios.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 "drivers/apple_embedded/godot_view_apple_embedded.h"
@interface GDTViewIOS : GDTView
@end

View file

@ -0,0 +1,87 @@
/**************************************************************************/
/* godot_view_ios.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_ios.h"
#import "display_layer_ios.h"
#include "core/error/error_macros.h"
@interface GDTViewIOS ()
GODOT_CLANG_WARNING_PUSH_AND_IGNORE("-Wobjc-property-synthesis")
@property(strong, nonatomic) CALayer<GDTDisplayLayer> *renderingLayer;
GODOT_CLANG_WARNING_POP
@end
@implementation GDTViewIOS
- (CALayer<GDTDisplayLayer> *)initializeRenderingForDriver:(NSString *)driverName {
if (self.renderingLayer) {
return self.renderingLayer;
}
CALayer<GDTDisplayLayer> *layer;
if ([driverName isEqualToString:@"vulkan"] || [driverName isEqualToString:@"metal"]) {
#if defined(TARGET_OS_SIMULATOR) && TARGET_OS_SIMULATOR
if (@available(iOS 13, *)) {
layer = [GDTMetalLayer layer];
} else {
return nil;
}
#else
layer = [GDTMetalLayer layer];
#endif
} else if ([driverName isEqualToString:@"opengl3"]) {
GODOT_CLANG_WARNING_PUSH_AND_IGNORE("-Wdeprecated-declarations") // OpenGL is deprecated in iOS 12.0.
layer = [GDTOpenGLLayer layer];
GODOT_CLANG_WARNING_POP
} else {
return nil;
}
layer.frame = self.bounds;
layer.contentsScale = self.contentScaleFactor;
[self.layer addSublayer:layer];
self.renderingLayer = layer;
[layer initializeDisplayLayer];
return self.renderingLayer;
}
@end
GDTView *GDTViewCreate() {
return [GDTViewIOS new];
}

View file

@ -30,30 +30,8 @@
#pragma once
#include "core/object/class_db.h"
#include "drivers/apple_embedded/apple_embedded.h"
#import <CoreHaptics/CoreHaptics.h>
class iOS : public Object {
GDCLASS(iOS, 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;
iOS();
class iOS : public AppleEmbedded {
GDCLASS(iOS, AppleEmbedded);
};

89
platform/ios/main_ios.mm Normal file
View file

@ -0,0 +1,89 @@
/**************************************************************************/
/* main_ios.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_ios.h"
#import "drivers/apple_embedded/godot_app_delegate.h"
#import "drivers/apple_embedded/main_utilities.h"
#include "main/main.h"
#import <UIKit/UIKit.h>
#include <cstdio>
int gargc;
char **gargv;
static OS_IOS *os = nullptr;
int main(int argc, char *argv[]) {
#if defined(VULKAN_ENABLED)
//MoltenVK - enable full component swizzling support
setenv("MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE", "1", 1);
#endif
gargc = argc;
gargv = argv;
@autoreleasepool {
NSString *className = NSStringFromClass([GDTApplicationDelegate class]);
UIApplicationMain(argc, argv, nil, className);
}
return 0;
}
int apple_embedded_main(int argc, char **argv) {
change_to_launch_dir(argv);
os = new OS_IOS();
// We must override main when testing is enabled
TEST_MAIN_OVERRIDE
char *fargv[64];
argc = process_args(argc, argv, fargv);
Error err = Main::setup(fargv[0], argc - 1, &fargv[1], false);
if (err != OK) {
if (err == ERR_HELP) { // Returned by --help and --version, so success.
return EXIT_SUCCESS;
}
return EXIT_FAILURE;
}
os->initialize_modules();
return os->get_exit_code();
}
void apple_embedded_finish() {
Main::cleanup();
delete os;
}

View file

@ -32,107 +32,16 @@
#ifdef IOS_ENABLED
#import "ios.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_ios.h"
#endif
#endif
class OS_IOS : public OS_Unix {
private:
static HashMap<String, void *> dynamic_symbol_lookup_table;
friend void register_dynamic_symbol(char *name, void *address);
AudioDriverCoreAudio audio_driver;
iOS *ios = 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();
#import "drivers/apple_embedded/os_apple_embedded.h"
class OS_IOS : public OS_AppleEmbedded {
public:
static OS_IOS *get_singleton();
OS_IOS();
~OS_IOS();
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_name() const 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_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;
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;
};
#endif // IOS_ENABLED

View file

@ -30,694 +30,23 @@
#import "os_ios.h"
#import "display_server_ios.h"
#ifdef IOS_ENABLED
#import "app_delegate.h"
#import "display_server_ios.h"
#import "godot_view.h"
#import "ios_terminal_logger.h"
#import "view_controller.h"
#include "core/config/project_settings.h"
#include "core/io/dir_access.h"
#include "core/io/file_access.h"
#include "core/io/file_access_pack.h"
#include "drivers/unix/syslog_logger.h"
#include "main/main.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 *ios_init_callbacks = nullptr;
static int ios_init_callbacks_count = 0;
static int ios_init_callbacks_capacity = 0;
HashMap<String, void *> OS_IOS::dynamic_symbol_lookup_table;
void add_ios_init_callback(init_callback cb) {
if (ios_init_callbacks_count == ios_init_callbacks_capacity) {
void *new_ptr = realloc(ios_init_callbacks, sizeof(cb) * (ios_init_callbacks_capacity + 32));
if (new_ptr) {
ios_init_callbacks = (init_callback *)(new_ptr);
ios_init_callbacks_capacity += 32;
} else {
ERR_FAIL_MSG("Unable to allocate memory for extension callbacks.");
}
}
ios_init_callbacks[ios_init_callbacks_count++] = cb;
}
void register_dynamic_symbol(char *name, void *address) {
OS_IOS::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_IOS *OS_IOS::get_singleton() {
return (OS_IOS *)OS::get_singleton();
return (OS_IOS *)OS_AppleEmbedded::get_singleton();
}
OS_IOS::OS_IOS() {
for (int i = 0; i < ios_init_callbacks_count; ++i) {
ios_init_callbacks[i]();
}
free(ios_init_callbacks);
ios_init_callbacks = nullptr;
ios_init_callbacks_count = 0;
ios_init_callbacks_capacity = 0;
main_loop = nullptr;
Vector<Logger *> loggers;
loggers.push_back(memnew(IOSTerminalLogger));
_set_logger(memnew(CompositeLogger(loggers)));
AudioDriverManager::add_driver(&audio_driver);
OS_IOS::OS_IOS() :
OS_AppleEmbedded() {
DisplayServerIOS::register_ios_driver();
}
OS_IOS::~OS_IOS() {}
void OS_IOS::alert(const String &p_alert, const String &p_title) {
const CharString utf8_alert = p_alert.utf8();
const CharString utf8_title = p_title.utf8();
iOS::alert(utf8_alert.get_data(), utf8_title.get_data());
}
void OS_IOS::initialize_core() {
OS_Unix::initialize_core();
}
void OS_IOS::initialize() {
initialize_core();
}
void OS_IOS::initialize_joypads() {
joypad_apple = memnew(JoypadApple);
}
void OS_IOS::initialize_modules() {
ios = memnew(iOS);
Engine::get_singleton()->add_singleton(Engine::Singleton("iOS", ios));
}
void OS_IOS::deinitialize_modules() {
if (joypad_apple) {
memdelete(joypad_apple);
}
if (ios) {
memdelete(ios);
}
}
void OS_IOS::set_main_loop(MainLoop *p_main_loop) {
main_loop = p_main_loop;
}
MainLoop *OS_IOS::get_main_loop() const {
return main_loop;
}
void OS_IOS::delete_main_loop() {
if (main_loop) {
main_loop->finalize();
memdelete(main_loop);
}
main_loop = nullptr;
}
bool OS_IOS::iterate() {
if (!main_loop) {
return true;
}
if (DisplayServer::get_singleton()) {
DisplayServer::get_singleton()->process_events();
}
joypad_apple->process_joypads();
return Main::iteration();
}
void OS_IOS::start() {
if (Main::start() == EXIT_SUCCESS) {
main_loop->initialize();
}
}
void OS_IOS::finalize() {
deinitialize_modules();
// Already gets called
//delete_main_loop();
}
// MARK: Dynamic Libraries
_FORCE_INLINE_ String OS_IOS::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_IOS::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_IOS::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_IOS::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_IOS::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_IOS::get_name() const {
return "iOS";
}
String OS_IOS::get_distribution_name() const {
return get_name();
}
String OS_IOS::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_IOS::get_model_name() const {
String model = ios->get_model();
if (model != "") {
return model;
}
return OS_Unix::get_model_name();
}
Error OS_IOS::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_IOS::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_IOS::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_IOS::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_IOS::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_IOS::get_unique_id() const {
NSString *uuid = [UIDevice currentDevice].identifierForVendor.UUIDString;
return String::utf8([uuid UTF8String]);
}
String OS_IOS::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_IOS::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_IOS::_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_IOS::_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_IOS::_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_IOS::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_IOS::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_IOS::vibrate_handheld(int p_duration_ms, float p_amplitude) {
if (ios->supports_haptic_engine()) {
if (p_amplitude > 0.0) {
p_amplitude = CLAMP(p_amplitude, 0.0, 1.0);
}
ios->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_IOS::_check_internal_feature_support(const String &p_feature) {
if (p_feature == "system_fonts") {
return true;
}
if (p_feature == "mobile") {
return true;
}
return false;
}
void OS_IOS::on_focus_out() {
if (is_focused) {
is_focused = false;
if (DisplayServerIOS::get_singleton()) {
DisplayServerIOS::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);
}
[AppDelegate.viewController.godotView stopRendering];
audio_driver.stop();
}
}
void OS_IOS::on_focus_in() {
if (!is_focused) {
is_focused = true;
if (DisplayServerIOS::get_singleton()) {
DisplayServerIOS::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);
}
[AppDelegate.viewController.godotView startRendering];
audio_driver.start();
}
}
void OS_IOS::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_IOS::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_IOS::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);
}
}
#endif // IOS_ENABLED

View file

@ -30,15 +30,4 @@
#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")
#import "drivers/apple_embedded/platform_config.h"

View file

@ -1,67 +1,7 @@
"""Functions used to generate source files during build time"""
import os
import shutil
from platform_methods import detect_mvk, lipo
def combine_libs(target, source, env):
lib_path = target[0].srcnode().abspath
if "osxcross" in env:
libtool = "$IOS_TOOLCHAIN_PATH/usr/bin/${ios_triple}libtool"
else:
libtool = "$IOS_TOOLCHAIN_PATH/usr/bin/libtool"
env.Execute(
libtool + ' -static -o "' + lib_path + '" ' + " ".join([('"' + lib.srcnode().abspath + '"') for lib in source])
)
from platform_methods import generate_bundle_apple_embedded
def generate_bundle(target, source, env):
bin_dir = env.Dir("#bin").abspath
# Template bundle.
app_prefix = "godot." + env["platform"]
rel_prefix = "libgodot." + env["platform"] + "." + "template_release"
dbg_prefix = "libgodot." + env["platform"] + "." + "template_debug"
if env.dev_build:
app_prefix += ".dev"
rel_prefix += ".dev"
dbg_prefix += ".dev"
if env["precision"] == "double":
app_prefix += ".double"
rel_prefix += ".double"
dbg_prefix += ".double"
# Lipo template libraries.
rel_target_bin = lipo(bin_dir + "/" + rel_prefix, env.extra_suffix + ".a")
dbg_target_bin = lipo(bin_dir + "/" + dbg_prefix, env.extra_suffix + ".a")
rel_target_bin_sim = lipo(bin_dir + "/" + rel_prefix, ".simulator" + env.extra_suffix + ".a")
dbg_target_bin_sim = lipo(bin_dir + "/" + dbg_prefix, ".simulator" + env.extra_suffix + ".a")
# Assemble Xcode project bundle.
app_dir = env.Dir("#bin/ios_xcode").abspath
templ = env.Dir("#misc/dist/ios_xcode").abspath
if os.path.exists(app_dir):
shutil.rmtree(app_dir)
shutil.copytree(templ, app_dir)
if rel_target_bin != "":
shutil.copy(rel_target_bin, app_dir + "/libgodot.ios.release.xcframework/ios-arm64/libgodot.a")
if dbg_target_bin != "":
shutil.copy(dbg_target_bin, app_dir + "/libgodot.ios.debug.xcframework/ios-arm64/libgodot.a")
if rel_target_bin_sim != "":
shutil.copy(
rel_target_bin_sim, app_dir + "/libgodot.ios.release.xcframework/ios-arm64_x86_64-simulator/libgodot.a"
)
if dbg_target_bin_sim != "":
shutil.copy(
dbg_target_bin_sim, app_dir + "/libgodot.ios.debug.xcframework/ios-arm64_x86_64-simulator/libgodot.a"
)
mvk_path = detect_mvk(env, "ios-arm64")
if mvk_path != "":
shutil.copytree(mvk_path, app_dir + "/MoltenVK.xcframework")
# ZIP Xcode project bundle.
zip_dir = env.Dir("#bin/" + (app_prefix + env.extra_suffix).replace(".", "_")).abspath
shutil.make_archive(zip_dir, "zip", root_dir=app_dir)
shutil.rmtree(app_dir)
generate_bundle_apple_embedded("ios", "ios-arm64", "ios-arm64_x86_64-simulator", True, target, source, env)

View file

@ -3,6 +3,8 @@
This folder contains the C++, Objective-C and Objective-C++ code for the macOS
platform port.
This platform uses shared Apple code ([`drivers/apple`](/drivers/apple)).
See also [`misc/dist/macos`](/misc/dist/macos) folder for additional files used
by this platform. [`misc/dist/macos_tools.app`](/misc/dist/macos_tools.app) is
an `.app` bundle template used for packaging the macOS editor, while

View file

@ -1,5 +1,6 @@
import os
import platform
import shutil
import subprocess
import sys
@ -160,3 +161,79 @@ def detect_mvk(env, osname):
return mvk_path
return ""
def combine_libs_apple_embedded(target, source, env):
lib_path = target[0].srcnode().abspath
if "osxcross" in env:
libtool = "$APPLE_TOOLCHAIN_PATH/usr/bin/${apple_target_triple}libtool"
else:
libtool = "$APPLE_TOOLCHAIN_PATH/usr/bin/libtool"
env.Execute(
libtool + ' -static -o "' + lib_path + '" ' + " ".join([('"' + lib.srcnode().abspath + '"') for lib in source])
)
def generate_bundle_apple_embedded(platform, framework_dir, framework_dir_sim, use_mkv, target, source, env):
bin_dir = env.Dir("#bin").abspath
# Template bundle.
app_prefix = "godot." + platform
rel_prefix = "libgodot." + platform + "." + "template_release"
dbg_prefix = "libgodot." + platform + "." + "template_debug"
if env.dev_build:
app_prefix += ".dev"
rel_prefix += ".dev"
dbg_prefix += ".dev"
if env["precision"] == "double":
app_prefix += ".double"
rel_prefix += ".double"
dbg_prefix += ".double"
# Lipo template libraries.
#
# env.extra_suffix contains ".simulator" when building for simulator,
# but it's undesired when calling lipo()
extra_suffix = env.extra_suffix.replace(".simulator", "")
rel_target_bin = lipo(bin_dir + "/" + rel_prefix, extra_suffix + ".a")
dbg_target_bin = lipo(bin_dir + "/" + dbg_prefix, extra_suffix + ".a")
rel_target_bin_sim = lipo(bin_dir + "/" + rel_prefix, ".simulator" + extra_suffix + ".a")
dbg_target_bin_sim = lipo(bin_dir + "/" + dbg_prefix, ".simulator" + extra_suffix + ".a")
# Assemble Xcode project bundle.
app_dir = env.Dir("#bin/" + platform + "_xcode").abspath
templ = env.Dir("#misc/dist/" + platform + "_xcode").abspath
if os.path.exists(app_dir):
shutil.rmtree(app_dir)
shutil.copytree(templ, app_dir)
if rel_target_bin != "":
print(f' Copying "{platform}" release framework')
shutil.copy(
rel_target_bin, app_dir + "/libgodot." + platform + ".release.xcframework/" + framework_dir + "/libgodot.a"
)
if dbg_target_bin != "":
print(f' Copying "{platform}" debug framework')
shutil.copy(
dbg_target_bin, app_dir + "/libgodot." + platform + ".debug.xcframework/" + framework_dir + "/libgodot.a"
)
if rel_target_bin_sim != "":
print(f' Copying "{platform}" (simulator) release framework')
shutil.copy(
rel_target_bin_sim,
app_dir + "/libgodot." + platform + ".release.xcframework/" + framework_dir_sim + "/libgodot.a",
)
if dbg_target_bin_sim != "":
print(f' Copying "{platform}" (simulator) debug framework')
shutil.copy(
dbg_target_bin_sim,
app_dir + "/libgodot." + platform + ".debug.xcframework/" + framework_dir_sim + "/libgodot.a",
)
if use_mkv:
mvk_path = detect_mvk(env, "ios-arm64")
if mvk_path != "":
shutil.copytree(mvk_path, app_dir + "/MoltenVK.xcframework")
# ZIP Xcode project bundle.
zip_dir = env.Dir("#bin/" + (app_prefix + extra_suffix).replace(".", "_")).abspath
shutil.make_archive(zip_dir, "zip", root_dir=app_dir)
shutil.rmtree(app_dir)

View file

@ -1791,7 +1791,7 @@ PackedStringArray LightmapGI::get_configuration_warnings() const {
warnings.push_back(RTR("The lightmap has no baked shadowmask textures. Please rebake with the Shadowmask Mode set to anything other than None."));
}
#elif defined(ANDROID_ENABLED) || defined(IOS_ENABLED)
#elif defined(ANDROID_ENABLED) || defined(APPLE_EMBEDDED_ENABLED)
warnings.push_back(vformat(RTR("Lightmaps cannot be baked on %s. Rendering existing baked lightmaps will still work."), OS::get_singleton()->get_name()));
#else
warnings.push_back(RTR("Lightmaps cannot be baked, as the `lightmapper_rd` module was disabled at compile-time. Rendering existing baked lightmaps will still work."));

View file

@ -435,7 +435,7 @@ void Fog::VolumetricFog::init(const Vector3i &fog_size, RID p_sky_shader) {
fog_map = RD::get_singleton()->texture_create(tf, RD::TextureView());
RD::get_singleton()->set_resource_name(fog_map, "Fog map");
#if defined(MACOS_ENABLED) || defined(IOS_ENABLED)
#if defined(MACOS_ENABLED) || defined(APPLE_EMBEDDED_ENABLED)
Vector<uint8_t> dm;
dm.resize_zeroed(fog_size.x * fog_size.y * fog_size.z * 4);
@ -574,7 +574,7 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P
{
RD::Uniform u;
#if defined(MACOS_ENABLED) || defined(IOS_ENABLED)
#if defined(MACOS_ENABLED) || defined(APPLE_EMBEDDED_ENABLED)
u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
#else
u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
@ -594,7 +594,7 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P
{
RD::Uniform u;
#if defined(MACOS_ENABLED) || defined(IOS_ENABLED)
#if defined(MACOS_ENABLED) || defined(APPLE_EMBEDDED_ENABLED)
u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
#else
u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
@ -606,7 +606,7 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P
{
RD::Uniform u;
#if defined(MACOS_ENABLED) || defined(IOS_ENABLED)
#if defined(MACOS_ENABLED) || defined(APPLE_EMBEDDED_ENABLED)
u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
#else
u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
@ -913,7 +913,7 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P
}
{
RD::Uniform u;
#if defined(MACOS_ENABLED) || defined(IOS_ENABLED)
#if defined(MACOS_ENABLED) || defined(APPLE_EMBEDDED_ENABLED)
u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
#else
u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
@ -924,7 +924,7 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P
}
{
RD::Uniform u;
#if defined(MACOS_ENABLED) || defined(IOS_ENABLED)
#if defined(MACOS_ENABLED) || defined(APPLE_EMBEDDED_ENABLED)
u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
#else
u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
@ -936,7 +936,7 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P
{
RD::Uniform u;
#if defined(MACOS_ENABLED) || defined(IOS_ENABLED)
#if defined(MACOS_ENABLED) || defined(APPLE_EMBEDDED_ENABLED)
u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
#else
u.uniform_type = RD::UNIFORM_TYPE_IMAGE;

View file

@ -227,7 +227,7 @@ void ShaderRD::_build_variant_code(StringBuilder &builder, uint32_t p_variant, c
for (const KeyValue<StringName, CharString> &E : p_version->code_sections) {
builder.append(String("#define ") + String(E.key) + "_CODE_USED\n");
}
#if (defined(MACOS_ENABLED) || defined(IOS_ENABLED))
#if (defined(MACOS_ENABLED) || defined(APPLE_EMBEDDED_ENABLED))
if (RD::get_singleton()->get_device_capabilities().device_family == RDD::DEVICE_VULKAN) {
builder.append("#define MOLTENVK_USED\n");
}

View file

@ -265,7 +265,7 @@ bool Utilities::has_os_feature(const String &p_feature) const {
return true;
}
#if !defined(ANDROID_ENABLED) && !defined(IOS_ENABLED)
#if !defined(ANDROID_ENABLED) && !defined(APPLE_EMBEDDED_ENABLED)
// Some Android devices report support for S3TC but we don't expect that and don't export the textures.
// This could be fixed but so few devices support it that it doesn't seem useful (and makes bigger APKs).
// For good measure we do the same hack for iOS, just in case.