diff options
Diffstat (limited to 'platform/osx')
49 files changed, 0 insertions, 14572 deletions
diff --git a/platform/osx/SCsub b/platform/osx/SCsub deleted file mode 100644 index 3a4c95613d..0000000000 --- a/platform/osx/SCsub +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env python - -Import("env") - -from platform_methods import run_in_subprocess -import platform_osx_builders - -files = [ - "os_osx.mm", - "godot_application.mm", - "godot_application_delegate.mm", - "crash_handler_osx.mm", - "osx_terminal_logger.mm", - "display_server_osx.mm", - "godot_content_view.mm", - "godot_window_delegate.mm", - "godot_window.mm", - "key_mapping_osx.mm", - "godot_main_osx.mm", - "dir_access_osx.mm", - "tts_osx.mm", - "joypad_osx.cpp", - "vulkan_context_osx.mm", - "gl_manager_osx_legacy.mm", -] - -prog = env.add_program("#bin/godot", files) - -if env["debug_symbols"] and env["separate_debug_symbols"]: - env.AddPostAction(prog, run_in_subprocess(platform_osx_builders.make_debug_osx)) diff --git a/platform/osx/crash_handler_osx.h b/platform/osx/crash_handler_osx.h deleted file mode 100644 index 72938e5e0a..0000000000 --- a/platform/osx/crash_handler_osx.h +++ /dev/null @@ -1,47 +0,0 @@ -/*************************************************************************/ -/* crash_handler_osx.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* 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. */ -/*************************************************************************/ - -#ifndef CRASH_HANDLER_OSX_H -#define CRASH_HANDLER_OSX_H - -class CrashHandler { - bool disabled; - -public: - void initialize(); - - void disable(); - bool is_disabled() const { return disabled; }; - - CrashHandler(); - ~CrashHandler(); -}; - -#endif // CRASH_HANDLER_OSX_H diff --git a/platform/osx/crash_handler_osx.mm b/platform/osx/crash_handler_osx.mm deleted file mode 100644 index a798ba3b46..0000000000 --- a/platform/osx/crash_handler_osx.mm +++ /dev/null @@ -1,203 +0,0 @@ -/*************************************************************************/ -/* crash_handler_osx.mm */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "crash_handler_osx.h" - -#include "core/config/project_settings.h" -#include "core/os/os.h" -#include "core/string/print_string.h" -#include "core/version.h" -#include "main/main.h" - -#include <string.h> -#include <unistd.h> - -#if defined(DEBUG_ENABLED) -#define CRASH_HANDLER_ENABLED 1 -#endif - -#ifdef CRASH_HANDLER_ENABLED -#include <cxxabi.h> -#include <dlfcn.h> -#include <execinfo.h> -#include <signal.h> -#include <stdlib.h> - -#include <mach-o/dyld.h> -#include <mach-o/getsect.h> - -static uint64_t load_address() { - const struct segment_command_64 *cmd = getsegbyname("__TEXT"); - char full_path[1024]; - uint32_t size = sizeof(full_path); - - if (cmd && !_NSGetExecutablePath(full_path, &size)) { - uint32_t dyld_count = _dyld_image_count(); - for (uint32_t i = 0; i < dyld_count; i++) { - const char *image_name = _dyld_get_image_name(i); - if (image_name && strncmp(image_name, full_path, 1024) == 0) { - return cmd->vmaddr + _dyld_get_image_vmaddr_slide(i); - } - } - } - - return 0; -} - -static void handle_crash(int sig) { - if (OS::get_singleton() == nullptr) { - abort(); - } - - void *bt_buffer[256]; - size_t size = backtrace(bt_buffer, 256); - String _execpath = OS::get_singleton()->get_executable_path(); - - String msg; - const ProjectSettings *proj_settings = ProjectSettings::get_singleton(); - if (proj_settings) { - msg = proj_settings->get("debug/settings/crash_handler/message"); - } - - // Tell MainLoop about the crash. This can be handled by users too in Node. - if (OS::get_singleton()->get_main_loop()) { - OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH); - } - - // Dump the backtrace to stderr with a message to the user - print_error("\n================================================================"); - print_error(vformat("%s: Program crashed with signal %d", __FUNCTION__, sig)); - - // Print the engine version just before, so that people are reminded to include the version in backtrace reports. - if (String(VERSION_HASH).is_empty()) { - print_error(vformat("Engine version: %s", VERSION_FULL_NAME)); - } else { - print_error(vformat("Engine version: %s (%s)", VERSION_FULL_NAME, VERSION_HASH)); - } - print_error(vformat("Dumping the backtrace. %s", msg)); - char **strings = backtrace_symbols(bt_buffer, size); - if (strings) { - void *load_addr = (void *)load_address(); - - for (size_t i = 1; i < size; i++) { - char fname[1024]; - Dl_info info; - - snprintf(fname, 1024, "%s", strings[i]); - - // Try to demangle the function name to provide a more readable one - if (dladdr(bt_buffer[i], &info) && info.dli_sname) { - if (info.dli_sname[0] == '_') { - int status; - char *demangled = abi::__cxa_demangle(info.dli_sname, nullptr, 0, &status); - - if (status == 0 && demangled) { - snprintf(fname, 1024, "%s", demangled); - } - - if (demangled) { - free(demangled); - } - } - } - - String output = fname; - - // Try to get the file/line number using atos - if (bt_buffer[i] > (void *)0x0 && OS::get_singleton()) { - List<String> args; - char str[1024]; - - args.push_back("-o"); - args.push_back(_execpath); -#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) - args.push_back("-arch"); - args.push_back("x86_64"); -#elif defined(__aarch64__) - args.push_back("-arch"); - args.push_back("arm64"); -#endif - args.push_back("-l"); - snprintf(str, 1024, "%p", load_addr); - args.push_back(str); - snprintf(str, 1024, "%p", bt_buffer[i]); - args.push_back(str); - - int ret; - String out = ""; - Error err = OS::get_singleton()->execute(String("atos"), args, &out, &ret); - if (err == OK && out.substr(0, 2) != "0x") { - out = out.substr(0, out.length() - 1); - output = out; - } - } - - print_error(vformat("[%d] %s", (int64_t)i, output)); - } - - free(strings); - } - print_error("-- END OF BACKTRACE --"); - print_error("================================================================"); - - // Abort to pass the error to the OS - abort(); -} -#endif - -CrashHandler::CrashHandler() { - disabled = false; -} - -CrashHandler::~CrashHandler() { - disable(); -} - -void CrashHandler::disable() { - if (disabled) { - return; - } - -#ifdef CRASH_HANDLER_ENABLED - signal(SIGSEGV, nullptr); - signal(SIGFPE, nullptr); - signal(SIGILL, nullptr); -#endif - - disabled = true; -} - -void CrashHandler::initialize() { -#ifdef CRASH_HANDLER_ENABLED - signal(SIGSEGV, handle_crash); - signal(SIGFPE, handle_crash); - signal(SIGILL, handle_crash); -#endif -} diff --git a/platform/osx/detect.py b/platform/osx/detect.py deleted file mode 100644 index 47765cff71..0000000000 --- a/platform/osx/detect.py +++ /dev/null @@ -1,251 +0,0 @@ -import os -import sys -from methods import detect_darwin_sdk_path - - -def is_active(): - return True - - -def get_name(): - return "OSX" - - -def can_build(): - if sys.platform == "darwin" or ("OSXCROSS_ROOT" in os.environ): - return True - - return False - - -def get_opts(): - from SCons.Variables import BoolVariable, EnumVariable - - return [ - ("osxcross_sdk", "OSXCross SDK version", "darwin16"), - ("MACOS_SDK_PATH", "Path to the macOS SDK", ""), - ("vulkan_sdk_path", "Path to the Vulkan SDK", ""), - EnumVariable("macports_clang", "Build using Clang from MacPorts", "no", ("no", "5.0", "devel")), - BoolVariable("debug_symbols", "Add debugging symbols to release/release_debug builds", True), - BoolVariable("separate_debug_symbols", "Create a separate file containing debugging symbols", False), - BoolVariable("use_ubsan", "Use LLVM/GCC compiler undefined behavior sanitizer (UBSAN)", False), - BoolVariable("use_asan", "Use LLVM/GCC compiler address sanitizer (ASAN)", False), - BoolVariable("use_tsan", "Use LLVM/GCC compiler thread sanitizer (TSAN)", False), - BoolVariable("use_coverage", "Use instrumentation codes in the binary (e.g. for code coverage)", False), - ] - - -def get_flags(): - return [ - ("use_volk", False), - ] - - -def get_mvk_sdk_path(): - def int_or_zero(i): - try: - return int(i) - except: - return 0 - - def ver_parse(a): - return [int_or_zero(i) for i in a.split(".")] - - dirname = os.path.expanduser("~/VulkanSDK") - files = os.listdir(dirname) - - ver_file = "0.0.0.0" - ver_num = ver_parse(ver_file) - - for file in files: - if os.path.isdir(os.path.join(dirname, file)): - ver_comp = ver_parse(file) - lib_name = os.path.join( - os.path.join(dirname, file), "MoltenVK/MoltenVK.xcframework/macos-arm64_x86_64/libMoltenVK.a" - ) - if os.path.isfile(lib_name) and ver_comp > ver_num: - ver_num = ver_comp - ver_file = file - - return os.path.join(os.path.join(dirname, ver_file), "MoltenVK/MoltenVK.xcframework/macos-arm64_x86_64/") - - -def configure(env): - ## Build type - - if env["target"] == "release": - if env["optimize"] == "speed": # optimize for speed (default) - env.Prepend(CCFLAGS=["-O3", "-fomit-frame-pointer", "-ftree-vectorize"]) - elif env["optimize"] == "size": # optimize for size - env.Prepend(CCFLAGS=["-Os", "-ftree-vectorize"]) - if env["arch"] != "arm64": - env.Prepend(CCFLAGS=["-msse2"]) - - if env["debug_symbols"]: - env.Prepend(CCFLAGS=["-g2"]) - - elif env["target"] == "release_debug": - if env["optimize"] == "speed": # optimize for speed (default) - env.Prepend(CCFLAGS=["-O2"]) - elif env["optimize"] == "size": # optimize for size - env.Prepend(CCFLAGS=["-Os"]) - if env["debug_symbols"]: - env.Prepend(CCFLAGS=["-g2"]) - - elif env["target"] == "debug": - env.Prepend(CCFLAGS=["-g3"]) - env.Prepend(LINKFLAGS=["-Xlinker", "-no_deduplicate"]) - - ## Architecture - - # Mac OS X no longer runs on 32-bit since 10.7 which is unsupported since 2014 - # As such, we only support 64-bit - env["bits"] = "64" - - ## Compiler configuration - - # Save this in environment for use by other modules - if "OSXCROSS_ROOT" in os.environ: - env["osxcross"] = True - - if env["arch"] == "arm64": - print("Building for macOS 11.0+, platform arm64.") - env.Append(ASFLAGS=["-arch", "arm64", "-mmacosx-version-min=11.0"]) - env.Append(CCFLAGS=["-arch", "arm64", "-mmacosx-version-min=11.0"]) - env.Append(LINKFLAGS=["-arch", "arm64", "-mmacosx-version-min=11.0"]) - else: - print("Building for macOS 10.12+, platform x86_64.") - env.Append(ASFLAGS=["-arch", "x86_64", "-mmacosx-version-min=10.12"]) - env.Append(CCFLAGS=["-arch", "x86_64", "-mmacosx-version-min=10.12"]) - env.Append(LINKFLAGS=["-arch", "x86_64", "-mmacosx-version-min=10.12"]) - - env.Append(CCFLAGS=["-fobjc-arc"]) - - if not "osxcross" in env: # regular native build - if env["macports_clang"] != "no": - mpprefix = os.environ.get("MACPORTS_PREFIX", "/opt/local") - mpclangver = env["macports_clang"] - env["CC"] = mpprefix + "/libexec/llvm-" + mpclangver + "/bin/clang" - env["CXX"] = mpprefix + "/libexec/llvm-" + mpclangver + "/bin/clang++" - env["AR"] = mpprefix + "/libexec/llvm-" + mpclangver + "/bin/llvm-ar" - env["RANLIB"] = mpprefix + "/libexec/llvm-" + mpclangver + "/bin/llvm-ranlib" - env["AS"] = mpprefix + "/libexec/llvm-" + mpclangver + "/bin/llvm-as" - else: - env["CC"] = "clang" - env["CXX"] = "clang++" - - detect_darwin_sdk_path("osx", env) - env.Append(CCFLAGS=["-isysroot", "$MACOS_SDK_PATH"]) - env.Append(LINKFLAGS=["-isysroot", "$MACOS_SDK_PATH"]) - - else: # osxcross build - root = os.environ.get("OSXCROSS_ROOT", 0) - if env["arch"] == "arm64": - basecmd = root + "/target/bin/arm64-apple-" + env["osxcross_sdk"] + "-" - else: - basecmd = root + "/target/bin/x86_64-apple-" + env["osxcross_sdk"] + "-" - - ccache_path = os.environ.get("CCACHE") - if ccache_path is None: - env["CC"] = basecmd + "cc" - env["CXX"] = basecmd + "c++" - else: - # there aren't any ccache wrappers available for OS X cross-compile, - # to enable caching we need to prepend the path to the ccache binary - env["CC"] = ccache_path + " " + basecmd + "cc" - env["CXX"] = ccache_path + " " + basecmd + "c++" - env["AR"] = basecmd + "ar" - env["RANLIB"] = basecmd + "ranlib" - env["AS"] = basecmd + "as" - - if env["use_ubsan"] or env["use_asan"] or env["use_tsan"]: - env.extra_suffix += ".san" - env.Append(CCFLAGS=["-DSANITIZERS_ENABLED"]) - - if env["use_ubsan"]: - env.Append( - CCFLAGS=[ - "-fsanitize=undefined,shift,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,null,return,signed-integer-overflow,bounds,float-divide-by-zero,float-cast-overflow,nonnull-attribute,returns-nonnull-attribute,bool,enum,vptr,pointer-overflow,builtin" - ] - ) - env.Append(LINKFLAGS=["-fsanitize=undefined"]) - env.Append(CCFLAGS=["-fsanitize=nullability-return,nullability-arg,function,nullability-assign"]) - - if env["use_asan"]: - env.Append(CCFLAGS=["-fsanitize=address,pointer-subtract,pointer-compare"]) - env.Append(LINKFLAGS=["-fsanitize=address"]) - - if env["use_tsan"]: - env.Append(CCFLAGS=["-fsanitize=thread"]) - env.Append(LINKFLAGS=["-fsanitize=thread"]) - - if env["use_coverage"]: - env.Append(CCFLAGS=["-ftest-coverage", "-fprofile-arcs"]) - env.Append(LINKFLAGS=["-ftest-coverage", "-fprofile-arcs"]) - - ## Dependencies - - if env["builtin_libtheora"]: - if env["arch"] != "arm64": - env["x86_libtheora_opt_gcc"] = True - - ## Flags - - env.Prepend(CPPPATH=["#platform/osx"]) - env.Append(CPPDEFINES=["OSX_ENABLED", "UNIX_ENABLED", "APPLE_STYLE_KEYS", "COREAUDIO_ENABLED", "COREMIDI_ENABLED"]) - env.Append( - LINKFLAGS=[ - "-framework", - "Cocoa", - "-framework", - "Carbon", - "-framework", - "AudioUnit", - "-framework", - "CoreAudio", - "-framework", - "CoreMIDI", - "-framework", - "IOKit", - "-framework", - "ForceFeedback", - "-framework", - "CoreVideo", - "-framework", - "AVFoundation", - "-framework", - "CoreMedia", - ] - ) - env.Append(LIBS=["pthread", "z"]) - - if env["opengl3"]: - env.Append(CPPDEFINES=["GLES_ENABLED", "GLES3_ENABLED"]) - env.Append(CCFLAGS=["-Wno-deprecated-declarations"]) # Disable deprecation warnings - env.Append(LINKFLAGS=["-framework", "OpenGL"]) - - env.Append(LINKFLAGS=["-rpath", "@executable_path/../Frameworks", "-rpath", "@executable_path"]) - - if env["vulkan"]: - env.Append(CPPDEFINES=["VULKAN_ENABLED"]) - env.Append(LINKFLAGS=["-framework", "Metal", "-framework", "QuartzCore", "-framework", "IOSurface"]) - if not env["use_volk"]: - env.Append(LINKFLAGS=["-lMoltenVK"]) - mvk_found = False - if env["vulkan_sdk_path"] != "": - mvk_path = os.path.join( - os.path.expanduser(env["vulkan_sdk_path"]), "MoltenVK/MoltenVK.xcframework/macos-arm64_x86_64/" - ) - if os.path.isfile(os.path.join(mvk_path, "libMoltenVK.a")): - mvk_found = True - env.Append(LINKFLAGS=["-L" + mvk_path]) - if not mvk_found: - mvk_path = get_mvk_sdk_path() - if os.path.isfile(os.path.join(mvk_path, "libMoltenVK.a")): - mvk_found = True - env.Append(LINKFLAGS=["-L" + mvk_path]) - if not mvk_found: - print( - "MoltenVK SDK installation directory not found, use 'vulkan_sdk_path' SCons parameter to specify SDK path." - ) - sys.exit(255) diff --git a/platform/osx/dir_access_osx.h b/platform/osx/dir_access_osx.h deleted file mode 100644 index 3c66c81d4f..0000000000 --- a/platform/osx/dir_access_osx.h +++ /dev/null @@ -1,55 +0,0 @@ -/*************************************************************************/ -/* dir_access_osx.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* 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. */ -/*************************************************************************/ - -#ifndef DIR_ACCESS_OSX_H -#define DIR_ACCESS_OSX_H - -#if defined(UNIX_ENABLED) || defined(LIBC_FILEIO_ENABLED) - -#include <dirent.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include "core/io/dir_access.h" -#include "drivers/unix/dir_access_unix.h" - -class DirAccessOSX : public DirAccessUnix { -protected: - virtual String fix_unicode_name(const char *p_name) const; - - virtual int get_drive_count(); - virtual String get_drive(int p_drive); - - virtual bool is_hidden(const String &p_name); -}; - -#endif //UNIX ENABLED -#endif diff --git a/platform/osx/dir_access_osx.mm b/platform/osx/dir_access_osx.mm deleted file mode 100644 index 6bafb9470d..0000000000 --- a/platform/osx/dir_access_osx.mm +++ /dev/null @@ -1,81 +0,0 @@ -/*************************************************************************/ -/* dir_access_osx.mm */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "dir_access_osx.h" - -#if defined(UNIX_ENABLED) || defined(LIBC_FILEIO_ENABLED) - -#include <errno.h> - -#import <AppKit/NSWorkspace.h> -#import <Foundation/Foundation.h> - -String DirAccessOSX::fix_unicode_name(const char *p_name) const { - String fname; - NSString *nsstr = [[NSString stringWithUTF8String:p_name] precomposedStringWithCanonicalMapping]; - - fname.parse_utf8([nsstr UTF8String]); - - return fname; -} - -int DirAccessOSX::get_drive_count() { - NSArray *res_keys = [NSArray arrayWithObjects:NSURLVolumeURLKey, NSURLIsSystemImmutableKey, nil]; - NSArray *vols = [[NSFileManager defaultManager] mountedVolumeURLsIncludingResourceValuesForKeys:res_keys options:NSVolumeEnumerationSkipHiddenVolumes]; - - return [vols count]; -} - -String DirAccessOSX::get_drive(int p_drive) { - NSArray *res_keys = [NSArray arrayWithObjects:NSURLVolumeURLKey, NSURLIsSystemImmutableKey, nil]; - NSArray *vols = [[NSFileManager defaultManager] mountedVolumeURLsIncludingResourceValuesForKeys:res_keys options:NSVolumeEnumerationSkipHiddenVolumes]; - int count = [vols count]; - - ERR_FAIL_INDEX_V(p_drive, count, ""); - - String volname; - NSString *path = [vols[p_drive] path]; - - volname.parse_utf8([path UTF8String]); - - return volname; -} - -bool DirAccessOSX::is_hidden(const String &p_name) { - String f = get_current_dir().plus_file(p_name); - NSURL *url = [NSURL fileURLWithPath:@(f.utf8().get_data())]; - NSNumber *hidden = nil; - if (![url getResourceValue:&hidden forKey:NSURLIsHiddenKey error:nil]) { - return DirAccessUnix::is_hidden(p_name); - } - return [hidden boolValue]; -} - -#endif //posix_enabled diff --git a/platform/osx/display_server_osx.h b/platform/osx/display_server_osx.h deleted file mode 100644 index 9575cb29a2..0000000000 --- a/platform/osx/display_server_osx.h +++ /dev/null @@ -1,406 +0,0 @@ -/*************************************************************************/ -/* display_server_osx.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* 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. */ -/*************************************************************************/ - -#ifndef DISPLAY_SERVER_OSX_H -#define DISPLAY_SERVER_OSX_H - -#define BitMap _QDBitMap // Suppress deprecated QuickDraw definition. - -#include "core/input/input.h" -#include "servers/display_server.h" - -#if defined(GLES3_ENABLED) -#include "gl_manager_osx_legacy.h" -#endif // GLES3_ENABLED - -#if defined(VULKAN_ENABLED) -#include "drivers/vulkan/rendering_device_vulkan.h" -#include "platform/osx/vulkan_context_osx.h" -#endif // VULKAN_ENABLED - -#import <AppKit/AppKit.h> -#import <AppKit/NSCursor.h> -#import <ApplicationServices/ApplicationServices.h> -#import <CoreVideo/CoreVideo.h> -#import <Foundation/Foundation.h> - -#undef BitMap -#undef CursorShape - -class DisplayServerOSX : public DisplayServer { - GDCLASS(DisplayServerOSX, DisplayServer) - - _THREAD_SAFE_CLASS_ - -public: - struct KeyEvent { - WindowID window_id = INVALID_WINDOW_ID; - unsigned int osx_state = false; - bool pressed = false; - bool echo = false; - bool raw = false; - Key keycode = Key::NONE; - Key physical_keycode = Key::NONE; - uint32_t unicode = 0; - }; - - struct WindowData { - id window_delegate; - id window_object; - id window_view; - - Vector<Vector2> mpath; - - Point2i mouse_pos; - - Size2i min_size; - Size2i max_size; - Size2i size; - - bool im_active = false; - Size2i im_position; - - Callable rect_changed_callback; - Callable event_callback; - Callable input_event_callback; - Callable input_text_callback; - Callable drop_files_callback; - - ObjectID instance_id; - - WindowID transient_parent = INVALID_WINDOW_ID; - bool exclusive = false; - HashSet<WindowID> transient_children; - - bool layered_window = false; - bool fullscreen = false; - bool on_top = false; - bool borderless = false; - bool resize_disabled = false; - bool no_focus = false; - bool is_popup = false; - - Rect2i parent_safe_rect; - }; - - List<WindowID> popup_list; - uint64_t time_since_popup = 0; - -private: -#if defined(GLES3_ENABLED) - GLManager_OSX *gl_manager = nullptr; -#endif -#if defined(VULKAN_ENABLED) - VulkanContextOSX *context_vulkan = nullptr; - RenderingDeviceVulkan *rendering_device_vulkan = nullptr; -#endif - String rendering_driver; - - NSMenu *apple_menu = nullptr; - NSMenu *dock_menu = nullptr; - HashMap<String, NSMenu *> submenu; - - struct WarpEvent { - NSTimeInterval timestamp; - NSPoint delta; - }; - List<WarpEvent> warp_events; - NSTimeInterval last_warp = 0; - bool ignore_warp = false; - - Vector<KeyEvent> key_event_buffer; - int key_event_pos = 0; - - id tts = nullptr; - - Point2i im_selection; - String im_text; - - CGEventSourceRef event_source; - MouseMode mouse_mode = MOUSE_MODE_VISIBLE; - MouseButton last_button_state = MouseButton::NONE; - - bool drop_events = false; - bool in_dispatch_input_event = false; - - struct LayoutInfo { - String name; - String code; - }; - Vector<LayoutInfo> kbd_layouts; - int current_layout = 0; - bool keyboard_layout_dirty = true; - - WindowID last_focused_window = INVALID_WINDOW_ID; - WindowID window_id_counter = MAIN_WINDOW_ID; - float display_max_scale = 1.f; - Point2i origin; - bool displays_arrangement_dirty = true; - bool is_resizing = false; - - CursorShape cursor_shape = CURSOR_ARROW; - NSCursor *cursors[CURSOR_MAX]; - HashMap<CursorShape, Vector<Variant>> cursors_cache; - - HashMap<WindowID, WindowData> windows; - - const NSMenu *_get_menu_root(const String &p_menu_root) const; - NSMenu *_get_menu_root(const String &p_menu_root); - - WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, const Rect2i &p_rect); - void _update_window_style(WindowData p_wd); - void _set_window_per_pixel_transparency_enabled(bool p_enabled, WindowID p_window); - - void _update_displays_arrangement(); - Point2i _get_screens_origin() const; - Point2i _get_native_screen_position(int p_screen) const; - static void _displays_arrangement_changed(CGDirectDisplayID display_id, CGDisplayChangeSummaryFlags flags, void *user_info); - - static void _dispatch_input_events(const Ref<InputEvent> &p_event); - void _dispatch_input_event(const Ref<InputEvent> &p_event); - void _push_input(const Ref<InputEvent> &p_event); - void _process_key_events(); - void _update_keyboard_layouts(); - static void _keyboard_layout_changed(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef user_info); - NSImage *_convert_to_nsimg(Ref<Image> &p_image) const; - - static NSCursor *_cursor_from_selector(SEL p_selector, SEL p_fallback = nil); - -public: - NSMenu *get_dock_menu() const; - void menu_callback(id p_sender); - - bool has_window(WindowID p_window) const; - WindowData &get_window(WindowID p_window); - - void send_event(NSEvent *p_event); - void send_window_event(const WindowData &p_wd, WindowEvent p_event); - void release_pressed_events(); - void get_key_modifier_state(unsigned int p_osx_state, Ref<InputEventWithModifiers> r_state) const; - void update_mouse_pos(WindowData &p_wd, NSPoint p_location_in_window); - void push_to_key_event_buffer(const KeyEvent &p_event); - void update_im_text(const Point2i &p_selection, const String &p_text); - void set_last_focused_window(WindowID p_window); - bool mouse_process_popups(bool p_close = false); - void popup_open(WindowID p_window); - void popup_close(WindowID p_window); - void set_is_resizing(bool p_is_resizing); - bool get_is_resizing() const; - - void window_update(WindowID p_window); - void window_destroy(WindowID p_window); - void window_resize(WindowID p_window, int p_width, int p_height); - - virtual bool has_feature(Feature p_feature) const override; - virtual String get_name() const override; - - virtual void global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; - virtual void global_menu_add_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; - virtual void global_menu_add_icon_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; - virtual void global_menu_add_icon_check_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; - virtual void global_menu_add_radio_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; - virtual void global_menu_add_icon_radio_check_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; - virtual void global_menu_add_multistate_item(const String &p_menu_root, const String &p_label, int p_max_states, int p_default_state, const Callable &p_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override; - virtual void global_menu_add_submenu_item(const String &p_menu_root, const String &p_label, const String &p_submenu, int p_index = -1) override; - virtual void global_menu_add_separator(const String &p_menu_root, int p_index = -1) override; - - virtual int global_menu_get_item_index_from_text(const String &p_menu_root, const String &p_text) const override; - virtual int global_menu_get_item_index_from_tag(const String &p_menu_root, const Variant &p_tag) const override; - - virtual bool global_menu_is_item_checked(const String &p_menu_root, int p_idx) const override; - virtual bool global_menu_is_item_checkable(const String &p_menu_root, int p_idx) const override; - virtual bool global_menu_is_item_radio_checkable(const String &p_menu_root, int p_idx) const override; - virtual Callable global_menu_get_item_callback(const String &p_menu_root, int p_idx) const override; - virtual Variant global_menu_get_item_tag(const String &p_menu_root, int p_idx) const override; - virtual String global_menu_get_item_text(const String &p_menu_root, int p_idx) const override; - virtual String global_menu_get_item_submenu(const String &p_menu_root, int p_idx) const override; - virtual Key global_menu_get_item_accelerator(const String &p_menu_root, int p_idx) const override; - virtual bool global_menu_is_item_disabled(const String &p_menu_root, int p_idx) const override; - virtual String global_menu_get_item_tooltip(const String &p_menu_root, int p_idx) const override; - virtual int global_menu_get_item_state(const String &p_menu_root, int p_idx) const override; - virtual int global_menu_get_item_max_states(const String &p_menu_root, int p_idx) const override; - virtual Ref<Texture2D> global_menu_get_item_icon(const String &p_menu_root, int p_idx) const override; - - virtual void global_menu_set_item_checked(const String &p_menu_root, int p_idx, bool p_checked) override; - virtual void global_menu_set_item_checkable(const String &p_menu_root, int p_idx, bool p_checkable) override; - virtual void global_menu_set_item_radio_checkable(const String &p_menu_root, int p_idx, bool p_checkable) override; - virtual void global_menu_set_item_callback(const String &p_menu_root, int p_idx, const Callable &p_callback) override; - virtual void global_menu_set_item_tag(const String &p_menu_root, int p_idx, const Variant &p_tag) override; - virtual void global_menu_set_item_text(const String &p_menu_root, int p_idx, const String &p_text) override; - virtual void global_menu_set_item_submenu(const String &p_menu_root, int p_idx, const String &p_submenu) override; - virtual void global_menu_set_item_accelerator(const String &p_menu_root, int p_idx, Key p_keycode) override; - virtual void global_menu_set_item_disabled(const String &p_menu_root, int p_idx, bool p_disabled) override; - virtual void global_menu_set_item_tooltip(const String &p_menu_root, int p_idx, const String &p_tooltip) override; - virtual void global_menu_set_item_state(const String &p_menu_root, int p_idx, int p_state) override; - virtual void global_menu_set_item_max_states(const String &p_menu_root, int p_idx, int p_max_states) override; - virtual void global_menu_set_item_icon(const String &p_menu_root, int p_idx, const Ref<Texture2D> &p_icon) override; - - virtual int global_menu_get_item_count(const String &p_menu_root) const override; - - virtual void global_menu_remove_item(const String &p_menu_root, int p_idx) override; - virtual void global_menu_clear(const String &p_menu_root) override; - - virtual bool tts_is_speaking() const override; - virtual bool tts_is_paused() const override; - virtual Array 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 Error dialog_show(String p_title, String p_description, Vector<String> p_buttons, const Callable &p_callback) override; - virtual Error dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback) override; - - virtual void mouse_set_mode(MouseMode p_mode) override; - virtual MouseMode mouse_get_mode() const override; - - bool update_mouse_wrap(WindowData &p_wd, NSPoint &r_delta, NSPoint &r_mpos, NSTimeInterval p_timestamp); - virtual void warp_mouse(const Point2i &p_position) override; - virtual Point2i mouse_get_position() const override; - void mouse_set_button_state(MouseButton p_state); - virtual MouseButton mouse_get_button_state() const override; - - virtual void clipboard_set(const String &p_text) override; - virtual String clipboard_get() const override; - - virtual int get_screen_count() 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 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_max_scale() const override; - virtual Rect2i screen_get_usable_rect(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<int> get_window_list() const override; - - virtual WindowID create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i()) override; - virtual void show_window(WindowID p_id) override; - virtual void delete_sub_window(WindowID p_id) override; - - virtual WindowID window_get_active_popup() const override; - virtual void window_set_popup_safe_rect(WindowID p_window, const Rect2i &p_rect) override; - virtual Rect2i window_get_popup_safe_rect(WindowID p_window) const 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; - - virtual void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID) override; - virtual void window_set_mouse_passthrough(const Vector<Vector2> &p_region, 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 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_exclusive(WindowID p_window, bool p_exclusive) 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_real_size(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_can_draw(WindowID p_window = MAIN_WINDOW_ID) const override; - - virtual bool can_any_window_draw() const override; - - virtual void window_set_ime_active(const bool p_active, WindowID p_window = MAIN_WINDOW_ID) override; - virtual void window_set_ime_position(const Point2i &p_pos, WindowID p_window = MAIN_WINDOW_ID) 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 gl_window_make_current(DisplayServer::WindowID p_window_id) 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 Point2i ime_get_selection() const override; - virtual String ime_get_text() const override; - - void cursor_update_shape(); - virtual void cursor_set_shape(CursorShape p_shape) override; - virtual CursorShape cursor_get_shape() const override; - virtual void cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()) override; - - virtual bool get_swap_cancel_ok() override; - - virtual int keyboard_get_layout_count() const override; - virtual int keyboard_get_current_layout() const override; - virtual void keyboard_set_current_layout(int p_index) override; - virtual String keyboard_get_layout_language(int p_index) const override; - virtual String keyboard_get_layout_name(int p_index) const override; - virtual Key keyboard_get_keycode_from_physical(Key p_keycode) const override; - - virtual void process_events() override; - virtual void force_process_and_drop_events() override; - - virtual void release_rendering_thread() override; - virtual void make_rendering_thread() override; - virtual void swap_buffers() override; - - virtual void set_native_icon(const String &p_filename) override; - virtual void set_icon(const Ref<Image> &p_icon) override; - - static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); - static Vector<String> get_rendering_drivers_func(); - - static void register_osx_driver(); - - DisplayServerOSX(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); - ~DisplayServerOSX(); -}; - -#endif // DISPLAY_SERVER_OSX_H diff --git a/platform/osx/display_server_osx.mm b/platform/osx/display_server_osx.mm deleted file mode 100644 index 11474dac46..0000000000 --- a/platform/osx/display_server_osx.mm +++ /dev/null @@ -1,3304 +0,0 @@ -/*************************************************************************/ -/* display_server_osx.mm */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "display_server_osx.h" - -#include "godot_content_view.h" -#include "godot_menu_item.h" -#include "godot_window.h" -#include "godot_window_delegate.h" -#include "key_mapping_osx.h" -#include "os_osx.h" - -#include "tts_osx.h" - -#include "core/io/marshalls.h" -#include "core/math/geometry_2d.h" -#include "core/os/keyboard.h" -#include "main/main.h" -#include "scene/resources/texture.h" - -#import <Carbon/Carbon.h> -#import <Cocoa/Cocoa.h> -#import <IOKit/IOCFPlugIn.h> -#import <IOKit/IOKitLib.h> -#import <IOKit/hid/IOHIDKeys.h> -#import <IOKit/hid/IOHIDLib.h> - -#if defined(GLES3_ENABLED) -#include "drivers/gles3/rasterizer_gles3.h" -#endif - -#if defined(VULKAN_ENABLED) -#include "servers/rendering/renderer_rd/renderer_compositor_rd.h" -#endif - -const NSMenu *DisplayServerOSX::_get_menu_root(const String &p_menu_root) const { - const NSMenu *menu = nullptr; - if (p_menu_root == "") { - // Main menu. - menu = [NSApp mainMenu]; - } else if (p_menu_root.to_lower() == "_dock") { - // macOS dock menu. - menu = dock_menu; - } else { - // Submenu. - if (submenu.has(p_menu_root)) { - menu = submenu[p_menu_root]; - } - } - if (menu == apple_menu) { - // Do not allow to change Apple menu. - return nullptr; - } - return menu; -} - -NSMenu *DisplayServerOSX::_get_menu_root(const String &p_menu_root) { - NSMenu *menu = nullptr; - if (p_menu_root == "") { - // Main menu. - menu = [NSApp mainMenu]; - } else if (p_menu_root.to_lower() == "_dock") { - // macOS dock menu. - menu = dock_menu; - } else { - // Submenu. - if (!submenu.has(p_menu_root)) { - NSMenu *n_menu = [[NSMenu alloc] initWithTitle:[NSString stringWithUTF8String:p_menu_root.utf8().get_data()]]; - [n_menu setAutoenablesItems:NO]; - submenu[p_menu_root] = n_menu; - } - menu = submenu[p_menu_root]; - } - if (menu == apple_menu) { - // Do not allow to change Apple menu. - return nullptr; - } - return menu; -} - -DisplayServerOSX::WindowID DisplayServerOSX::_create_window(WindowMode p_mode, VSyncMode p_vsync_mode, const Rect2i &p_rect) { - WindowID id; - const float scale = screen_get_max_scale(); - { - WindowData wd; - - wd.window_delegate = [[GodotWindowDelegate alloc] init]; - ERR_FAIL_COND_V_MSG(wd.window_delegate == nil, INVALID_WINDOW_ID, "Can't create a window delegate"); - [wd.window_delegate setWindowID:window_id_counter]; - - Point2i position = p_rect.position; - // OS X native y-coordinate relative to _get_screens_origin() is negative, - // Godot passes a positive value. - position.y *= -1; - position += _get_screens_origin(); - - // initWithContentRect uses bottom-left corner of the window’s frame as origin. - wd.window_object = [[GodotWindow alloc] - initWithContentRect:NSMakeRect(position.x / scale, (position.y - p_rect.size.height) / scale, p_rect.size.width / scale, p_rect.size.height / scale) - styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable - backing:NSBackingStoreBuffered - defer:NO]; - ERR_FAIL_COND_V_MSG(wd.window_object == nil, INVALID_WINDOW_ID, "Can't create a window"); - [wd.window_object setWindowID:window_id_counter]; - - wd.window_view = [[GodotContentView alloc] init]; - ERR_FAIL_COND_V_MSG(wd.window_view == nil, INVALID_WINDOW_ID, "Can't create a window view"); - [wd.window_view setWindowID:window_id_counter]; - [wd.window_view setWantsLayer:TRUE]; - - [wd.window_object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; - [wd.window_object setContentView:wd.window_view]; - [wd.window_object setDelegate:wd.window_delegate]; - [wd.window_object setAcceptsMouseMovedEvents:YES]; - [wd.window_object setRestorable:NO]; - [wd.window_object setColorSpace:[NSColorSpace sRGBColorSpace]]; - - if ([wd.window_object respondsToSelector:@selector(setTabbingMode:)]) { - [wd.window_object setTabbingMode:NSWindowTabbingModeDisallowed]; - } - - CALayer *layer = [(NSView *)wd.window_view layer]; - if (layer) { - layer.contentsScale = scale; - } - -#if defined(VULKAN_ENABLED) - if (context_vulkan) { - Error err = context_vulkan->window_create(window_id_counter, p_vsync_mode, wd.window_view, p_rect.size.width, p_rect.size.height); - ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Can't create a Vulkan context"); - } -#endif -#if defined(GLES3_ENABLED) - if (gl_manager) { - Error err = gl_manager->window_create(window_id_counter, wd.window_view, p_rect.size.width, p_rect.size.height); - ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Can't create an OpenGL context"); - } -#endif - id = window_id_counter++; - windows[id] = wd; - } - - WindowData &wd = windows[id]; - window_set_mode(p_mode, id); - - const NSRect contentRect = [wd.window_view frame]; - wd.size.width = contentRect.size.width * scale; - wd.size.height = contentRect.size.height * scale; - - CALayer *layer = [(NSView *)wd.window_view layer]; - if (layer) { - layer.contentsScale = scale; - } - -#if defined(GLES3_ENABLED) - if (gl_manager) { - gl_manager->window_resize(id, wd.size.width, wd.size.height); - } -#endif -#if defined(VULKAN_ENABLED) - if (context_vulkan) { - context_vulkan->window_resize(id, wd.size.width, wd.size.height); - } -#endif - - return id; -} - -void DisplayServerOSX::_update_window_style(WindowData p_wd) { - bool borderless_full = false; - - if (p_wd.borderless) { - NSRect frameRect = [p_wd.window_object frame]; - NSRect screenRect = [[p_wd.window_object screen] frame]; - - // Check if our window covers up the screen. - if (frameRect.origin.x <= screenRect.origin.x && frameRect.origin.y <= frameRect.origin.y && - frameRect.size.width >= screenRect.size.width && frameRect.size.height >= screenRect.size.height) { - borderless_full = true; - } - } - - if (borderless_full) { - // If the window covers up the screen set the level to above the main menu and hide on deactivate. - [(NSWindow *)p_wd.window_object setLevel:NSMainMenuWindowLevel + 1]; - [(NSWindow *)p_wd.window_object setHidesOnDeactivate:YES]; - } else { - // Reset these when our window is not a borderless window that covers up the screen. - if (p_wd.on_top && !p_wd.fullscreen) { - [(NSWindow *)p_wd.window_object setLevel:NSFloatingWindowLevel]; - } else { - [(NSWindow *)p_wd.window_object setLevel:NSNormalWindowLevel]; - } - [(NSWindow *)p_wd.window_object setHidesOnDeactivate:NO]; - } -} - -void DisplayServerOSX::_set_window_per_pixel_transparency_enabled(bool p_enabled, WindowID p_window) { - ERR_FAIL_COND(!windows.has(p_window)); - WindowData &wd = windows[p_window]; - - if (!OS::get_singleton()->is_layered_allowed()) { - return; - } - if (wd.layered_window != p_enabled) { - if (p_enabled) { - [wd.window_object setBackgroundColor:[NSColor clearColor]]; - [wd.window_object setOpaque:NO]; - [wd.window_object setHasShadow:NO]; - CALayer *layer = [(NSView *)wd.window_view layer]; - if (layer) { - [layer setBackgroundColor:[NSColor clearColor].CGColor]; - [layer setOpaque:NO]; - } -#if defined(GLES3_ENABLED) - if (gl_manager) { - gl_manager->window_set_per_pixel_transparency_enabled(p_window, true); - } -#endif - wd.layered_window = true; - } else { - [wd.window_object setBackgroundColor:[NSColor colorWithCalibratedWhite:1 alpha:1]]; - [wd.window_object setOpaque:YES]; - [wd.window_object setHasShadow:YES]; - CALayer *layer = [(NSView *)wd.window_view layer]; - if (layer) { - [layer setBackgroundColor:[NSColor colorWithCalibratedWhite:1 alpha:1].CGColor]; - [layer setOpaque:YES]; - } -#if defined(GLES3_ENABLED) - if (gl_manager) { - gl_manager->window_set_per_pixel_transparency_enabled(p_window, false); - } -#endif - wd.layered_window = false; - } - NSRect frameRect = [wd.window_object frame]; - [wd.window_object setFrame:NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width + 1, frameRect.size.height) display:YES]; - [wd.window_object setFrame:frameRect display:YES]; - } -} - -void DisplayServerOSX::_update_displays_arrangement() { - origin = Point2i(); - - for (int i = 0; i < get_screen_count(); i++) { - Point2i position = _get_native_screen_position(i); - if (position.x < origin.x) { - origin.x = position.x; - } - if (position.y > origin.y) { - origin.y = position.y; - } - } - displays_arrangement_dirty = false; -} - -Point2i DisplayServerOSX::_get_screens_origin() const { - // Returns the native top-left screen coordinate of the smallest rectangle - // that encompasses all screens. Needed in get_screen_position(), - // window_get_position, and window_set_position() - // to convert between OS X native screen coordinates and the ones expected by Godot. - - if (displays_arrangement_dirty) { - const_cast<DisplayServerOSX *>(this)->_update_displays_arrangement(); - } - - return origin; -} - -Point2i DisplayServerOSX::_get_native_screen_position(int p_screen) const { - NSArray *screenArray = [NSScreen screens]; - if ((NSUInteger)p_screen < [screenArray count]) { - NSRect nsrect = [[screenArray objectAtIndex:p_screen] frame]; - // Return the top-left corner of the screen, for OS X the y starts at the bottom. - return Point2i(nsrect.origin.x, nsrect.origin.y + nsrect.size.height) * screen_get_max_scale(); - } - - return Point2i(); -} - -void DisplayServerOSX::_displays_arrangement_changed(CGDirectDisplayID display_id, CGDisplayChangeSummaryFlags flags, void *user_info) { - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (ds) { - ds->displays_arrangement_dirty = true; - } -} - -void DisplayServerOSX::_dispatch_input_events(const Ref<InputEvent> &p_event) { - ((DisplayServerOSX *)(get_singleton()))->_dispatch_input_event(p_event); -} - -void DisplayServerOSX::_dispatch_input_event(const Ref<InputEvent> &p_event) { - _THREAD_SAFE_METHOD_ - if (!in_dispatch_input_event) { - in_dispatch_input_event = true; - - Variant ev = p_event; - Variant *evp = &ev; - Variant ret; - Callable::CallError ce; - - { - List<WindowID>::Element *E = popup_list.back(); - if (E && Object::cast_to<InputEventKey>(*p_event)) { - // Redirect keyboard input to active popup. - if (windows.has(E->get())) { - Callable callable = windows[E->get()].input_event_callback; - if (callable.is_valid()) { - callable.call((const Variant **)&evp, 1, ret, ce); - } - } - in_dispatch_input_event = false; - return; - } - } - - Ref<InputEventFromWindow> event_from_window = p_event; - if (event_from_window.is_valid() && event_from_window->get_window_id() != INVALID_WINDOW_ID) { - // Send to a window. - if (windows.has(event_from_window->get_window_id())) { - Callable callable = windows[event_from_window->get_window_id()].input_event_callback; - if (callable.is_valid()) { - callable.call((const Variant **)&evp, 1, ret, ce); - } - } - } else { - // Send to all windows. - for (KeyValue<WindowID, WindowData> &E : windows) { - Callable callable = E.value.input_event_callback; - if (callable.is_valid()) { - callable.call((const Variant **)&evp, 1, ret, ce); - } - } - } - in_dispatch_input_event = false; - } -} - -void DisplayServerOSX::_push_input(const Ref<InputEvent> &p_event) { - Ref<InputEvent> ev = p_event; - Input::get_singleton()->parse_input_event(ev); -} - -void DisplayServerOSX::_process_key_events() { - Ref<InputEventKey> k; - for (int i = 0; i < key_event_pos; i++) { - const KeyEvent &ke = key_event_buffer[i]; - if (ke.raw) { - // Non IME input - no composite characters, pass events as is. - k.instantiate(); - - k->set_window_id(ke.window_id); - get_key_modifier_state(ke.osx_state, k); - k->set_pressed(ke.pressed); - k->set_echo(ke.echo); - k->set_keycode(ke.keycode); - k->set_physical_keycode((Key)ke.physical_keycode); - k->set_unicode(ke.unicode); - - _push_input(k); - } else { - // IME input. - if ((i == 0 && ke.keycode == Key::NONE) || (i > 0 && key_event_buffer[i - 1].keycode == Key::NONE)) { - k.instantiate(); - - k->set_window_id(ke.window_id); - get_key_modifier_state(ke.osx_state, k); - k->set_pressed(ke.pressed); - k->set_echo(ke.echo); - k->set_keycode(Key::NONE); - k->set_physical_keycode(Key::NONE); - k->set_unicode(ke.unicode); - - _push_input(k); - } - if (ke.keycode != Key::NONE) { - k.instantiate(); - - k->set_window_id(ke.window_id); - get_key_modifier_state(ke.osx_state, k); - k->set_pressed(ke.pressed); - k->set_echo(ke.echo); - k->set_keycode(ke.keycode); - k->set_physical_keycode((Key)ke.physical_keycode); - - if (i + 1 < key_event_pos && key_event_buffer[i + 1].keycode == Key::NONE) { - k->set_unicode(key_event_buffer[i + 1].unicode); - } - - _push_input(k); - } - } - } - - key_event_pos = 0; -} - -void DisplayServerOSX::_update_keyboard_layouts() { - kbd_layouts.clear(); - current_layout = 0; - - TISInputSourceRef cur_source = TISCopyCurrentKeyboardInputSource(); - NSString *cur_name = (__bridge NSString *)TISGetInputSourceProperty(cur_source, kTISPropertyLocalizedName); - CFRelease(cur_source); - - // Enum IME layouts. - NSDictionary *filter_ime = @{ (NSString *)kTISPropertyInputSourceType : (NSString *)kTISTypeKeyboardInputMode }; - NSArray *list_ime = (__bridge NSArray *)TISCreateInputSourceList((__bridge CFDictionaryRef)filter_ime, false); - for (NSUInteger i = 0; i < [list_ime count]; i++) { - LayoutInfo ly; - NSString *name = (__bridge NSString *)TISGetInputSourceProperty((__bridge TISInputSourceRef)[list_ime objectAtIndex:i], kTISPropertyLocalizedName); - ly.name.parse_utf8([name UTF8String]); - - NSArray *langs = (__bridge NSArray *)TISGetInputSourceProperty((__bridge TISInputSourceRef)[list_ime objectAtIndex:i], kTISPropertyInputSourceLanguages); - ly.code.parse_utf8([(NSString *)[langs objectAtIndex:0] UTF8String]); - kbd_layouts.push_back(ly); - - if ([name isEqualToString:cur_name]) { - current_layout = kbd_layouts.size() - 1; - } - } - - // Enum plain keyboard layouts. - NSDictionary *filter_kbd = @{ (NSString *)kTISPropertyInputSourceType : (NSString *)kTISTypeKeyboardLayout }; - NSArray *list_kbd = (__bridge NSArray *)TISCreateInputSourceList((__bridge CFDictionaryRef)filter_kbd, false); - for (NSUInteger i = 0; i < [list_kbd count]; i++) { - LayoutInfo ly; - NSString *name = (__bridge NSString *)TISGetInputSourceProperty((__bridge TISInputSourceRef)[list_kbd objectAtIndex:i], kTISPropertyLocalizedName); - ly.name.parse_utf8([name UTF8String]); - - NSArray *langs = (__bridge NSArray *)TISGetInputSourceProperty((__bridge TISInputSourceRef)[list_kbd objectAtIndex:i], kTISPropertyInputSourceLanguages); - ly.code.parse_utf8([(NSString *)[langs objectAtIndex:0] UTF8String]); - kbd_layouts.push_back(ly); - - if ([name isEqualToString:cur_name]) { - current_layout = kbd_layouts.size() - 1; - } - } - - keyboard_layout_dirty = false; -} - -void DisplayServerOSX::_keyboard_layout_changed(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef user_info) { - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (ds) { - ds->keyboard_layout_dirty = true; - } -} - -NSImage *DisplayServerOSX::_convert_to_nsimg(Ref<Image> &p_image) const { - p_image->convert(Image::FORMAT_RGBA8); - NSBitmapImageRep *imgrep = [[NSBitmapImageRep alloc] - initWithBitmapDataPlanes:NULL - pixelsWide:p_image->get_width() - pixelsHigh:p_image->get_height() - bitsPerSample:8 - samplesPerPixel:4 - hasAlpha:YES - isPlanar:NO - colorSpaceName:NSDeviceRGBColorSpace - bytesPerRow:int(p_image->get_width()) * 4 - bitsPerPixel:32]; - ERR_FAIL_COND_V(imgrep == nil, nil); - uint8_t *pixels = [imgrep bitmapData]; - - int len = p_image->get_width() * p_image->get_height(); - const uint8_t *r = p_image->get_data().ptr(); - - /* Premultiply the alpha channel */ - for (int i = 0; i < len; i++) { - uint8_t alpha = r[i * 4 + 3]; - pixels[i * 4 + 0] = (uint8_t)(((uint16_t)r[i * 4 + 0] * alpha) / 255); - pixels[i * 4 + 1] = (uint8_t)(((uint16_t)r[i * 4 + 1] * alpha) / 255); - pixels[i * 4 + 2] = (uint8_t)(((uint16_t)r[i * 4 + 2] * alpha) / 255); - pixels[i * 4 + 3] = alpha; - } - - NSImage *nsimg = [[NSImage alloc] initWithSize:NSMakeSize(p_image->get_width(), p_image->get_height())]; - ERR_FAIL_COND_V(nsimg == nil, nil); - [nsimg addRepresentation:imgrep]; - return nsimg; -} - -NSCursor *DisplayServerOSX::_cursor_from_selector(SEL p_selector, SEL p_fallback) { - if ([NSCursor respondsToSelector:p_selector]) { - id object = [NSCursor performSelector:p_selector]; - if ([object isKindOfClass:[NSCursor class]]) { - return object; - } - } - if (p_fallback) { - // Fallback should be a reasonable default, no need to check. - return [NSCursor performSelector:p_fallback]; - } - return [NSCursor arrowCursor]; -} - -NSMenu *DisplayServerOSX::get_dock_menu() const { - return dock_menu; -} - -void DisplayServerOSX::menu_callback(id p_sender) { - if (![p_sender representedObject]) { - return; - } - - GodotMenuItem *value = [p_sender representedObject]; - - if (value) { - if (value->max_states > 0) { - value->state++; - if (value->state >= value->max_states) { - value->state = 0; - } - } - - if (value->checkable_type == CHECKABLE_TYPE_CHECK_BOX) { - if ([p_sender state] == NSControlStateValueOff) { - [p_sender setState:NSControlStateValueOn]; - } else { - [p_sender setState:NSControlStateValueOff]; - } - } - - if (value->callback != Callable()) { - Variant tag = value->meta; - Variant *tagp = &tag; - Variant ret; - Callable::CallError ce; - value->callback.call((const Variant **)&tagp, 1, ret, ce); - } - } -} - -bool DisplayServerOSX::has_window(WindowID p_window) const { - return windows.has(p_window); -} - -DisplayServerOSX::WindowData &DisplayServerOSX::get_window(WindowID p_window) { - return windows[p_window]; -} - -void DisplayServerOSX::send_event(NSEvent *p_event) { - // Special case handling of command-period, which is traditionally a special - // shortcut in macOS and doesn't arrive at our regular keyDown handler. - if ([p_event type] == NSEventTypeKeyDown) { - if (([p_event modifierFlags] & NSEventModifierFlagCommand) && [p_event keyCode] == 0x2f) { - Ref<InputEventKey> k; - k.instantiate(); - - get_key_modifier_state([p_event modifierFlags], k); - k->set_window_id(DisplayServerOSX::INVALID_WINDOW_ID); - k->set_pressed(true); - k->set_keycode(Key::PERIOD); - k->set_physical_keycode(Key::PERIOD); - k->set_echo([p_event isARepeat]); - - Input::get_singleton()->parse_input_event(k); - } - } -} - -void DisplayServerOSX::send_window_event(const WindowData &wd, WindowEvent p_event) { - _THREAD_SAFE_METHOD_ - - if (!wd.event_callback.is_null()) { - Variant event = int(p_event); - Variant *eventp = &event; - Variant ret; - Callable::CallError ce; - wd.event_callback.call((const Variant **)&eventp, 1, ret, ce); - } -} - -void DisplayServerOSX::release_pressed_events() { - _THREAD_SAFE_METHOD_ - if (Input::get_singleton()) { - Input::get_singleton()->release_pressed_events(); - } -} - -void DisplayServerOSX::get_key_modifier_state(unsigned int p_osx_state, Ref<InputEventWithModifiers> r_state) const { - r_state->set_shift_pressed((p_osx_state & NSEventModifierFlagShift)); - r_state->set_ctrl_pressed((p_osx_state & NSEventModifierFlagControl)); - r_state->set_alt_pressed((p_osx_state & NSEventModifierFlagOption)); - r_state->set_meta_pressed((p_osx_state & NSEventModifierFlagCommand)); -} - -void DisplayServerOSX::update_mouse_pos(DisplayServerOSX::WindowData &p_wd, NSPoint p_location_in_window) { - const NSRect content_rect = [p_wd.window_view frame]; - const float scale = screen_get_max_scale(); - p_wd.mouse_pos.x = p_location_in_window.x * scale; - p_wd.mouse_pos.y = (content_rect.size.height - p_location_in_window.y) * scale; - Input::get_singleton()->set_mouse_position(p_wd.mouse_pos); -} - -void DisplayServerOSX::push_to_key_event_buffer(const DisplayServerOSX::KeyEvent &p_event) { - if (key_event_pos >= key_event_buffer.size()) { - key_event_buffer.resize(1 + key_event_pos); - } - key_event_buffer.write[key_event_pos++] = p_event; -} - -void DisplayServerOSX::update_im_text(const Point2i &p_selection, const String &p_text) { - im_selection = p_selection; - im_text = p_text; - - OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE); -} - -void DisplayServerOSX::set_last_focused_window(WindowID p_window) { - last_focused_window = p_window; -} - -void DisplayServerOSX::set_is_resizing(bool p_is_resizing) { - is_resizing = p_is_resizing; -} - -bool DisplayServerOSX::get_is_resizing() const { - return is_resizing; -} - -void DisplayServerOSX::window_update(WindowID p_window) { -#if defined(GLES3_ENABLED) - if (gl_manager) { - gl_manager->window_update(p_window); - } -#endif -} - -void DisplayServerOSX::window_destroy(WindowID p_window) { -#if defined(GLES3_ENABLED) - if (gl_manager) { - gl_manager->window_destroy(p_window); - } -#endif -#ifdef VULKAN_ENABLED - if (context_vulkan) { - context_vulkan->window_destroy(p_window); - } -#endif - windows.erase(p_window); -} - -void DisplayServerOSX::window_resize(WindowID p_window, int p_width, int p_height) { -#if defined(GLES3_ENABLED) - if (gl_manager) { - gl_manager->window_resize(p_window, p_width, p_height); - } -#endif -#if defined(VULKAN_ENABLED) - if (context_vulkan) { - context_vulkan->window_resize(p_window, p_width, p_height); - } -#endif -} - -bool DisplayServerOSX::has_feature(Feature p_feature) const { - switch (p_feature) { - case FEATURE_GLOBAL_MENU: - case FEATURE_SUBWINDOWS: - //case FEATURE_TOUCHSCREEN: - case FEATURE_MOUSE: - case FEATURE_MOUSE_WARP: - case FEATURE_CLIPBOARD: - case FEATURE_CURSOR_SHAPE: - case FEATURE_CUSTOM_CURSOR_SHAPE: - case FEATURE_NATIVE_DIALOG: - case FEATURE_IME: - case FEATURE_WINDOW_TRANSPARENCY: - case FEATURE_HIDPI: - case FEATURE_ICON: - case FEATURE_NATIVE_ICON: - //case FEATURE_KEEP_SCREEN_ON: - case FEATURE_SWAP_BUFFERS: - case FEATURE_TEXT_TO_SPEECH: - return true; - default: { - } - } - return false; -} - -String DisplayServerOSX::get_name() const { - return "OSX"; -} - -void DisplayServerOSX::global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - String keycode = KeyMappingOSX::keycode_get_native_string(p_accel & KeyModifierMask::CODE_MASK); - NSMenuItem *menu_item; - if (p_index != -1) { - menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()] atIndex:p_index]; - } else { - menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]]; - } - GodotMenuItem *obj = [[GodotMenuItem alloc] init]; - obj->callback = p_callback; - obj->meta = p_tag; - obj->checkable_type = CHECKABLE_TYPE_NONE; - obj->max_states = 0; - obj->state = 0; - [menu_item setKeyEquivalentModifierMask:KeyMappingOSX::keycode_get_native_mask(p_accel)]; - [menu_item setRepresentedObject:obj]; - } -} - -void DisplayServerOSX::global_menu_add_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - String keycode = KeyMappingOSX::keycode_get_native_string(p_accel & KeyModifierMask::CODE_MASK); - NSMenuItem *menu_item; - if (p_index != -1) { - menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()] atIndex:p_index]; - } else { - menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]]; - } - GodotMenuItem *obj = [[GodotMenuItem alloc] init]; - obj->callback = p_callback; - obj->meta = p_tag; - obj->checkable_type = CHECKABLE_TYPE_CHECK_BOX; - obj->max_states = 0; - obj->state = 0; - [menu_item setKeyEquivalentModifierMask:KeyMappingOSX::keycode_get_native_mask(p_accel)]; - [menu_item setRepresentedObject:obj]; - } -} - -void DisplayServerOSX::global_menu_add_icon_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - String keycode = KeyMappingOSX::keycode_get_native_string(p_accel & KeyModifierMask::CODE_MASK); - NSMenuItem *menu_item; - if (p_index != -1) { - menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()] atIndex:p_index]; - } else { - menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]]; - } - GodotMenuItem *obj = [[GodotMenuItem alloc] init]; - obj->callback = p_callback; - obj->meta = p_tag; - obj->checkable_type = CHECKABLE_TYPE_NONE; - obj->max_states = 0; - obj->state = 0; - if (p_icon.is_valid()) { - obj->img = p_icon->get_image(); - obj->img = obj->img->duplicate(); - if (obj->img->is_compressed()) { - obj->img->decompress(); - } - obj->img->resize(16, 16, Image::INTERPOLATE_LANCZOS); - [menu_item setImage:_convert_to_nsimg(obj->img)]; - } - [menu_item setKeyEquivalentModifierMask:KeyMappingOSX::keycode_get_native_mask(p_accel)]; - [menu_item setRepresentedObject:obj]; - } -} - -void DisplayServerOSX::global_menu_add_icon_check_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - String keycode = KeyMappingOSX::keycode_get_native_string(p_accel & KeyModifierMask::CODE_MASK); - NSMenuItem *menu_item; - if (p_index != -1) { - menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()] atIndex:p_index]; - } else { - menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]]; - } - GodotMenuItem *obj = [[GodotMenuItem alloc] init]; - obj->callback = p_callback; - obj->meta = p_tag; - obj->checkable_type = CHECKABLE_TYPE_CHECK_BOX; - obj->max_states = 0; - obj->state = 0; - if (p_icon.is_valid()) { - obj->img = p_icon->get_image(); - obj->img = obj->img->duplicate(); - if (obj->img->is_compressed()) { - obj->img->decompress(); - } - obj->img->resize(16, 16, Image::INTERPOLATE_LANCZOS); - [menu_item setImage:_convert_to_nsimg(obj->img)]; - } - [menu_item setKeyEquivalentModifierMask:KeyMappingOSX::keycode_get_native_mask(p_accel)]; - [menu_item setRepresentedObject:obj]; - } -} - -void DisplayServerOSX::global_menu_add_radio_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - String keycode = KeyMappingOSX::keycode_get_native_string(p_accel & KeyModifierMask::CODE_MASK); - NSMenuItem *menu_item; - if (p_index != -1) { - menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()] atIndex:p_index]; - } else { - menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]]; - } - GodotMenuItem *obj = [[GodotMenuItem alloc] init]; - obj->callback = p_callback; - obj->meta = p_tag; - obj->checkable_type = CHECKABLE_TYPE_RADIO_BUTTON; - obj->max_states = 0; - obj->state = 0; - [menu_item setKeyEquivalentModifierMask:KeyMappingOSX::keycode_get_native_mask(p_accel)]; - [menu_item setRepresentedObject:obj]; - } -} - -void DisplayServerOSX::global_menu_add_icon_radio_check_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - String keycode = KeyMappingOSX::keycode_get_native_string(p_accel & KeyModifierMask::CODE_MASK); - NSMenuItem *menu_item; - if (p_index != -1) { - menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()] atIndex:p_index]; - } else { - menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]]; - } - GodotMenuItem *obj = [[GodotMenuItem alloc] init]; - obj->callback = p_callback; - obj->meta = p_tag; - obj->checkable_type = CHECKABLE_TYPE_RADIO_BUTTON; - obj->max_states = 0; - obj->state = 0; - if (p_icon.is_valid()) { - obj->img = p_icon->get_image(); - obj->img = obj->img->duplicate(); - if (obj->img->is_compressed()) { - obj->img->decompress(); - } - obj->img->resize(16, 16, Image::INTERPOLATE_LANCZOS); - [menu_item setImage:_convert_to_nsimg(obj->img)]; - } - [menu_item setKeyEquivalentModifierMask:KeyMappingOSX::keycode_get_native_mask(p_accel)]; - [menu_item setRepresentedObject:obj]; - } -} - -void DisplayServerOSX::global_menu_add_multistate_item(const String &p_menu_root, const String &p_label, int p_max_states, int p_default_state, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - String keycode = KeyMappingOSX::keycode_get_native_string(p_accel & KeyModifierMask::CODE_MASK); - NSMenuItem *menu_item; - if (p_index != -1) { - menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()] atIndex:p_index]; - } else { - menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]]; - } - GodotMenuItem *obj = [[GodotMenuItem alloc] init]; - obj->callback = p_callback; - obj->meta = p_tag; - obj->checkable_type = CHECKABLE_TYPE_NONE; - obj->max_states = p_max_states; - obj->state = p_default_state; - [menu_item setKeyEquivalentModifierMask:KeyMappingOSX::keycode_get_native_mask(p_accel)]; - [menu_item setRepresentedObject:obj]; - } -} - -void DisplayServerOSX::global_menu_add_submenu_item(const String &p_menu_root, const String &p_label, const String &p_submenu, int p_index) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - NSMenu *sub_menu = _get_menu_root(p_submenu); - if (menu && sub_menu) { - if (sub_menu == menu) { - ERR_PRINT("Can't set submenu to self!"); - return; - } - if ([sub_menu supermenu]) { - ERR_PRINT("Can't set submenu to menu that is already a submenu of some other menu!"); - return; - } - NSMenuItem *menu_item; - if (p_index != -1) { - menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:nil keyEquivalent:@"" atIndex:p_index]; - } else { - menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:nil keyEquivalent:@""]; - } - [sub_menu setTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()]]; - [menu setSubmenu:sub_menu forItem:menu_item]; - } -} - -void DisplayServerOSX::global_menu_add_separator(const String &p_menu_root, int p_index) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - if (p_index != -1) { - [menu insertItem:[NSMenuItem separatorItem] atIndex:p_index]; - } else { - [menu addItem:[NSMenuItem separatorItem]]; - } - } -} - -int DisplayServerOSX::global_menu_get_item_index_from_text(const String &p_menu_root, const String &p_text) const { - _THREAD_SAFE_METHOD_ - - const NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - return [menu indexOfItemWithTitle:[NSString stringWithUTF8String:p_text.utf8().get_data()]]; - } - - return -1; -} - -int DisplayServerOSX::global_menu_get_item_index_from_tag(const String &p_menu_root, const Variant &p_tag) const { - _THREAD_SAFE_METHOD_ - - const NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - for (NSInteger i = 0; i < [menu numberOfItems]; i++) { - const NSMenuItem *menu_item = [menu itemAtIndex:i]; - if (menu_item) { - const GodotMenuItem *obj = [menu_item representedObject]; - if (obj && obj->meta == p_tag) { - return i; - } - } - } - } - - return -1; -} - -bool DisplayServerOSX::global_menu_is_item_checked(const String &p_menu_root, int p_idx) const { - _THREAD_SAFE_METHOD_ - - const NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - return ([menu_item state] == NSControlStateValueOn); - } - } - return false; -} - -bool DisplayServerOSX::global_menu_is_item_checkable(const String &p_menu_root, int p_idx) const { - _THREAD_SAFE_METHOD_ - - const NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - GodotMenuItem *obj = [menu_item representedObject]; - if (obj) { - return obj->checkable_type == CHECKABLE_TYPE_CHECK_BOX; - } - } - } - return false; -} - -bool DisplayServerOSX::global_menu_is_item_radio_checkable(const String &p_menu_root, int p_idx) const { - _THREAD_SAFE_METHOD_ - - const NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - GodotMenuItem *obj = [menu_item representedObject]; - if (obj) { - return obj->checkable_type == CHECKABLE_TYPE_RADIO_BUTTON; - } - } - } - return false; -} - -Callable DisplayServerOSX::global_menu_get_item_callback(const String &p_menu_root, int p_idx) const { - _THREAD_SAFE_METHOD_ - - const NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - GodotMenuItem *obj = [menu_item representedObject]; - if (obj) { - return obj->callback; - } - } - } - return Callable(); -} - -Variant DisplayServerOSX::global_menu_get_item_tag(const String &p_menu_root, int p_idx) const { - _THREAD_SAFE_METHOD_ - - const NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - GodotMenuItem *obj = [menu_item representedObject]; - if (obj) { - return obj->meta; - } - } - } - return Variant(); -} - -String DisplayServerOSX::global_menu_get_item_text(const String &p_menu_root, int p_idx) const { - _THREAD_SAFE_METHOD_ - - const NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - return String::utf8([[menu_item title] UTF8String]); - } - } - return String(); -} - -String DisplayServerOSX::global_menu_get_item_submenu(const String &p_menu_root, int p_idx) const { - _THREAD_SAFE_METHOD_ - - const NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - const NSMenu *sub_menu = [menu_item submenu]; - if (sub_menu) { - for (const KeyValue<String, NSMenu *> &E : submenu) { - if (E.value == sub_menu) { - return E.key; - } - } - } - } - } - return String(); -} - -Key DisplayServerOSX::global_menu_get_item_accelerator(const String &p_menu_root, int p_idx) const { - _THREAD_SAFE_METHOD_ - - const NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - String ret = String::utf8([[menu_item keyEquivalent] UTF8String]); - Key keycode = find_keycode(ret); - NSUInteger mask = [menu_item keyEquivalentModifierMask]; - if (mask & NSEventModifierFlagControl) { - keycode |= KeyModifierMask::CTRL; - } - if (mask & NSEventModifierFlagOption) { - keycode |= KeyModifierMask::ALT; - } - if (mask & NSEventModifierFlagShift) { - keycode |= KeyModifierMask::SHIFT; - } - if (mask & NSEventModifierFlagCommand) { - keycode |= KeyModifierMask::META; - } - if (mask & NSEventModifierFlagNumericPad) { - keycode |= KeyModifierMask::KPAD; - } - return keycode; - } - } - return Key::NONE; -} - -bool DisplayServerOSX::global_menu_is_item_disabled(const String &p_menu_root, int p_idx) const { - _THREAD_SAFE_METHOD_ - - const NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - return ![menu_item isEnabled]; - } - } - return false; -} - -String DisplayServerOSX::global_menu_get_item_tooltip(const String &p_menu_root, int p_idx) const { - _THREAD_SAFE_METHOD_ - - const NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - return String::utf8([[menu_item toolTip] UTF8String]); - } - } - return String(); -} - -int DisplayServerOSX::global_menu_get_item_state(const String &p_menu_root, int p_idx) const { - _THREAD_SAFE_METHOD_ - - const NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - GodotMenuItem *obj = [menu_item representedObject]; - if (obj) { - return obj->state; - } - } - } - return 0; -} - -int DisplayServerOSX::global_menu_get_item_max_states(const String &p_menu_root, int p_idx) const { - _THREAD_SAFE_METHOD_ - - const NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - GodotMenuItem *obj = [menu_item representedObject]; - if (obj) { - return obj->max_states; - } - } - } - return 0; -} - -Ref<Texture2D> DisplayServerOSX::global_menu_get_item_icon(const String &p_menu_root, int p_idx) const { - _THREAD_SAFE_METHOD_ - - const NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - const NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - GodotMenuItem *obj = [menu_item representedObject]; - if (obj) { - if (obj->img.is_valid()) { - return ImageTexture::create_from_image(obj->img); - } - } - } - } - return Ref<Texture2D>(); -} - -void DisplayServerOSX::global_menu_set_item_checked(const String &p_menu_root, int p_idx, bool p_checked) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu. - return; - } - NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - if (p_checked) { - [menu_item setState:NSControlStateValueOn]; - } else { - [menu_item setState:NSControlStateValueOff]; - } - } - } -} - -void DisplayServerOSX::global_menu_set_item_checkable(const String &p_menu_root, int p_idx, bool p_checkable) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu. - return; - } - NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - GodotMenuItem *obj = [menu_item representedObject]; - obj->checkable_type = (p_checkable) ? CHECKABLE_TYPE_CHECK_BOX : CHECKABLE_TYPE_NONE; - } - } -} - -void DisplayServerOSX::global_menu_set_item_radio_checkable(const String &p_menu_root, int p_idx, bool p_checkable) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu. - return; - } - NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - GodotMenuItem *obj = [menu_item representedObject]; - obj->checkable_type = (p_checkable) ? CHECKABLE_TYPE_RADIO_BUTTON : CHECKABLE_TYPE_NONE; - } - } -} - -void DisplayServerOSX::global_menu_set_item_callback(const String &p_menu_root, int p_idx, const Callable &p_callback) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu. - return; - } - NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - GodotMenuItem *obj = [menu_item representedObject]; - obj->callback = p_callback; - } - } -} - -void DisplayServerOSX::global_menu_set_item_tag(const String &p_menu_root, int p_idx, const Variant &p_tag) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu. - return; - } - NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - GodotMenuItem *obj = [menu_item representedObject]; - obj->meta = p_tag; - } - } -} - -void DisplayServerOSX::global_menu_set_item_text(const String &p_menu_root, int p_idx, const String &p_text) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu. - return; - } - NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - [menu_item setTitle:[NSString stringWithUTF8String:p_text.utf8().get_data()]]; - } - } -} - -void DisplayServerOSX::global_menu_set_item_submenu(const String &p_menu_root, int p_idx, const String &p_submenu) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - NSMenu *sub_menu = _get_menu_root(p_submenu); - if (menu && sub_menu) { - if (sub_menu == menu) { - ERR_PRINT("Can't set submenu to self!"); - return; - } - if ([sub_menu supermenu]) { - ERR_PRINT("Can't set submenu to menu that is already a submenu of some other menu!"); - return; - } - if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu. - return; - } - NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - [menu setSubmenu:sub_menu forItem:menu_item]; - } - } -} - -void DisplayServerOSX::global_menu_set_item_accelerator(const String &p_menu_root, int p_idx, Key p_keycode) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu. - return; - } - NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - [menu_item setKeyEquivalentModifierMask:KeyMappingOSX::keycode_get_native_mask(p_keycode)]; - String keycode = KeyMappingOSX::keycode_get_native_string(p_keycode & KeyModifierMask::CODE_MASK); - [menu_item setKeyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]]; - } - } -} - -void DisplayServerOSX::global_menu_set_item_disabled(const String &p_menu_root, int p_idx, bool p_disabled) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu. - return; - } - NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - [menu_item setEnabled:(!p_disabled)]; - } - } -} - -void DisplayServerOSX::global_menu_set_item_tooltip(const String &p_menu_root, int p_idx, const String &p_tooltip) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu. - return; - } - NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - [menu_item setToolTip:[NSString stringWithUTF8String:p_tooltip.utf8().get_data()]]; - } - } -} - -void DisplayServerOSX::global_menu_set_item_state(const String &p_menu_root, int p_idx, int p_state) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu. - return; - } - NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - GodotMenuItem *obj = [menu_item representedObject]; - if (obj) { - obj->state = p_state; - } - } - } -} - -void DisplayServerOSX::global_menu_set_item_max_states(const String &p_menu_root, int p_idx, int p_max_states) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu. - return; - } - NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - GodotMenuItem *obj = [menu_item representedObject]; - if (obj) { - obj->max_states = p_max_states; - } - } - } -} - -void DisplayServerOSX::global_menu_set_item_icon(const String &p_menu_root, int p_idx, const Ref<Texture2D> &p_icon) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu. - return; - } - NSMenuItem *menu_item = [menu itemAtIndex:p_idx]; - if (menu_item) { - GodotMenuItem *obj = [menu_item representedObject]; - if (p_icon.is_valid()) { - obj->img = p_icon->get_image(); - obj->img = obj->img->duplicate(); - if (obj->img->is_compressed()) { - obj->img->decompress(); - } - obj->img->resize(16, 16, Image::INTERPOLATE_LANCZOS); - [menu_item setImage:_convert_to_nsimg(obj->img)]; - } else { - obj->img = Ref<Image>(); - [menu_item setImage:nil]; - } - } - } -} - -int DisplayServerOSX::global_menu_get_item_count(const String &p_menu_root) const { - _THREAD_SAFE_METHOD_ - - const NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - return [menu numberOfItems]; - } else { - return 0; - } -} - -void DisplayServerOSX::global_menu_remove_item(const String &p_menu_root, int p_idx) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not delete Apple menu. - return; - } - [menu removeItemAtIndex:p_idx]; - } -} - -void DisplayServerOSX::global_menu_clear(const String &p_menu_root) { - _THREAD_SAFE_METHOD_ - - NSMenu *menu = _get_menu_root(p_menu_root); - if (menu) { - [menu removeAllItems]; - // Restore Apple menu. - if (menu == [NSApp mainMenu]) { - NSMenuItem *menu_item = [menu addItemWithTitle:@"" action:nil keyEquivalent:@""]; - [menu setSubmenu:apple_menu forItem:menu_item]; - } - } -} - -bool DisplayServerOSX::tts_is_speaking() const { - ERR_FAIL_COND_V(!tts, false); - return [tts isSpeaking]; -} - -bool DisplayServerOSX::tts_is_paused() const { - ERR_FAIL_COND_V(!tts, false); - return [tts isPaused]; -} - -Array DisplayServerOSX::tts_get_voices() const { - ERR_FAIL_COND_V(!tts, Array()); - return [tts getVoices]; -} - -void DisplayServerOSX::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) { - ERR_FAIL_COND(!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 DisplayServerOSX::tts_pause() { - ERR_FAIL_COND(!tts); - [tts pauseSpeaking]; -} - -void DisplayServerOSX::tts_resume() { - ERR_FAIL_COND(!tts); - [tts resumeSpeaking]; -} - -void DisplayServerOSX::tts_stop() { - ERR_FAIL_COND(!tts); - [tts stopSpeaking]; -} - -Error DisplayServerOSX::dialog_show(String p_title, String p_description, Vector<String> p_buttons, const Callable &p_callback) { - _THREAD_SAFE_METHOD_ - - NSAlert *window = [[NSAlert alloc] init]; - NSString *ns_title = [NSString stringWithUTF8String:p_title.utf8().get_data()]; - NSString *ns_description = [NSString stringWithUTF8String:p_description.utf8().get_data()]; - - for (int i = 0; i < p_buttons.size(); i++) { - NSString *ns_button = [NSString stringWithUTF8String:p_buttons[i].utf8().get_data()]; - [window addButtonWithTitle:ns_button]; - } - [window setMessageText:ns_title]; - [window setInformativeText:ns_description]; - [window setAlertStyle:NSAlertStyleInformational]; - - int button_pressed; - NSInteger ret = [window runModal]; - if (ret == NSAlertFirstButtonReturn) { - button_pressed = 0; - } else if (ret == NSAlertSecondButtonReturn) { - button_pressed = 1; - } else if (ret == NSAlertThirdButtonReturn) { - button_pressed = 2; - } else { - button_pressed = 2 + (ret - NSAlertThirdButtonReturn); - } - - if (!p_callback.is_null()) { - Variant button = button_pressed; - Variant *buttonp = &button; - Variant ret; - Callable::CallError ce; - p_callback.call((const Variant **)&buttonp, 1, ret, ce); - } - - return OK; -} - -Error DisplayServerOSX::dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback) { - _THREAD_SAFE_METHOD_ - - NSAlert *window = [[NSAlert alloc] init]; - NSString *ns_title = [NSString stringWithUTF8String:p_title.utf8().get_data()]; - NSString *ns_description = [NSString stringWithUTF8String:p_description.utf8().get_data()]; - NSTextField *input = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 250, 30)]; - - [window addButtonWithTitle:@"OK"]; - [window setMessageText:ns_title]; - [window setInformativeText:ns_description]; - [window setAlertStyle:NSAlertStyleInformational]; - - [input setStringValue:[NSString stringWithUTF8String:p_partial.utf8().get_data()]]; - [window setAccessoryView:input]; - - [window runModal]; - - String ret; - ret.parse_utf8([[input stringValue] UTF8String]); - - if (!p_callback.is_null()) { - Variant text = ret; - Variant *textp = &text; - Variant ret; - Callable::CallError ce; - p_callback.call((const Variant **)&textp, 1, ret, ce); - } - - return OK; -} - -void DisplayServerOSX::mouse_set_mode(MouseMode p_mode) { - _THREAD_SAFE_METHOD_ - - if (p_mode == mouse_mode) { - return; - } - - WindowID window_id = windows.has(last_focused_window) ? last_focused_window : MAIN_WINDOW_ID; - WindowData &wd = windows[window_id]; - if (p_mode == MOUSE_MODE_CAPTURED) { - // Apple Docs state that the display parameter is not used. - // "This parameter is not used. By default, you may pass kCGDirectMainDisplay." - // https://developer.apple.com/library/mac/documentation/graphicsimaging/reference/Quartz_Services_Ref/Reference/reference.html - if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) { - CGDisplayHideCursor(kCGDirectMainDisplay); - } - CGAssociateMouseAndMouseCursorPosition(false); - [wd.window_object setMovable:NO]; - const NSRect contentRect = [wd.window_view frame]; - NSRect pointInWindowRect = NSMakeRect(contentRect.size.width / 2, contentRect.size.height / 2, 0, 0); - NSPoint pointOnScreen = [[wd.window_view window] convertRectToScreen:pointInWindowRect].origin; - CGPoint lMouseWarpPos = { pointOnScreen.x, CGDisplayBounds(CGMainDisplayID()).size.height - pointOnScreen.y }; - CGWarpMouseCursorPosition(lMouseWarpPos); - } else if (p_mode == MOUSE_MODE_HIDDEN) { - if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) { - CGDisplayHideCursor(kCGDirectMainDisplay); - } - [wd.window_object setMovable:YES]; - CGAssociateMouseAndMouseCursorPosition(true); - } else if (p_mode == MOUSE_MODE_CONFINED) { - CGDisplayShowCursor(kCGDirectMainDisplay); - [wd.window_object setMovable:NO]; - CGAssociateMouseAndMouseCursorPosition(false); - } else if (p_mode == MOUSE_MODE_CONFINED_HIDDEN) { - if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) { - CGDisplayHideCursor(kCGDirectMainDisplay); - } - [wd.window_object setMovable:NO]; - CGAssociateMouseAndMouseCursorPosition(false); - } else { // MOUSE_MODE_VISIBLE - CGDisplayShowCursor(kCGDirectMainDisplay); - [wd.window_object setMovable:YES]; - CGAssociateMouseAndMouseCursorPosition(true); - } - - last_warp = [[NSProcessInfo processInfo] systemUptime]; - ignore_warp = true; - warp_events.clear(); - mouse_mode = p_mode; - - if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) { - cursor_update_shape(); - } -} - -DisplayServer::MouseMode DisplayServerOSX::mouse_get_mode() const { - return mouse_mode; -} - -bool DisplayServerOSX::update_mouse_wrap(WindowData &p_wd, NSPoint &r_delta, NSPoint &r_mpos, NSTimeInterval p_timestamp) { - _THREAD_SAFE_METHOD_ - - if (ignore_warp) { - // Discard late events, before warp. - if (p_timestamp < last_warp) { - return true; - } - ignore_warp = false; - return true; - } - - if (mouse_mode == DisplayServer::MOUSE_MODE_CONFINED || mouse_mode == DisplayServer::MOUSE_MODE_CONFINED_HIDDEN) { - // Discard late events. - if (p_timestamp < last_warp) { - return true; - } - - // Warp affects next event delta, subtract previous warp deltas. - List<WarpEvent>::Element *F = warp_events.front(); - while (F) { - if (F->get().timestamp < p_timestamp) { - List<DisplayServerOSX::WarpEvent>::Element *E = F; - r_delta.x -= E->get().delta.x; - r_delta.y -= E->get().delta.y; - F = F->next(); - warp_events.erase(E); - } else { - F = F->next(); - } - } - - // Confine mouse position to the window, and update delta. - NSRect frame = [p_wd.window_object frame]; - NSPoint conf_pos = r_mpos; - conf_pos.x = CLAMP(conf_pos.x + r_delta.x, 0.f, frame.size.width); - conf_pos.y = CLAMP(conf_pos.y - r_delta.y, 0.f, frame.size.height); - r_delta.x = conf_pos.x - r_mpos.x; - r_delta.y = r_mpos.y - conf_pos.y; - r_mpos = conf_pos; - - // Move mouse cursor. - NSRect point_in_window_rect = NSMakeRect(conf_pos.x, conf_pos.y, 0, 0); - conf_pos = [[p_wd.window_view window] convertRectToScreen:point_in_window_rect].origin; - conf_pos.y = CGDisplayBounds(CGMainDisplayID()).size.height - conf_pos.y; - CGWarpMouseCursorPosition(conf_pos); - - // Save warp data. - last_warp = [[NSProcessInfo processInfo] systemUptime]; - - DisplayServerOSX::WarpEvent ev; - ev.timestamp = last_warp; - ev.delta = r_delta; - warp_events.push_back(ev); - } - - return false; -} - -void DisplayServerOSX::warp_mouse(const Point2i &p_position) { - _THREAD_SAFE_METHOD_ - - if (mouse_mode != MOUSE_MODE_CAPTURED) { - WindowID window_id = windows.has(last_focused_window) ? last_focused_window : MAIN_WINDOW_ID; - WindowData &wd = windows[window_id]; - - // Local point in window coords. - const NSRect contentRect = [wd.window_view frame]; - const float scale = screen_get_max_scale(); - NSRect pointInWindowRect = NSMakeRect(p_position.x / scale, contentRect.size.height - (p_position.y / scale - 1), 0, 0); - NSPoint pointOnScreen = [[wd.window_view window] convertRectToScreen:pointInWindowRect].origin; - - // Point in scren coords. - CGPoint lMouseWarpPos = { pointOnScreen.x, CGDisplayBounds(CGMainDisplayID()).size.height - pointOnScreen.y }; - - // Do the warping. - CGEventSourceRef lEventRef = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState); - CGEventSourceSetLocalEventsSuppressionInterval(lEventRef, 0.0); - CGAssociateMouseAndMouseCursorPosition(false); - CGWarpMouseCursorPosition(lMouseWarpPos); - if (mouse_mode != MOUSE_MODE_CONFINED && mouse_mode != MOUSE_MODE_CONFINED_HIDDEN) { - CGAssociateMouseAndMouseCursorPosition(true); - } - } -} - -Point2i DisplayServerOSX::mouse_get_position() const { - _THREAD_SAFE_METHOD_ - - const NSPoint mouse_pos = [NSEvent mouseLocation]; - const float scale = screen_get_max_scale(); - - for (NSScreen *screen in [NSScreen screens]) { - NSRect frame = [screen frame]; - if (NSMouseInRect(mouse_pos, frame, NO)) { - Vector2i pos = Vector2i((int)mouse_pos.x, (int)mouse_pos.y); - pos *= scale; - pos -= _get_screens_origin(); - pos.y *= -1; - return pos; - } - } - return Vector2i(); -} - -void DisplayServerOSX::mouse_set_button_state(MouseButton p_state) { - last_button_state = p_state; -} - -MouseButton DisplayServerOSX::mouse_get_button_state() const { - return last_button_state; -} - -void DisplayServerOSX::clipboard_set(const String &p_text) { - _THREAD_SAFE_METHOD_ - - NSString *copiedString = [NSString stringWithUTF8String:p_text.utf8().get_data()]; - NSArray *copiedStringArray = [NSArray arrayWithObject:copiedString]; - - NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; - [pasteboard clearContents]; - [pasteboard writeObjects:copiedStringArray]; -} - -String DisplayServerOSX::clipboard_get() const { - _THREAD_SAFE_METHOD_ - - NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; - NSArray *classArray = [NSArray arrayWithObject:[NSString class]]; - NSDictionary *options = [NSDictionary dictionary]; - - BOOL ok = [pasteboard canReadObjectForClasses:classArray options:options]; - - if (!ok) { - return ""; - } - - NSArray *objectsToPaste = [pasteboard readObjectsForClasses:classArray options:options]; - NSString *string = [objectsToPaste objectAtIndex:0]; - - String ret; - ret.parse_utf8([string UTF8String]); - return ret; -} - -int DisplayServerOSX::get_screen_count() const { - _THREAD_SAFE_METHOD_ - - NSArray *screenArray = [NSScreen screens]; - return [screenArray count]; -} - -Point2i DisplayServerOSX::screen_get_position(int p_screen) const { - _THREAD_SAFE_METHOD_ - - if (p_screen == SCREEN_OF_MAIN_WINDOW) { - p_screen = window_get_current_screen(); - } - - Point2i position = _get_native_screen_position(p_screen) - _get_screens_origin(); - // OS X native y-coordinate relative to _get_screens_origin() is negative, - // Godot expects a positive value. - position.y *= -1; - return position; -} - -Size2i DisplayServerOSX::screen_get_size(int p_screen) const { - _THREAD_SAFE_METHOD_ - - if (p_screen == SCREEN_OF_MAIN_WINDOW) { - p_screen = window_get_current_screen(); - } - - NSArray *screenArray = [NSScreen screens]; - if ((NSUInteger)p_screen < [screenArray count]) { - // Note: Use frame to get the whole screen size. - NSRect nsrect = [[screenArray objectAtIndex:p_screen] frame]; - return Size2i(nsrect.size.width, nsrect.size.height) * screen_get_max_scale(); - } - - return Size2i(); -} - -int DisplayServerOSX::screen_get_dpi(int p_screen) const { - _THREAD_SAFE_METHOD_ - - if (p_screen == SCREEN_OF_MAIN_WINDOW) { - p_screen = window_get_current_screen(); - } - - NSArray *screenArray = [NSScreen screens]; - if ((NSUInteger)p_screen < [screenArray count]) { - NSDictionary *description = [[screenArray objectAtIndex:p_screen] deviceDescription]; - - const NSSize displayPixelSize = [[description objectForKey:NSDeviceSize] sizeValue]; - const CGSize displayPhysicalSize = CGDisplayScreenSize([[description objectForKey:@"NSScreenNumber"] unsignedIntValue]); - float scale = [[screenArray objectAtIndex:p_screen] backingScaleFactor]; - - float den2 = (displayPhysicalSize.width / 25.4f) * (displayPhysicalSize.width / 25.4f) + (displayPhysicalSize.height / 25.4f) * (displayPhysicalSize.height / 25.4f); - if (den2 > 0.0f) { - return ceil(sqrt(displayPixelSize.width * displayPixelSize.width + displayPixelSize.height * displayPixelSize.height) / sqrt(den2) * scale); - } - } - - return 72; -} - -float DisplayServerOSX::screen_get_scale(int p_screen) const { - _THREAD_SAFE_METHOD_ - - if (p_screen == SCREEN_OF_MAIN_WINDOW) { - p_screen = window_get_current_screen(); - } - if (OS::get_singleton()->is_hidpi_allowed()) { - NSArray *screenArray = [NSScreen screens]; - if ((NSUInteger)p_screen < [screenArray count]) { - if ([[screenArray objectAtIndex:p_screen] respondsToSelector:@selector(backingScaleFactor)]) { - return fmax(1.0, [[screenArray objectAtIndex:p_screen] backingScaleFactor]); - } - } - } - - return 1.f; -} - -float DisplayServerOSX::screen_get_max_scale() const { - _THREAD_SAFE_METHOD_ - - // Note: Do not update max display scale on screen configuration change, existing editor windows can't be rescaled on the fly. - return display_max_scale; -} - -Rect2i DisplayServerOSX::screen_get_usable_rect(int p_screen) const { - _THREAD_SAFE_METHOD_ - - if (p_screen == SCREEN_OF_MAIN_WINDOW) { - p_screen = window_get_current_screen(); - } - - NSArray *screenArray = [NSScreen screens]; - if ((NSUInteger)p_screen < [screenArray count]) { - const float scale = screen_get_max_scale(); - NSRect nsrect = [[screenArray objectAtIndex:p_screen] visibleFrame]; - - Point2i position = Point2i(nsrect.origin.x, nsrect.origin.y + nsrect.size.height) * scale - _get_screens_origin(); - position.y *= -1; - Size2i size = Size2i(nsrect.size.width, nsrect.size.height) * scale; - - return Rect2i(position, size); - } - - return Rect2i(); -} - -float DisplayServerOSX::screen_get_refresh_rate(int p_screen) const { - _THREAD_SAFE_METHOD_ - - if (p_screen == SCREEN_OF_MAIN_WINDOW) { - p_screen = window_get_current_screen(); - } - - NSArray *screenArray = [NSScreen screens]; - if ((NSUInteger)p_screen < [screenArray count]) { - NSDictionary *description = [[screenArray objectAtIndex:p_screen] deviceDescription]; - const CGDisplayModeRef displayMode = CGDisplayCopyDisplayMode([[description objectForKey:@"NSScreenNumber"] unsignedIntValue]); - const double displayRefreshRate = CGDisplayModeGetRefreshRate(displayMode); - return (float)displayRefreshRate; - } - ERR_PRINT("An error occurred while trying to get the screen refresh rate."); - return SCREEN_REFRESH_RATE_FALLBACK; -} - -Vector<DisplayServer::WindowID> DisplayServerOSX::get_window_list() const { - _THREAD_SAFE_METHOD_ - - Vector<int> ret; - for (const KeyValue<WindowID, WindowData> &E : windows) { - ret.push_back(E.key); - } - return ret; -} - -DisplayServer::WindowID DisplayServerOSX::create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect) { - _THREAD_SAFE_METHOD_ - - WindowID id = _create_window(p_mode, p_vsync_mode, p_rect); - for (int i = 0; i < WINDOW_FLAG_MAX; i++) { - if (p_flags & (1 << i)) { - window_set_flag(WindowFlags(i), true, id); - } - } - - return id; -} - -void DisplayServerOSX::show_window(WindowID p_id) { - WindowData &wd = windows[p_id]; - - popup_open(p_id); - if (wd.no_focus || wd.is_popup) { - [wd.window_object orderFront:nil]; - } else { - [wd.window_object makeKeyAndOrderFront:nil]; - } -} - -void DisplayServerOSX::delete_sub_window(WindowID p_id) { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND(!windows.has(p_id)); - ERR_FAIL_COND_MSG(p_id == MAIN_WINDOW_ID, "Main window can't be deleted"); - - WindowData &wd = windows[p_id]; - - [wd.window_object setContentView:nil]; - [wd.window_object close]; -} - -void DisplayServerOSX::window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window) { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND(!windows.has(p_window)); - WindowData &wd = windows[p_window]; - wd.rect_changed_callback = p_callable; -} - -void DisplayServerOSX::window_set_window_event_callback(const Callable &p_callable, WindowID p_window) { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND(!windows.has(p_window)); - WindowData &wd = windows[p_window]; - wd.event_callback = p_callable; -} - -void DisplayServerOSX::window_set_input_event_callback(const Callable &p_callable, WindowID p_window) { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND(!windows.has(p_window)); - WindowData &wd = windows[p_window]; - wd.input_event_callback = p_callable; -} - -void DisplayServerOSX::window_set_input_text_callback(const Callable &p_callable, WindowID p_window) { - _THREAD_SAFE_METHOD_ - ERR_FAIL_COND(!windows.has(p_window)); - WindowData &wd = windows[p_window]; - wd.input_text_callback = p_callable; -} - -void DisplayServerOSX::window_set_drop_files_callback(const Callable &p_callable, WindowID p_window) { - _THREAD_SAFE_METHOD_ - ERR_FAIL_COND(!windows.has(p_window)); - WindowData &wd = windows[p_window]; - wd.drop_files_callback = p_callable; -} - -void DisplayServerOSX::window_set_title(const String &p_title, WindowID p_window) { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND(!windows.has(p_window)); - WindowData &wd = windows[p_window]; - - [wd.window_object setTitle:[NSString stringWithUTF8String:p_title.utf8().get_data()]]; -} - -void DisplayServerOSX::window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window) { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND(!windows.has(p_window)); - WindowData &wd = windows[p_window]; - - wd.mpath = p_region; -} - -int DisplayServerOSX::window_get_current_screen(WindowID p_window) const { - _THREAD_SAFE_METHOD_ - ERR_FAIL_COND_V(!windows.has(p_window), -1); - const WindowData &wd = windows[p_window]; - - const NSUInteger index = [[NSScreen screens] indexOfObject:[wd.window_object screen]]; - return (index == NSNotFound) ? 0 : index; -} - -void DisplayServerOSX::window_set_current_screen(int p_screen, WindowID p_window) { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND(!windows.has(p_window)); - WindowData &wd = windows[p_window]; - - if (window_get_current_screen(p_window) == p_screen) { - return; - } - - bool was_fullscreen = false; - if (wd.fullscreen) { - // Temporary exit fullscreen mode to move window. - [wd.window_object toggleFullScreen:nil]; - was_fullscreen = true; - } - - Point2i wpos = window_get_position(p_window) - screen_get_position(window_get_current_screen(p_window)); - window_set_position(wpos + screen_get_position(p_screen), p_window); - - if (was_fullscreen) { - // Re-enter fullscreen mode. - [wd.window_object toggleFullScreen:nil]; - } -} - -void DisplayServerOSX::window_set_exclusive(WindowID p_window, bool p_exclusive) { - _THREAD_SAFE_METHOD_ - ERR_FAIL_COND(!windows.has(p_window)); - WindowData &wd = windows[p_window]; - if (wd.exclusive != p_exclusive) { - wd.exclusive = p_exclusive; - if (wd.transient_parent != INVALID_WINDOW_ID) { - WindowData &wd_parent = windows[wd.transient_parent]; - if (wd.exclusive) { - ERR_FAIL_COND_MSG([[wd_parent.window_object childWindows] count] > 0, "Transient parent has another exclusive child."); - [wd_parent.window_object addChildWindow:wd.window_object ordered:NSWindowAbove]; - } else { - [wd_parent.window_object removeChildWindow:wd.window_object]; - } - } - } -} - -Point2i DisplayServerOSX::window_get_position(WindowID p_window) const { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND_V(!windows.has(p_window), Point2i()); - const WindowData &wd = windows[p_window]; - - // Use content rect position (without titlebar / window border). - const NSRect contentRect = [wd.window_view frame]; - const NSRect nsrect = [wd.window_object convertRectToScreen:contentRect]; - Point2i pos; - - // Return the position of the top-left corner, for OS X the y starts at the bottom. - const float scale = screen_get_max_scale(); - pos.x = nsrect.origin.x; - pos.y = (nsrect.origin.y + nsrect.size.height); - pos *= scale; - pos -= _get_screens_origin(); - // OS X native y-coordinate relative to _get_screens_origin() is negative, - // Godot expects a positive value. - pos.y *= -1; - return pos; -} - -void DisplayServerOSX::window_set_position(const Point2i &p_position, WindowID p_window) { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND(!windows.has(p_window)); - WindowData &wd = windows[p_window]; - - Point2i position = p_position; - // OS X native y-coordinate relative to _get_screens_origin() is negative, - // Godot passes a positive value. - position.y *= -1; - position += _get_screens_origin(); - position /= screen_get_max_scale(); - - // Remove titlebar / window border size. - const NSRect contentRect = [wd.window_view frame]; - const NSRect windowRect = [wd.window_object frame]; - const NSRect nsrect = [wd.window_object convertRectToScreen:contentRect]; - Point2i offset; - offset.x = (nsrect.origin.x - windowRect.origin.x); - offset.y = (nsrect.origin.y + nsrect.size.height); - offset.y -= (windowRect.origin.y + windowRect.size.height); - - [wd.window_object setFrameTopLeftPoint:NSMakePoint(position.x - offset.x, position.y - offset.y)]; - - _update_window_style(wd); - update_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream]); -} - -void DisplayServerOSX::window_set_transient(WindowID p_window, WindowID p_parent) { - _THREAD_SAFE_METHOD_ - ERR_FAIL_COND(p_window == p_parent); - - ERR_FAIL_COND(!windows.has(p_window)); - WindowData &wd_window = windows[p_window]; - - ERR_FAIL_COND(wd_window.transient_parent == p_parent); - - ERR_FAIL_COND_MSG(wd_window.on_top, "Windows with the 'on top' can't become transient."); - if (p_parent == INVALID_WINDOW_ID) { - // Remove transient. - ERR_FAIL_COND(wd_window.transient_parent == INVALID_WINDOW_ID); - ERR_FAIL_COND(!windows.has(wd_window.transient_parent)); - - WindowData &wd_parent = windows[wd_window.transient_parent]; - - wd_window.transient_parent = INVALID_WINDOW_ID; - wd_parent.transient_children.erase(p_window); - [wd_window.window_object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; - - if (wd_window.exclusive) { - [wd_parent.window_object removeChildWindow:wd_window.window_object]; - } - } else { - ERR_FAIL_COND(!windows.has(p_parent)); - ERR_FAIL_COND_MSG(wd_window.transient_parent != INVALID_WINDOW_ID, "Window already has a transient parent"); - WindowData &wd_parent = windows[p_parent]; - - wd_window.transient_parent = p_parent; - wd_parent.transient_children.insert(p_window); - [wd_window.window_object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary]; - - if (wd_window.exclusive) { - [wd_parent.window_object addChildWindow:wd_window.window_object ordered:NSWindowAbove]; - } - } -} - -void DisplayServerOSX::window_set_max_size(const Size2i p_size, WindowID p_window) { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND(!windows.has(p_window)); - WindowData &wd = windows[p_window]; - - if ((p_size != Size2i()) && ((p_size.x < wd.min_size.x) || (p_size.y < wd.min_size.y))) { - ERR_PRINT("Maximum window size can't be smaller than minimum window size!"); - return; - } - wd.max_size = p_size; - - if ((wd.max_size != Size2i()) && !wd.fullscreen) { - Size2i size = wd.max_size / screen_get_max_scale(); - [wd.window_object setContentMaxSize:NSMakeSize(size.x, size.y)]; - } else { - [wd.window_object setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)]; - } -} - -Size2i DisplayServerOSX::window_get_max_size(WindowID p_window) const { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND_V(!windows.has(p_window), Size2i()); - const WindowData &wd = windows[p_window]; - return wd.max_size; -} - -void DisplayServerOSX::window_set_min_size(const Size2i p_size, WindowID p_window) { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND(!windows.has(p_window)); - WindowData &wd = windows[p_window]; - - if ((p_size != Size2i()) && (wd.max_size != Size2i()) && ((p_size.x > wd.max_size.x) || (p_size.y > wd.max_size.y))) { - ERR_PRINT("Minimum window size can't be larger than maximum window size!"); - return; - } - wd.min_size = p_size; - - if ((wd.min_size != Size2i()) && !wd.fullscreen) { - Size2i size = wd.min_size / screen_get_max_scale(); - [wd.window_object setContentMinSize:NSMakeSize(size.x, size.y)]; - } else { - [wd.window_object setContentMinSize:NSMakeSize(0, 0)]; - } -} - -Size2i DisplayServerOSX::window_get_min_size(WindowID p_window) const { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND_V(!windows.has(p_window), Size2i()); - const WindowData &wd = windows[p_window]; - - return wd.min_size; -} - -void DisplayServerOSX::window_set_size(const Size2i p_size, WindowID p_window) { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND(!windows.has(p_window)); - WindowData &wd = windows[p_window]; - - Size2i size = p_size / screen_get_max_scale(); - - NSPoint top_left; - NSRect old_frame = [wd.window_object frame]; - top_left.x = old_frame.origin.x; - top_left.y = NSMaxY(old_frame); - - NSRect new_frame = NSMakeRect(0, 0, size.x, size.y); - new_frame = [wd.window_object frameRectForContentRect:new_frame]; - - new_frame.origin.x = top_left.x; - new_frame.origin.y = top_left.y - new_frame.size.height; - - [wd.window_object setFrame:new_frame display:YES]; - - _update_window_style(wd); -} - -Size2i DisplayServerOSX::window_get_size(WindowID p_window) const { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND_V(!windows.has(p_window), Size2i()); - const WindowData &wd = windows[p_window]; - return wd.size; -} - -Size2i DisplayServerOSX::window_get_real_size(WindowID p_window) const { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND_V(!windows.has(p_window), Size2i()); - const WindowData &wd = windows[p_window]; - NSRect frame = [wd.window_object frame]; - return Size2i(frame.size.width, frame.size.height) * screen_get_max_scale(); -} - -void DisplayServerOSX::window_set_mode(WindowMode p_mode, WindowID p_window) { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND(!windows.has(p_window)); - WindowData &wd = windows[p_window]; - - WindowMode old_mode = window_get_mode(p_window); - if (old_mode == p_mode) { - return; // Do nothing. - } - - switch (old_mode) { - case WINDOW_MODE_WINDOWED: { - // Do nothing. - } break; - case WINDOW_MODE_MINIMIZED: { - [wd.window_object deminiaturize:nil]; - } break; - case WINDOW_MODE_EXCLUSIVE_FULLSCREEN: - case WINDOW_MODE_FULLSCREEN: { - [(NSWindow *)wd.window_object setLevel:NSNormalWindowLevel]; - _set_window_per_pixel_transparency_enabled(true, p_window); - if (wd.resize_disabled) { // Restore resize disabled. - [wd.window_object setStyleMask:[wd.window_object styleMask] & ~NSWindowStyleMaskResizable]; - } - if (wd.min_size != Size2i()) { - Size2i size = wd.min_size / screen_get_max_scale(); - [wd.window_object setContentMinSize:NSMakeSize(size.x, size.y)]; - } - if (wd.max_size != Size2i()) { - Size2i size = wd.max_size / screen_get_max_scale(); - [wd.window_object setContentMaxSize:NSMakeSize(size.x, size.y)]; - } - [wd.window_object toggleFullScreen:nil]; - wd.fullscreen = false; - } break; - case WINDOW_MODE_MAXIMIZED: { - if ([wd.window_object isZoomed]) { - [wd.window_object zoom:nil]; - } - } break; - } - - switch (p_mode) { - case WINDOW_MODE_WINDOWED: { - // Do nothing. - } break; - case WINDOW_MODE_MINIMIZED: { - [wd.window_object performMiniaturize:nil]; - } break; - case WINDOW_MODE_EXCLUSIVE_FULLSCREEN: - case WINDOW_MODE_FULLSCREEN: { - _set_window_per_pixel_transparency_enabled(false, p_window); - if (wd.resize_disabled) { // Fullscreen window should be resizable to work. - [wd.window_object setStyleMask:[wd.window_object styleMask] | NSWindowStyleMaskResizable]; - } - [wd.window_object setContentMinSize:NSMakeSize(0, 0)]; - [wd.window_object setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)]; - [wd.window_object toggleFullScreen:nil]; - wd.fullscreen = true; - } break; - case WINDOW_MODE_MAXIMIZED: { - if (![wd.window_object isZoomed]) { - [wd.window_object zoom:nil]; - } - } break; - } -} - -DisplayServer::WindowMode DisplayServerOSX::window_get_mode(WindowID p_window) const { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND_V(!windows.has(p_window), WINDOW_MODE_WINDOWED); - const WindowData &wd = windows[p_window]; - - if (wd.fullscreen) { // If fullscreen, it's not in another mode. - return WINDOW_MODE_FULLSCREEN; - } - if ([wd.window_object isZoomed] && !wd.resize_disabled) { - return WINDOW_MODE_MAXIMIZED; - } - if ([wd.window_object respondsToSelector:@selector(isMiniaturized)]) { - if ([wd.window_object isMiniaturized]) { - return WINDOW_MODE_MINIMIZED; - } - } - - // All other discarded, return windowed. - return WINDOW_MODE_WINDOWED; -} - -bool DisplayServerOSX::window_is_maximize_allowed(WindowID p_window) const { - return true; -} - -void DisplayServerOSX::window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window) { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND(!windows.has(p_window)); - WindowData &wd = windows[p_window]; - - switch (p_flag) { - case WINDOW_FLAG_RESIZE_DISABLED: { - wd.resize_disabled = p_enabled; - if (wd.fullscreen) { // Fullscreen window should be resizable, style will be applied on exiting fullscreen. - return; - } - if (p_enabled) { - [wd.window_object setStyleMask:[wd.window_object styleMask] & ~NSWindowStyleMaskResizable]; - } else { - [wd.window_object setStyleMask:[wd.window_object styleMask] | NSWindowStyleMaskResizable]; - } - } break; - case WINDOW_FLAG_BORDERLESS: { - // OrderOut prevents a lose focus bug with the window. - if ([wd.window_object isVisible]) { - [wd.window_object orderOut:nil]; - } - wd.borderless = p_enabled; - if (p_enabled) { - [wd.window_object setStyleMask:NSWindowStyleMaskBorderless]; - } else { - _set_window_per_pixel_transparency_enabled(false, p_window); - [wd.window_object setStyleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | (wd.resize_disabled ? 0 : NSWindowStyleMaskResizable)]; - // Force update of the window styles. - NSRect frameRect = [wd.window_object frame]; - [wd.window_object setFrame:NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width + 1, frameRect.size.height) display:NO]; - [wd.window_object setFrame:frameRect display:NO]; - } - _update_window_style(wd); - if ([wd.window_object isVisible]) { - if (wd.no_focus || wd.is_popup) { - [wd.window_object orderFront:nil]; - } else { - [wd.window_object makeKeyAndOrderFront:nil]; - } - } - } break; - case WINDOW_FLAG_ALWAYS_ON_TOP: { - wd.on_top = p_enabled; - if (wd.fullscreen) { - return; - } - if (p_enabled) { - [(NSWindow *)wd.window_object setLevel:NSFloatingWindowLevel]; - } else { - [(NSWindow *)wd.window_object setLevel:NSNormalWindowLevel]; - } - } break; - case WINDOW_FLAG_TRANSPARENT: { - if (p_enabled) { - [wd.window_object setStyleMask:NSWindowStyleMaskBorderless]; // Force borderless. - } else if (!wd.borderless) { - [wd.window_object setStyleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | (wd.resize_disabled ? 0 : NSWindowStyleMaskResizable)]; - } - _set_window_per_pixel_transparency_enabled(p_enabled, p_window); - } break; - case WINDOW_FLAG_NO_FOCUS: { - wd.no_focus = p_enabled; - } break; - case WINDOW_FLAG_POPUP: { - ERR_FAIL_COND_MSG(p_window == MAIN_WINDOW_ID, "Main window can't be popup."); - ERR_FAIL_COND_MSG([wd.window_object isVisible] && (wd.is_popup != p_enabled), "Popup flag can't changed while window is opened."); - wd.is_popup = p_enabled; - } break; - default: { - } - } -} - -bool DisplayServerOSX::window_get_flag(WindowFlags p_flag, WindowID p_window) const { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND_V(!windows.has(p_window), false); - const WindowData &wd = windows[p_window]; - - switch (p_flag) { - case WINDOW_FLAG_RESIZE_DISABLED: { - return wd.resize_disabled; - } break; - case WINDOW_FLAG_BORDERLESS: { - return [wd.window_object styleMask] == NSWindowStyleMaskBorderless; - } break; - case WINDOW_FLAG_ALWAYS_ON_TOP: { - if (wd.fullscreen) { - return wd.on_top; - } else { - return [(NSWindow *)wd.window_object level] == NSFloatingWindowLevel; - } - } break; - case WINDOW_FLAG_TRANSPARENT: { - return wd.layered_window; - } break; - case WINDOW_FLAG_NO_FOCUS: { - return wd.no_focus; - } break; - case WINDOW_FLAG_POPUP: { - return wd.is_popup; - } break; - default: { - } - } - - return false; -} - -void DisplayServerOSX::window_request_attention(WindowID p_window) { - // It's app global, ignore window id. - [NSApp requestUserAttention:NSCriticalRequest]; -} - -void DisplayServerOSX::window_move_to_foreground(WindowID p_window) { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND(!windows.has(p_window)); - const WindowData &wd = windows[p_window]; - - [[NSApplication sharedApplication] activateIgnoringOtherApps:YES]; - if (wd.no_focus || wd.is_popup) { - [wd.window_object orderFront:nil]; - } else { - [wd.window_object makeKeyAndOrderFront:nil]; - } -} - -bool DisplayServerOSX::window_can_draw(WindowID p_window) const { - return window_get_mode(p_window) != WINDOW_MODE_MINIMIZED; -} - -bool DisplayServerOSX::can_any_window_draw() const { - _THREAD_SAFE_METHOD_ - - for (const KeyValue<WindowID, WindowData> &E : windows) { - if (window_get_mode(E.key) != WINDOW_MODE_MINIMIZED) { - return true; - } - } - return false; -} - -void DisplayServerOSX::window_set_ime_active(const bool p_active, WindowID p_window) { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND(!windows.has(p_window)); - WindowData &wd = windows[p_window]; - - wd.im_active = p_active; - - if (!p_active) { - [wd.window_view cancelComposition]; - } -} - -void DisplayServerOSX::window_set_ime_position(const Point2i &p_pos, WindowID p_window) { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND(!windows.has(p_window)); - WindowData &wd = windows[p_window]; - - wd.im_position = p_pos; -} - -DisplayServer::WindowID DisplayServerOSX::get_window_at_screen_position(const Point2i &p_position) const { - Point2i position = p_position; - position.y *= -1; - position += _get_screens_origin(); - position /= screen_get_max_scale(); - - NSInteger wnum = [NSWindow windowNumberAtPoint:NSMakePoint(position.x, position.y) belowWindowWithWindowNumber:0 /*topmost*/]; - for (const KeyValue<WindowID, WindowData> &E : windows) { - if ([E.value.window_object windowNumber] == wnum) { - return E.key; - } - } - return INVALID_WINDOW_ID; -} - -int64_t DisplayServerOSX::window_get_native_handle(HandleType p_handle_type, WindowID p_window) const { - ERR_FAIL_COND_V(!windows.has(p_window), 0); - switch (p_handle_type) { - case DISPLAY_HANDLE: { - return 0; // Not supported. - } - case WINDOW_HANDLE: { - return (int64_t)windows[p_window].window_object; - } - case WINDOW_VIEW: { - return (int64_t)windows[p_window].window_view; - } - default: { - return 0; - } - } -} - -void DisplayServerOSX::window_attach_instance_id(ObjectID p_instance, WindowID p_window) { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND(!windows.has(p_window)); - windows[p_window].instance_id = p_instance; -} - -ObjectID DisplayServerOSX::window_get_attached_instance_id(WindowID p_window) const { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND_V(!windows.has(p_window), ObjectID()); - return windows[p_window].instance_id; -} - -void DisplayServerOSX::gl_window_make_current(DisplayServer::WindowID p_window_id) { -#if defined(GLES3_ENABLED) - gl_manager->window_make_current(p_window_id); -#endif -} - -void DisplayServerOSX::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) { - _THREAD_SAFE_METHOD_ -#if defined(GLES3_ENABLED) - if (gl_manager) { - gl_manager->set_use_vsync(p_vsync_mode); - } -#endif -#if defined(VULKAN_ENABLED) - if (context_vulkan) { - context_vulkan->set_vsync_mode(p_window, p_vsync_mode); - } -#endif -} - -DisplayServer::VSyncMode DisplayServerOSX::window_get_vsync_mode(WindowID p_window) const { - _THREAD_SAFE_METHOD_ -#if defined(GLES3_ENABLED) - if (gl_manager) { - return (gl_manager->is_using_vsync() ? DisplayServer::VSyncMode::VSYNC_ENABLED : DisplayServer::VSyncMode::VSYNC_DISABLED); - } -#endif -#if defined(VULKAN_ENABLED) - if (context_vulkan) { - return context_vulkan->get_vsync_mode(p_window); - } -#endif - return DisplayServer::VSYNC_ENABLED; -} - -Point2i DisplayServerOSX::ime_get_selection() const { - return im_selection; -} - -String DisplayServerOSX::ime_get_text() const { - return im_text; -} - -void DisplayServerOSX::cursor_update_shape() { - _THREAD_SAFE_METHOD_ - - if (cursors[cursor_shape] != nullptr) { - [cursors[cursor_shape] set]; - } else { - switch (cursor_shape) { - case CURSOR_ARROW: - [[NSCursor arrowCursor] set]; - break; - case CURSOR_IBEAM: - [[NSCursor IBeamCursor] set]; - break; - case CURSOR_POINTING_HAND: - [[NSCursor pointingHandCursor] set]; - break; - case CURSOR_CROSS: - [[NSCursor crosshairCursor] set]; - break; - case CURSOR_WAIT: - [[NSCursor arrowCursor] set]; - break; - case CURSOR_BUSY: - [[NSCursor arrowCursor] set]; - break; - case CURSOR_DRAG: - [[NSCursor closedHandCursor] set]; - break; - case CURSOR_CAN_DROP: - [[NSCursor openHandCursor] set]; - break; - case CURSOR_FORBIDDEN: - [[NSCursor operationNotAllowedCursor] set]; - break; - case CURSOR_VSIZE: - [_cursor_from_selector(@selector(_windowResizeNorthSouthCursor), @selector(resizeUpDownCursor)) set]; - break; - case CURSOR_HSIZE: - [_cursor_from_selector(@selector(_windowResizeEastWestCursor), @selector(resizeLeftRightCursor)) set]; - break; - case CURSOR_BDIAGSIZE: - [_cursor_from_selector(@selector(_windowResizeNorthEastSouthWestCursor)) set]; - break; - case CURSOR_FDIAGSIZE: - [_cursor_from_selector(@selector(_windowResizeNorthWestSouthEastCursor)) set]; - break; - case CURSOR_MOVE: - [[NSCursor arrowCursor] set]; - break; - case CURSOR_VSPLIT: - [[NSCursor resizeUpDownCursor] set]; - break; - case CURSOR_HSPLIT: - [[NSCursor resizeLeftRightCursor] set]; - break; - case CURSOR_HELP: - [_cursor_from_selector(@selector(_helpCursor)) set]; - break; - default: { - } - } - } -} - -void DisplayServerOSX::cursor_set_shape(CursorShape p_shape) { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_INDEX(p_shape, CURSOR_MAX); - - if (cursor_shape == p_shape) { - return; - } - - cursor_shape = p_shape; - - if (mouse_mode != MOUSE_MODE_VISIBLE && mouse_mode != MOUSE_MODE_CONFINED) { - return; - } - - cursor_update_shape(); -} - -DisplayServerOSX::CursorShape DisplayServerOSX::cursor_get_shape() const { - return cursor_shape; -} - -void DisplayServerOSX::cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { - _THREAD_SAFE_METHOD_ - - if (p_cursor.is_valid()) { - HashMap<CursorShape, Vector<Variant>>::Iterator cursor_c = cursors_cache.find(p_shape); - - if (cursor_c) { - if (cursor_c->value[0] == p_cursor && cursor_c->value[1] == p_hotspot) { - cursor_set_shape(p_shape); - return; - } - cursors_cache.erase(p_shape); - } - - Ref<Texture2D> texture = p_cursor; - Ref<AtlasTexture> atlas_texture = p_cursor; - Ref<Image> image; - Size2 texture_size; - Rect2 atlas_rect; - - if (texture.is_valid()) { - image = texture->get_image(); - } - - if (!image.is_valid() && atlas_texture.is_valid()) { - texture = atlas_texture->get_atlas(); - - atlas_rect.size.width = texture->get_width(); - atlas_rect.size.height = texture->get_height(); - atlas_rect.position.x = atlas_texture->get_region().position.x; - atlas_rect.position.y = atlas_texture->get_region().position.y; - - texture_size.width = atlas_texture->get_region().size.x; - texture_size.height = atlas_texture->get_region().size.y; - } else if (image.is_valid()) { - texture_size.width = texture->get_width(); - texture_size.height = texture->get_height(); - } - - ERR_FAIL_COND(!texture.is_valid()); - ERR_FAIL_COND(p_hotspot.x < 0 || p_hotspot.y < 0); - ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256); - ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height); - - image = texture->get_image(); - - ERR_FAIL_COND(!image.is_valid()); - - NSBitmapImageRep *imgrep = [[NSBitmapImageRep alloc] - initWithBitmapDataPlanes:nullptr - pixelsWide:int(texture_size.width) - pixelsHigh:int(texture_size.height) - bitsPerSample:8 - samplesPerPixel:4 - hasAlpha:YES - isPlanar:NO - colorSpaceName:NSDeviceRGBColorSpace - bytesPerRow:int(texture_size.width) * 4 - bitsPerPixel:32]; - - ERR_FAIL_COND(imgrep == nil); - uint8_t *pixels = [imgrep bitmapData]; - - int len = int(texture_size.width * texture_size.height); - - for (int i = 0; i < len; i++) { - int row_index = floor(i / texture_size.width) + atlas_rect.position.y; - int column_index = (i % int(texture_size.width)) + atlas_rect.position.x; - - if (atlas_texture.is_valid()) { - column_index = MIN(column_index, atlas_rect.size.width - 1); - row_index = MIN(row_index, atlas_rect.size.height - 1); - } - - uint32_t color = image->get_pixel(column_index, row_index).to_argb32(); - - uint8_t alpha = (color >> 24) & 0xFF; - pixels[i * 4 + 0] = ((color >> 16) & 0xFF) * alpha / 255; - pixels[i * 4 + 1] = ((color >> 8) & 0xFF) * alpha / 255; - pixels[i * 4 + 2] = ((color)&0xFF) * alpha / 255; - pixels[i * 4 + 3] = alpha; - } - - NSImage *nsimage = [[NSImage alloc] initWithSize:NSMakeSize(texture_size.width, texture_size.height)]; - [nsimage addRepresentation:imgrep]; - - NSCursor *cursor = [[NSCursor alloc] initWithImage:nsimage hotSpot:NSMakePoint(p_hotspot.x, p_hotspot.y)]; - - cursors[p_shape] = cursor; - - Vector<Variant> params; - params.push_back(p_cursor); - params.push_back(p_hotspot); - cursors_cache.insert(p_shape, params); - - if (p_shape == cursor_shape) { - if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) { - [cursor set]; - } - } - } else { - // Reset to default system cursor. - if (cursors[p_shape] != nullptr) { - cursors[p_shape] = nullptr; - } - - cursor_update_shape(); - - cursors_cache.erase(p_shape); - } -} - -bool DisplayServerOSX::get_swap_cancel_ok() { - return false; -} - -int DisplayServerOSX::keyboard_get_layout_count() const { - if (keyboard_layout_dirty) { - const_cast<DisplayServerOSX *>(this)->_update_keyboard_layouts(); - } - return kbd_layouts.size(); -} - -void DisplayServerOSX::keyboard_set_current_layout(int p_index) { - if (keyboard_layout_dirty) { - const_cast<DisplayServerOSX *>(this)->_update_keyboard_layouts(); - } - - ERR_FAIL_INDEX(p_index, kbd_layouts.size()); - - NSString *cur_name = [NSString stringWithUTF8String:kbd_layouts[p_index].name.utf8().get_data()]; - - NSDictionary *filter_kbd = @{ (NSString *)kTISPropertyInputSourceType : (NSString *)kTISTypeKeyboardLayout }; - NSArray *list_kbd = (__bridge NSArray *)TISCreateInputSourceList((__bridge CFDictionaryRef)filter_kbd, false); - for (NSUInteger i = 0; i < [list_kbd count]; i++) { - NSString *name = (__bridge NSString *)TISGetInputSourceProperty((__bridge TISInputSourceRef)[list_kbd objectAtIndex:i], kTISPropertyLocalizedName); - if ([name isEqualToString:cur_name]) { - TISSelectInputSource((__bridge TISInputSourceRef)[list_kbd objectAtIndex:i]); - break; - } - } - - NSDictionary *filter_ime = @{ (NSString *)kTISPropertyInputSourceType : (NSString *)kTISTypeKeyboardInputMode }; - NSArray *list_ime = (__bridge NSArray *)TISCreateInputSourceList((__bridge CFDictionaryRef)filter_ime, false); - for (NSUInteger i = 0; i < [list_ime count]; i++) { - NSString *name = (__bridge NSString *)TISGetInputSourceProperty((__bridge TISInputSourceRef)[list_ime objectAtIndex:i], kTISPropertyLocalizedName); - if ([name isEqualToString:cur_name]) { - TISSelectInputSource((__bridge TISInputSourceRef)[list_ime objectAtIndex:i]); - break; - } - } -} - -int DisplayServerOSX::keyboard_get_current_layout() const { - if (keyboard_layout_dirty) { - const_cast<DisplayServerOSX *>(this)->_update_keyboard_layouts(); - } - - return current_layout; -} - -String DisplayServerOSX::keyboard_get_layout_language(int p_index) const { - if (keyboard_layout_dirty) { - const_cast<DisplayServerOSX *>(this)->_update_keyboard_layouts(); - } - - ERR_FAIL_INDEX_V(p_index, kbd_layouts.size(), ""); - return kbd_layouts[p_index].code; -} - -String DisplayServerOSX::keyboard_get_layout_name(int p_index) const { - if (keyboard_layout_dirty) { - const_cast<DisplayServerOSX *>(this)->_update_keyboard_layouts(); - } - - ERR_FAIL_INDEX_V(p_index, kbd_layouts.size(), ""); - return kbd_layouts[p_index].name; -} - -Key DisplayServerOSX::keyboard_get_keycode_from_physical(Key p_keycode) const { - if (p_keycode == Key::PAUSE) { - return p_keycode; - } - - Key modifiers = p_keycode & KeyModifierMask::MODIFIER_MASK; - Key keycode_no_mod = p_keycode & KeyModifierMask::CODE_MASK; - unsigned int osx_keycode = KeyMappingOSX::unmap_key((Key)keycode_no_mod); - return (Key)(KeyMappingOSX::remap_key(osx_keycode, 0) | modifiers); -} - -void DisplayServerOSX::process_events() { - _THREAD_SAFE_METHOD_ - - while (true) { - NSEvent *event = [NSApp - nextEventMatchingMask:NSEventMaskAny - untilDate:[NSDate distantPast] - inMode:NSDefaultRunLoopMode - dequeue:YES]; - - if (event == nil) { - break; - } - - [NSApp sendEvent:event]; - } - - if (!drop_events) { - _process_key_events(); - Input::get_singleton()->flush_buffered_events(); - } - - for (KeyValue<WindowID, WindowData> &E : windows) { - WindowData &wd = E.value; - if (wd.mpath.size() > 0) { - update_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream]); - if (Geometry2D::is_point_in_polygon(wd.mouse_pos, wd.mpath)) { - if ([wd.window_object ignoresMouseEvents]) { - [wd.window_object setIgnoresMouseEvents:NO]; - } - } else { - if (![wd.window_object ignoresMouseEvents]) { - [wd.window_object setIgnoresMouseEvents:YES]; - } - } - } else { - if ([wd.window_object ignoresMouseEvents]) { - [wd.window_object setIgnoresMouseEvents:NO]; - } - } - } -} - -void DisplayServerOSX::force_process_and_drop_events() { - _THREAD_SAFE_METHOD_ - - drop_events = true; - process_events(); - drop_events = false; -} - -void DisplayServerOSX::release_rendering_thread() { -} - -void DisplayServerOSX::make_rendering_thread() { -} - -void DisplayServerOSX::swap_buffers() { -#if defined(GLES3_ENABLED) - if (gl_manager) { - gl_manager->swap_buffers(); - } -#endif -} - -void DisplayServerOSX::set_native_icon(const String &p_filename) { - _THREAD_SAFE_METHOD_ - - Ref<FileAccess> f = FileAccess::open(p_filename, FileAccess::READ); - ERR_FAIL_COND(f.is_null()); - - Vector<uint8_t> data; - uint64_t len = f->get_length(); - data.resize(len); - f->get_buffer((uint8_t *)&data.write[0], len); - - NSData *icon_data = [[NSData alloc] initWithBytes:&data.write[0] length:len]; - ERR_FAIL_COND_MSG(!icon_data, "Error reading icon data."); - - NSImage *icon = [[NSImage alloc] initWithData:icon_data]; - ERR_FAIL_COND_MSG(!icon, "Error loading icon."); - - [NSApp setApplicationIconImage:icon]; -} - -void DisplayServerOSX::set_icon(const Ref<Image> &p_icon) { - _THREAD_SAFE_METHOD_ - - Ref<Image> img = p_icon; - img = img->duplicate(); - img->convert(Image::FORMAT_RGBA8); - NSBitmapImageRep *imgrep = [[NSBitmapImageRep alloc] - initWithBitmapDataPlanes:nullptr - pixelsWide:img->get_width() - pixelsHigh:img->get_height() - bitsPerSample:8 - samplesPerPixel:4 - hasAlpha:YES - isPlanar:NO - colorSpaceName:NSDeviceRGBColorSpace - bytesPerRow:img->get_width() * 4 - bitsPerPixel:32]; - ERR_FAIL_COND(imgrep == nil); - uint8_t *pixels = [imgrep bitmapData]; - - int len = img->get_width() * img->get_height(); - const uint8_t *r = img->get_data().ptr(); - - /* Premultiply the alpha channel */ - for (int i = 0; i < len; i++) { - uint8_t alpha = r[i * 4 + 3]; - pixels[i * 4 + 0] = (uint8_t)(((uint16_t)r[i * 4 + 0] * alpha) / 255); - pixels[i * 4 + 1] = (uint8_t)(((uint16_t)r[i * 4 + 1] * alpha) / 255); - pixels[i * 4 + 2] = (uint8_t)(((uint16_t)r[i * 4 + 2] * alpha) / 255); - pixels[i * 4 + 3] = alpha; - } - - NSImage *nsimg = [[NSImage alloc] initWithSize:NSMakeSize(img->get_width(), img->get_height())]; - ERR_FAIL_COND(nsimg == nil); - - [nsimg addRepresentation:imgrep]; - [NSApp setApplicationIconImage:nsimg]; -} - -DisplayServer *DisplayServerOSX::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { - DisplayServer *ds = memnew(DisplayServerOSX(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_resolution, r_error)); - if (r_error != OK) { - OS::get_singleton()->alert("Your video card driver does not support any of the supported Vulkan or OpenGL versions.", "Unable to initialize Video driver"); - } - return ds; -} - -Vector<String> DisplayServerOSX::get_rendering_drivers_func() { - Vector<String> drivers; - -#if defined(VULKAN_ENABLED) - drivers.push_back("vulkan"); -#endif -#if defined(GLES3_ENABLED) - drivers.push_back("opengl3"); -#endif - - return drivers; -} - -void DisplayServerOSX::register_osx_driver() { - register_create_function("osx", create_func, get_rendering_drivers_func); -} - -DisplayServer::WindowID DisplayServerOSX::window_get_active_popup() const { - const List<WindowID>::Element *E = popup_list.back(); - if (E) { - return E->get(); - } else { - return INVALID_WINDOW_ID; - } -} - -void DisplayServerOSX::window_set_popup_safe_rect(WindowID p_window, const Rect2i &p_rect) { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND(!windows.has(p_window)); - WindowData &wd = windows[p_window]; - wd.parent_safe_rect = p_rect; -} - -Rect2i DisplayServerOSX::window_get_popup_safe_rect(WindowID p_window) const { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND_V(!windows.has(p_window), Rect2i()); - const WindowData &wd = windows[p_window]; - return wd.parent_safe_rect; -} - -void DisplayServerOSX::popup_open(WindowID p_window) { - _THREAD_SAFE_METHOD_ - - WindowData &wd = windows[p_window]; - if (wd.is_popup) { - bool was_empty = popup_list.is_empty(); - // Find current popup parent, or root popup if new window is not transient. - List<WindowID>::Element *C = nullptr; - List<WindowID>::Element *E = popup_list.back(); - while (E) { - if (wd.transient_parent != E->get() || wd.transient_parent == INVALID_WINDOW_ID) { - C = E; - E = E->prev(); - } else { - break; - } - } - if (C) { - send_window_event(windows[C->get()], DisplayServerOSX::WINDOW_EVENT_CLOSE_REQUEST); - } - - if (was_empty && popup_list.is_empty()) { - // Inform OS that popup was opened, to close other native popups. - [[NSDistributedNotificationCenter defaultCenter] postNotificationName:@"com.apple.HIToolbox.beginMenuTrackingNotification" object:@"org.godotengine.godot.popup_window"]; - } - time_since_popup = OS::get_singleton()->get_ticks_msec(); - popup_list.push_back(p_window); - } -} - -void DisplayServerOSX::popup_close(WindowID p_window) { - _THREAD_SAFE_METHOD_ - - bool was_empty = popup_list.is_empty(); - List<WindowID>::Element *E = popup_list.find(p_window); - while (E) { - List<WindowID>::Element *F = E->next(); - WindowID win_id = E->get(); - popup_list.erase(E); - - send_window_event(windows[win_id], DisplayServerOSX::WINDOW_EVENT_CLOSE_REQUEST); - E = F; - } - if (!was_empty && popup_list.is_empty()) { - // Inform OS that all popups are closed. - [[NSDistributedNotificationCenter defaultCenter] postNotificationName:@"com.apple.HIToolbox.endMenuTrackingNotification" object:@"org.godotengine.godot.popup_window"]; - } -} - -bool DisplayServerOSX::mouse_process_popups(bool p_close) { - _THREAD_SAFE_METHOD_ - - bool was_empty = popup_list.is_empty(); - bool closed = false; - if (p_close) { - // Close all popups. - List<WindowID>::Element *E = popup_list.front(); - if (E) { - send_window_event(windows[E->get()], DisplayServerOSX::WINDOW_EVENT_CLOSE_REQUEST); - closed = true; - } - if (!was_empty) { - // Inform OS that all popups are closed. - [[NSDistributedNotificationCenter defaultCenter] postNotificationName:@"com.apple.HIToolbox.endMenuTrackingNotification" object:@"org.godotengine.godot.popup_window"]; - } - } else { - uint64_t delta = OS::get_singleton()->get_ticks_msec() - time_since_popup; - if (delta < 250) { - return false; - } - - Point2i pos = mouse_get_position(); - List<WindowID>::Element *C = nullptr; - List<WindowID>::Element *E = popup_list.back(); - // Find top popup to close. - while (E) { - // Popup window area. - Rect2i win_rect = Rect2i(window_get_position(E->get()), window_get_size(E->get())); - // Area of the parent window, which responsible for opening sub-menu. - Rect2i safe_rect = window_get_popup_safe_rect(E->get()); - if (win_rect.has_point(pos)) { - break; - } else if (safe_rect != Rect2i() && safe_rect.has_point(pos)) { - break; - } else { - C = E; - E = E->prev(); - } - } - if (C) { - send_window_event(windows[C->get()], DisplayServerOSX::WINDOW_EVENT_CLOSE_REQUEST); - closed = true; - } - if (!was_empty && popup_list.is_empty()) { - // Inform OS that all popups are closed. - [[NSDistributedNotificationCenter defaultCenter] postNotificationName:@"com.apple.HIToolbox.endMenuTrackingNotification" object:@"org.godotengine.godot.popup_window"]; - } - } - return closed; -} - -DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { - Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events); - - r_error = OK; - - memset(cursors, 0, sizeof(cursors)); - - event_source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); - ERR_FAIL_COND(!event_source); - - CGEventSourceSetLocalEventsSuppressionInterval(event_source, 0.0); - - int screen_count = get_screen_count(); - for (int i = 0; i < screen_count; i++) { - display_max_scale = fmax(display_max_scale, screen_get_scale(i)); - } - - // Register to be notified on keyboard layout changes. - CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(), - nullptr, _keyboard_layout_changed, - kTISNotifySelectedKeyboardInputSourceChanged, nullptr, - CFNotificationSuspensionBehaviorDeliverImmediately); - - // Register to be notified on displays arrangement changes. - CGDisplayRegisterReconfigurationCallback(_displays_arrangement_changed, nullptr); - - // Init TTS - tts = [[TTS_OSX alloc] init]; - - NSMenuItem *menu_item; - NSString *title; - - NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"]; - if (nsappname == nil) { - nsappname = [[NSProcessInfo processInfo] processName]; - } - - // Setup Dock menu. - dock_menu = [[NSMenu alloc] initWithTitle:@"_dock"]; - [dock_menu setAutoenablesItems:NO]; - - // Setup Apple menu. - apple_menu = [[NSMenu alloc] initWithTitle:@""]; - title = [NSString stringWithFormat:NSLocalizedString(@"About %@", nil), nsappname]; - [apple_menu addItemWithTitle:title action:@selector(showAbout:) keyEquivalent:@""]; - [apple_menu setAutoenablesItems:NO]; - - [apple_menu addItem:[NSMenuItem separatorItem]]; - - NSMenu *services = [[NSMenu alloc] initWithTitle:@""]; - menu_item = [apple_menu addItemWithTitle:NSLocalizedString(@"Services", nil) action:nil keyEquivalent:@""]; - [apple_menu setSubmenu:services forItem:menu_item]; - [NSApp setServicesMenu:services]; - - [apple_menu addItem:[NSMenuItem separatorItem]]; - - title = [NSString stringWithFormat:NSLocalizedString(@"Hide %@", nil), nsappname]; - [apple_menu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"]; - - menu_item = [apple_menu addItemWithTitle:NSLocalizedString(@"Hide Others", nil) action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; - [menu_item setKeyEquivalentModifierMask:(NSEventModifierFlagOption | NSEventModifierFlagCommand)]; - - [apple_menu addItemWithTitle:NSLocalizedString(@"Show all", nil) action:@selector(unhideAllApplications:) keyEquivalent:@""]; - - [apple_menu addItem:[NSMenuItem separatorItem]]; - - title = [NSString stringWithFormat:NSLocalizedString(@"Quit %@", nil), nsappname]; - [apple_menu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"]; - - // Add items to the menu bar. - NSMenu *main_menu = [NSApp mainMenu]; - menu_item = [main_menu addItemWithTitle:@"" action:nil keyEquivalent:@""]; - [main_menu setSubmenu:apple_menu forItem:menu_item]; - [main_menu setAutoenablesItems:NO]; - - //!!!!!!!!!!!!!!!!!!!!!!!!!! - //TODO - do Vulkan and OpenGL support checks, driver selection and fallback - rendering_driver = p_rendering_driver; - -#if defined(GLES3_ENABLED) - if (rendering_driver == "opengl3") { - GLManager_OSX::ContextType opengl_api_type = GLManager_OSX::GLES_3_0_COMPATIBLE; - gl_manager = memnew(GLManager_OSX(opengl_api_type)); - if (gl_manager->initialize() != OK) { - memdelete(gl_manager); - gl_manager = nullptr; - r_error = ERR_UNAVAILABLE; - ERR_FAIL_MSG("Could not initialize OpenGL"); - return; - } - } -#endif -#if defined(VULKAN_ENABLED) - if (rendering_driver == "vulkan") { - context_vulkan = memnew(VulkanContextOSX); - if (context_vulkan->initialize() != OK) { - memdelete(context_vulkan); - context_vulkan = nullptr; - r_error = ERR_CANT_CREATE; - ERR_FAIL_MSG("Could not initialize Vulkan"); - } - } -#endif - - Point2i window_position( - screen_get_position(0).x + (screen_get_size(0).width - p_resolution.width) / 2, - screen_get_position(0).y + (screen_get_size(0).height - p_resolution.height) / 2); - WindowID main_window = _create_window(p_mode, p_vsync_mode, Rect2i(window_position, p_resolution)); - ERR_FAIL_COND(main_window == INVALID_WINDOW_ID); - for (int i = 0; i < WINDOW_FLAG_MAX; i++) { - if (p_flags & (1 << i)) { - window_set_flag(WindowFlags(i), true, main_window); - } - } - show_window(MAIN_WINDOW_ID); - -#if defined(GLES3_ENABLED) - if (rendering_driver == "opengl3") { - RasterizerGLES3::make_current(); - } -#endif -#if defined(VULKAN_ENABLED) - if (rendering_driver == "vulkan") { - rendering_device_vulkan = memnew(RenderingDeviceVulkan); - rendering_device_vulkan->initialize(context_vulkan); - - RendererCompositorRD::make_current(); - } -#endif -} - -DisplayServerOSX::~DisplayServerOSX() { - // Destroy all windows. - for (HashMap<WindowID, WindowData>::Iterator E = windows.begin(); E;) { - HashMap<WindowID, WindowData>::Iterator F = E; - ++E; - [F->value.window_object setContentView:nil]; - [F->value.window_object close]; - } - - // Destroy drivers. -#if defined(GLES3_ENABLED) - if (gl_manager) { - memdelete(gl_manager); - gl_manager = nullptr; - } -#endif -#if defined(VULKAN_ENABLED) - if (rendering_device_vulkan) { - rendering_device_vulkan->finalize(); - memdelete(rendering_device_vulkan); - rendering_device_vulkan = nullptr; - } - - if (context_vulkan) { - memdelete(context_vulkan); - context_vulkan = nullptr; - } -#endif - - CFNotificationCenterRemoveObserver(CFNotificationCenterGetDistributedCenter(), nullptr, kTISNotifySelectedKeyboardInputSourceChanged, nullptr); - CGDisplayRemoveReconfigurationCallback(_displays_arrangement_changed, nullptr); - - cursors_cache.clear(); -} diff --git a/platform/osx/export/codesign.cpp b/platform/osx/export/codesign.cpp deleted file mode 100644 index fd044c00cc..0000000000 --- a/platform/osx/export/codesign.cpp +++ /dev/null @@ -1,1564 +0,0 @@ -/*************************************************************************/ -/* codesign.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "codesign.h" - -#include "lipo.h" -#include "macho.h" -#include "plist.h" - -#include "core/os/os.h" -#include "editor/editor_paths.h" -#include "editor/editor_settings.h" - -#include "modules/modules_enabled.gen.h" // For regex. - -#include <ctime> - -#ifdef MODULE_REGEX_ENABLED - -/*************************************************************************/ -/* CodeSignCodeResources */ -/*************************************************************************/ - -String CodeSignCodeResources::hash_sha1_base64(const String &p_path) { - Ref<FileAccess> fa = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_V_MSG(fa.is_null(), String(), vformat("CodeSign/CodeResources: Can't open file: \"%s\".", p_path)); - - CryptoCore::SHA1Context ctx; - ctx.start(); - - unsigned char step[4096]; - while (true) { - uint64_t br = fa->get_buffer(step, 4096); - if (br > 0) { - ctx.update(step, br); - } - if (br < 4096) { - break; - } - } - - unsigned char hash[0x14]; - ctx.finish(hash); - - return CryptoCore::b64_encode_str(hash, 0x14); -} - -String CodeSignCodeResources::hash_sha256_base64(const String &p_path) { - Ref<FileAccess> fa = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_V_MSG(fa.is_null(), String(), vformat("CodeSign/CodeResources: Can't open file: \"%s\".", p_path)); - - CryptoCore::SHA256Context ctx; - ctx.start(); - - unsigned char step[4096]; - while (true) { - uint64_t br = fa->get_buffer(step, 4096); - if (br > 0) { - ctx.update(step, br); - } - if (br < 4096) { - break; - } - } - - unsigned char hash[0x20]; - ctx.finish(hash); - - return CryptoCore::b64_encode_str(hash, 0x20); -} - -void CodeSignCodeResources::add_rule1(const String &p_rule, const String &p_key, int p_weight, bool p_store) { - rules1.push_back(CRRule(p_rule, p_key, p_weight, p_store)); -} - -void CodeSignCodeResources::add_rule2(const String &p_rule, const String &p_key, int p_weight, bool p_store) { - rules2.push_back(CRRule(p_rule, p_key, p_weight, p_store)); -} - -CodeSignCodeResources::CRMatch CodeSignCodeResources::match_rules1(const String &p_path) const { - CRMatch found = CRMatch::CR_MATCH_NO; - int weight = 0; - for (int i = 0; i < rules1.size(); i++) { - RegEx regex = RegEx(rules1[i].file_pattern); - if (regex.search(p_path).is_valid()) { - if (rules1[i].key == "omit") { - return CRMatch::CR_MATCH_NO; - } else if (rules1[i].key == "nested") { - if (weight <= rules1[i].weight) { - found = CRMatch::CR_MATCH_NESTED; - weight = rules1[i].weight; - } - } else if (rules1[i].key == "optional") { - if (weight <= rules1[i].weight) { - found = CRMatch::CR_MATCH_OPTIONAL; - weight = rules1[i].weight; - } - } else { - if (weight <= rules1[i].weight) { - found = CRMatch::CR_MATCH_YES; - weight = rules1[i].weight; - } - } - } - } - return found; -} - -CodeSignCodeResources::CRMatch CodeSignCodeResources::match_rules2(const String &p_path) const { - CRMatch found = CRMatch::CR_MATCH_NO; - int weight = 0; - for (int i = 0; i < rules2.size(); i++) { - RegEx regex = RegEx(rules2[i].file_pattern); - if (regex.search(p_path).is_valid()) { - if (rules2[i].key == "omit") { - return CRMatch::CR_MATCH_NO; - } else if (rules2[i].key == "nested") { - if (weight <= rules2[i].weight) { - found = CRMatch::CR_MATCH_NESTED; - weight = rules2[i].weight; - } - } else if (rules2[i].key == "optional") { - if (weight <= rules2[i].weight) { - found = CRMatch::CR_MATCH_OPTIONAL; - weight = rules2[i].weight; - } - } else { - if (weight <= rules2[i].weight) { - found = CRMatch::CR_MATCH_YES; - weight = rules2[i].weight; - } - } - } - } - return found; -} - -bool CodeSignCodeResources::add_file1(const String &p_root, const String &p_path) { - CRMatch found = match_rules1(p_path); - if (found != CRMatch::CR_MATCH_YES && found != CRMatch::CR_MATCH_OPTIONAL) { - return true; // No match. - } - - CRFile f; - f.name = p_path; - f.optional = (found == CRMatch::CR_MATCH_OPTIONAL); - f.nested = false; - f.hash = hash_sha1_base64(p_root.plus_file(p_path)); - print_verbose(vformat("CodeSign/CodeResources: File(V1) %s hash1:%s", f.name, f.hash)); - - files1.push_back(f); - return true; -} - -bool CodeSignCodeResources::add_file2(const String &p_root, const String &p_path) { - CRMatch found = match_rules2(p_path); - if (found == CRMatch::CR_MATCH_NESTED) { - return add_nested_file(p_root, p_path, p_root.plus_file(p_path)); - } - if (found != CRMatch::CR_MATCH_YES && found != CRMatch::CR_MATCH_OPTIONAL) { - return true; // No match. - } - - CRFile f; - f.name = p_path; - f.optional = (found == CRMatch::CR_MATCH_OPTIONAL); - f.nested = false; - f.hash = hash_sha1_base64(p_root.plus_file(p_path)); - f.hash2 = hash_sha256_base64(p_root.plus_file(p_path)); - - print_verbose(vformat("CodeSign/CodeResources: File(V2) %s hash1:%s hash2:%s", f.name, f.hash, f.hash2)); - - files2.push_back(f); - return true; -} - -bool CodeSignCodeResources::add_nested_file(const String &p_root, const String &p_path, const String &p_exepath) { -#define CLEANUP() \ - if (files_to_add.size() > 1) { \ - for (int j = 0; j < files_to_add.size(); j++) { \ - da->remove(files_to_add[j]); \ - } \ - } - - Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - ERR_FAIL_COND_V(da.is_null(), false); - - Vector<String> files_to_add; - if (LipO::is_lipo(p_exepath)) { - String tmp_path_name = EditorPaths::get_singleton()->get_cache_dir().plus_file("_lipo"); - Error err = da->make_dir_recursive(tmp_path_name); - ERR_FAIL_COND_V_MSG(err != OK, false, vformat("CodeSign/CodeResources: Failed to create \"%s\" subfolder.", tmp_path_name)); - LipO lip; - if (lip.open_file(p_exepath)) { - for (int i = 0; i < lip.get_arch_count(); i++) { - if (!lip.extract_arch(i, tmp_path_name.plus_file("_rqexe_" + itos(i)))) { - CLEANUP(); - ERR_FAIL_V_MSG(false, "CodeSign/CodeResources: Failed to extract thin binary."); - } - files_to_add.push_back(tmp_path_name.plus_file("_rqexe_" + itos(i))); - } - } - } else if (MachO::is_macho(p_exepath)) { - files_to_add.push_back(p_exepath); - } - - CRFile f; - f.name = p_path; - f.optional = false; - f.nested = true; - for (int i = 0; i < files_to_add.size(); i++) { - MachO mh; - if (!mh.open_file(files_to_add[i])) { - CLEANUP(); - ERR_FAIL_V_MSG(false, "CodeSign/CodeResources: Invalid executable file."); - } - PackedByteArray hash = mh.get_cdhash_sha256(); // Use SHA-256 variant, if available. - if (hash.size() != 0x20) { - hash = mh.get_cdhash_sha1(); // Use SHA-1 instead. - if (hash.size() != 0x14) { - CLEANUP(); - ERR_FAIL_V_MSG(false, "CodeSign/CodeResources: Unsigned nested executable file."); - } - } - hash.resize(0x14); // Always clamp to 0x14 size. - f.hash = CryptoCore::b64_encode_str(hash.ptr(), hash.size()); - - PackedByteArray rq_blob = mh.get_requirements(); - String req_string; - if (rq_blob.size() > 8) { - CodeSignRequirements rq = CodeSignRequirements(rq_blob); - Vector<String> rqs = rq.parse_requirements(); - for (int j = 0; j < rqs.size(); j++) { - if (rqs[j].begins_with("designated => ")) { - req_string = rqs[j].replace("designated => ", ""); - } - } - } - if (req_string.is_empty()) { - req_string = "cdhash H\"" + String::hex_encode_buffer(hash.ptr(), hash.size()) + "\""; - } - print_verbose(vformat("CodeSign/CodeResources: Nested object %s (cputype: %d) cdhash:%s designated rq:%s", f.name, mh.get_cputype(), f.hash, req_string)); - if (f.requirements != req_string) { - if (i != 0) { - f.requirements += " or "; - } - f.requirements += req_string; - } - } - files2.push_back(f); - - CLEANUP(); - return true; - -#undef CLEANUP -} - -bool CodeSignCodeResources::add_folder_recursive(const String &p_root, const String &p_path, const String &p_main_exe_path) { - Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - ERR_FAIL_COND_V(da.is_null(), false); - Error err = da->change_dir(p_root.plus_file(p_path)); - ERR_FAIL_COND_V(err != OK, false); - - bool ret = true; - da->list_dir_begin(); - String n = da->get_next(); - while (n != String()) { - if (n != "." && n != "..") { - String path = p_root.plus_file(p_path).plus_file(n); - if (path == p_main_exe_path) { - n = da->get_next(); - continue; // Skip main executable. - } - if (da->current_is_dir()) { - CRMatch found = match_rules2(p_path.plus_file(n)); - String fmw_ver = "Current"; // Framework version (default). - String info_path; - String main_exe; - bool bundle = false; - if (da->file_exists(path.plus_file("Contents/Info.plist"))) { - info_path = path.plus_file("Contents/Info.plist"); - main_exe = path.plus_file("Contents/MacOS"); - bundle = true; - } else if (da->file_exists(path.plus_file(vformat("Versions/%s/Resources/Info.plist", fmw_ver)))) { - info_path = path.plus_file(vformat("Versions/%s/Resources/Info.plist", fmw_ver)); - main_exe = path.plus_file(vformat("Versions/%s", fmw_ver)); - bundle = true; - } else if (da->file_exists(path.plus_file("Info.plist"))) { - info_path = path.plus_file("Info.plist"); - main_exe = path; - bundle = true; - } - if (bundle && found == CRMatch::CR_MATCH_NESTED && !info_path.is_empty()) { - // Read Info.plist. - PList info_plist; - if (info_plist.load_file(info_path)) { - if (info_plist.get_root()->data_type == PList::PLNodeType::PL_NODE_TYPE_DICT && info_plist.get_root()->data_dict.has("CFBundleExecutable")) { - main_exe = main_exe.plus_file(String::utf8(info_plist.get_root()->data_dict["CFBundleExecutable"]->data_string.get_data())); - } else { - ERR_FAIL_V_MSG(false, "CodeSign/CodeResources: Invalid Info.plist, no exe name."); - } - } else { - ERR_FAIL_V_MSG(false, "CodeSign/CodeResources: Invalid Info.plist, can't load."); - } - ret = ret && add_nested_file(p_root, p_path.plus_file(n), main_exe); - } else { - ret = ret && add_folder_recursive(p_root, p_path.plus_file(n), p_main_exe_path); - } - } else { - ret = ret && add_file1(p_root, p_path.plus_file(n)); - ret = ret && add_file2(p_root, p_path.plus_file(n)); - } - } - - n = da->get_next(); - } - - da->list_dir_end(); - return ret; -} - -bool CodeSignCodeResources::save_to_file(const String &p_path) { - PList pl; - - print_verbose(vformat("CodeSign/CodeResources: Writing to file: %s", p_path)); - - // Write version 1 hashes. - Ref<PListNode> files1_dict = PListNode::new_dict(); - pl.get_root()->push_subnode(files1_dict, "files"); - for (int i = 0; i < files1.size(); i++) { - if (files1[i].optional) { - Ref<PListNode> file_dict = PListNode::new_dict(); - files1_dict->push_subnode(file_dict, files1[i].name); - - file_dict->push_subnode(PListNode::new_data(files1[i].hash), "hash"); - file_dict->push_subnode(PListNode::new_bool(true), "optional"); - } else { - files1_dict->push_subnode(PListNode::new_data(files1[i].hash), files1[i].name); - } - } - - // Write version 2 hashes. - Ref<PListNode> files2_dict = PListNode::new_dict(); - pl.get_root()->push_subnode(files2_dict, "files2"); - for (int i = 0; i < files2.size(); i++) { - Ref<PListNode> file_dict = PListNode::new_dict(); - files2_dict->push_subnode(file_dict, files2[i].name); - - if (files2[i].nested) { - file_dict->push_subnode(PListNode::new_data(files2[i].hash), "cdhash"); - file_dict->push_subnode(PListNode::new_string(files2[i].requirements), "requirement"); - } else { - file_dict->push_subnode(PListNode::new_data(files2[i].hash), "hash"); - file_dict->push_subnode(PListNode::new_data(files2[i].hash2), "hash2"); - if (files2[i].optional) { - file_dict->push_subnode(PListNode::new_bool(true), "optional"); - } - } - } - - // Write version 1 rules. - Ref<PListNode> rules1_dict = PListNode::new_dict(); - pl.get_root()->push_subnode(rules1_dict, "rules"); - for (int i = 0; i < rules1.size(); i++) { - if (rules1[i].store) { - if (rules1[i].key.is_empty() && rules1[i].weight <= 0) { - rules1_dict->push_subnode(PListNode::new_bool(true), rules1[i].file_pattern); - } else { - Ref<PListNode> rule_dict = PListNode::new_dict(); - rules1_dict->push_subnode(rule_dict, rules1[i].file_pattern); - if (!rules1[i].key.is_empty()) { - rule_dict->push_subnode(PListNode::new_bool(true), rules1[i].key); - } - if (rules1[i].weight != 1) { - rule_dict->push_subnode(PListNode::new_real(rules1[i].weight), "weight"); - } - } - } - } - - // Write version 2 rules. - Ref<PListNode> rules2_dict = PListNode::new_dict(); - pl.get_root()->push_subnode(rules2_dict, "rules2"); - for (int i = 0; i < rules2.size(); i++) { - if (rules2[i].store) { - if (rules2[i].key.is_empty() && rules2[i].weight <= 0) { - rules2_dict->push_subnode(PListNode::new_bool(true), rules2[i].file_pattern); - } else { - Ref<PListNode> rule_dict = PListNode::new_dict(); - rules2_dict->push_subnode(rule_dict, rules2[i].file_pattern); - if (!rules2[i].key.is_empty()) { - rule_dict->push_subnode(PListNode::new_bool(true), rules2[i].key); - } - if (rules2[i].weight != 1) { - rule_dict->push_subnode(PListNode::new_real(rules2[i].weight), "weight"); - } - } - } - } - String text = pl.save_text(); - ERR_FAIL_COND_V_MSG(text.is_empty(), false, "CodeSign/CodeResources: Generating resources PList failed."); - - Ref<FileAccess> fa = FileAccess::open(p_path, FileAccess::WRITE); - ERR_FAIL_COND_V_MSG(fa.is_null(), false, vformat("CodeSign/CodeResources: Can't open file: \"%s\".", p_path)); - - CharString cs = text.utf8(); - fa->store_buffer((const uint8_t *)cs.ptr(), cs.length()); - return true; -} - -/*************************************************************************/ -/* CodeSignRequirements */ -/*************************************************************************/ - -CodeSignRequirements::CodeSignRequirements() { - blob.append_array({ 0xFA, 0xDE, 0x0C, 0x01 }); // Requirement set magic. - blob.append_array({ 0x00, 0x00, 0x00, 0x0C }); // Length of requirements set (12 bytes). - blob.append_array({ 0x00, 0x00, 0x00, 0x00 }); // Empty. -} - -CodeSignRequirements::CodeSignRequirements(const PackedByteArray &p_data) { - blob = p_data; -} - -_FORCE_INLINE_ void CodeSignRequirements::_parse_certificate_slot(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const { -#define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x)) - ERR_FAIL_COND_MSG(r_pos >= p_rq_size, "CodeSign/Requirements: Out of bounds."); - r_out += "certificate "; - uint32_t tag_slot = _R(r_pos); - if (tag_slot == 0x00000000) { - r_out += "leaf"; - } else if (tag_slot == 0xffffffff) { - r_out += "root"; - } else { - r_out += itos((int32_t)tag_slot); - } - r_pos += 4; -#undef _R -} - -_FORCE_INLINE_ void CodeSignRequirements::_parse_key(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const { -#define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x)) - ERR_FAIL_COND_MSG(r_pos >= p_rq_size, "CodeSign/Requirements: Out of bounds."); - uint32_t key_size = _R(r_pos); - ERR_FAIL_COND_MSG(r_pos + key_size > p_rq_size, "CodeSign/Requirements: Out of bounds."); - CharString key; - key.resize(key_size); - memcpy(key.ptrw(), blob.ptr() + r_pos + 4, key_size); - r_pos += 4 + key_size + PAD(key_size, 4); - r_out += "[" + String::utf8(key, key_size) + "]"; -#undef _R -} - -_FORCE_INLINE_ void CodeSignRequirements::_parse_oid_key(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const { -#define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x)) - ERR_FAIL_COND_MSG(r_pos >= p_rq_size, "CodeSign/Requirements: Out of bounds."); - uint32_t key_size = _R(r_pos); - ERR_FAIL_COND_MSG(r_pos + key_size > p_rq_size, "CodeSign/Requirements: Out of bounds."); - r_out += "[field."; - r_out += itos(blob[r_pos + 4] / 40) + "."; - r_out += itos(blob[r_pos + 4] % 40); - uint32_t spos = r_pos + 5; - while (spos < r_pos + 4 + key_size) { - r_out += "."; - if (blob[spos] <= 127) { - r_out += itos(blob[spos]); - spos += 1; - } else { - uint32_t x = (0x7F & blob[spos]) << 7; - spos += 1; - while (blob[spos] > 127) { - x = (x + (0x7F & blob[spos])) << 7; - spos += 1; - } - x = (x + (0x7F & blob[spos])); - r_out += itos(x); - spos += 1; - } - } - r_out += "]"; - r_pos += 4 + key_size + PAD(key_size, 4); -#undef _R -} - -_FORCE_INLINE_ void CodeSignRequirements::_parse_hash_string(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const { -#define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x)) - ERR_FAIL_COND_MSG(r_pos >= p_rq_size, "CodeSign/Requirements: Out of bounds."); - uint32_t tag_size = _R(r_pos); - ERR_FAIL_COND_MSG(r_pos + tag_size > p_rq_size, "CodeSign/Requirements: Out of bounds."); - PackedByteArray data; - data.resize(tag_size); - memcpy(data.ptrw(), blob.ptr() + r_pos + 4, tag_size); - r_out += "H\"" + String::hex_encode_buffer(data.ptr(), data.size()) + "\""; - r_pos += 4 + tag_size + PAD(tag_size, 4); -#undef _R -} - -_FORCE_INLINE_ void CodeSignRequirements::_parse_value(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const { -#define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x)) - ERR_FAIL_COND_MSG(r_pos >= p_rq_size, "CodeSign/Requirements: Out of bounds."); - uint32_t key_size = _R(r_pos); - ERR_FAIL_COND_MSG(r_pos + key_size > p_rq_size, "CodeSign/Requirements: Out of bounds."); - CharString key; - key.resize(key_size); - memcpy(key.ptrw(), blob.ptr() + r_pos + 4, key_size); - r_pos += 4 + key_size + PAD(key_size, 4); - r_out += "\"" + String::utf8(key, key_size) + "\""; -#undef _R -} - -_FORCE_INLINE_ void CodeSignRequirements::_parse_date(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const { -#define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x)) - ERR_FAIL_COND_MSG(r_pos >= p_rq_size, "CodeSign/Requirements: Out of bounds."); - uint32_t date = _R(r_pos); - time_t t = 978307200 + date; - struct tm lt; -#ifdef WINDOWS_ENABLED - gmtime_s(<, &t); -#else - gmtime_r(&t, <); -#endif - r_out += vformat("<%04d-%02d-%02d ", (int)(1900 + lt.tm_year), (int)(lt.tm_mon + 1), (int)(lt.tm_mday)) + vformat("%02d:%02d:%02d +0000>", (int)(lt.tm_hour), (int)(lt.tm_min), (int)(lt.tm_sec)); -#undef _R -} - -_FORCE_INLINE_ bool CodeSignRequirements::_parse_match(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const { -#define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x)) - ERR_FAIL_COND_V_MSG(r_pos >= p_rq_size, false, "CodeSign/Requirements: Out of bounds."); - uint32_t match = _R(r_pos); - r_pos += 4; - switch (match) { - case 0x00000000: { - r_out += "exists"; - } break; - case 0x00000001: { - r_out += "= "; - _parse_value(r_pos, r_out, p_rq_size); - } break; - case 0x00000002: { - r_out += "~ "; - _parse_value(r_pos, r_out, p_rq_size); - } break; - case 0x00000003: { - r_out += "= *"; - _parse_value(r_pos, r_out, p_rq_size); - } break; - case 0x00000004: { - r_out += "= "; - _parse_value(r_pos, r_out, p_rq_size); - r_out += "*"; - } break; - case 0x00000005: { - r_out += "< "; - _parse_value(r_pos, r_out, p_rq_size); - } break; - case 0x00000006: { - r_out += "> "; - _parse_value(r_pos, r_out, p_rq_size); - } break; - case 0x00000007: { - r_out += "<= "; - _parse_value(r_pos, r_out, p_rq_size); - } break; - case 0x00000008: { - r_out += ">= "; - _parse_value(r_pos, r_out, p_rq_size); - } break; - case 0x00000009: { - r_out += "= "; - _parse_date(r_pos, r_out, p_rq_size); - } break; - case 0x0000000A: { - r_out += "< "; - _parse_date(r_pos, r_out, p_rq_size); - } break; - case 0x0000000B: { - r_out += "> "; - _parse_date(r_pos, r_out, p_rq_size); - } break; - case 0x0000000C: { - r_out += "<= "; - _parse_date(r_pos, r_out, p_rq_size); - } break; - case 0x0000000D: { - r_out += ">= "; - _parse_date(r_pos, r_out, p_rq_size); - } break; - case 0x0000000E: { - r_out += "absent"; - } break; - default: { - return false; - } - } - return true; -#undef _R -} - -Vector<String> CodeSignRequirements::parse_requirements() const { -#define _R(x) BSWAP32(*(uint32_t *)(blob.ptr() + x)) - Vector<String> list; - - // Read requirements set header. - ERR_FAIL_COND_V_MSG(blob.size() < 12, list, "CodeSign/Requirements: Blob is too small."); - uint32_t magic = _R(0); - ERR_FAIL_COND_V_MSG(magic != 0xfade0c01, list, "CodeSign/Requirements: Invalid set magic."); - uint32_t size = _R(4); - ERR_FAIL_COND_V_MSG(size != (uint32_t)blob.size(), list, "CodeSign/Requirements: Invalid set size."); - uint32_t count = _R(8); - - for (uint32_t i = 0; i < count; i++) { - String out; - - // Read requirement header. - uint32_t rq_type = _R(12 + i * 8); - uint32_t rq_offset = _R(12 + i * 8 + 4); - ERR_FAIL_COND_V_MSG(rq_offset + 12 >= (uint32_t)blob.size(), list, "CodeSign/Requirements: Invalid requirement offset."); - switch (rq_type) { - case 0x00000001: { - out += "host => "; - } break; - case 0x00000002: { - out += "guest => "; - } break; - case 0x00000003: { - out += "designated => "; - } break; - case 0x00000004: { - out += "library => "; - } break; - case 0x00000005: { - out += "plugin => "; - } break; - default: { - ERR_FAIL_V_MSG(list, "CodeSign/Requirements: Invalid requirement type."); - } - } - uint32_t rq_magic = _R(rq_offset); - uint32_t rq_size = _R(rq_offset + 4); - uint32_t rq_ver = _R(rq_offset + 8); - uint32_t pos = rq_offset + 12; - ERR_FAIL_COND_V_MSG(rq_magic != 0xfade0c00, list, "CodeSign/Requirements: Invalid requirement magic."); - ERR_FAIL_COND_V_MSG(rq_ver != 0x00000001, list, "CodeSign/Requirements: Invalid requirement version."); - - // Read requirement tokens. - List<String> tokens; - while (pos < rq_offset + rq_size) { - uint32_t rq_tag = _R(pos); - pos += 4; - String token; - switch (rq_tag) { - case 0x00000000: { - token = "false"; - } break; - case 0x00000001: { - token = "true"; - } break; - case 0x00000002: { - token = "identifier "; - _parse_value(pos, token, rq_offset + rq_size); - } break; - case 0x00000003: { - token = "anchor apple"; - } break; - case 0x00000004: { - _parse_certificate_slot(pos, token, rq_offset + rq_size); - token += " "; - _parse_hash_string(pos, token, rq_offset + rq_size); - } break; - case 0x00000005: { - token = "info"; - _parse_key(pos, token, rq_offset + rq_size); - token += " = "; - _parse_value(pos, token, rq_offset + rq_size); - } break; - case 0x00000006: { - token = "and"; - } break; - case 0x00000007: { - token = "or"; - } break; - case 0x00000008: { - token = "cdhash "; - _parse_hash_string(pos, token, rq_offset + rq_size); - } break; - case 0x00000009: { - token = "!"; - } break; - case 0x0000000A: { - token = "info"; - _parse_key(pos, token, rq_offset + rq_size); - token += " "; - ERR_FAIL_COND_V_MSG(!_parse_match(pos, token, rq_offset + rq_size), list, "CodeSign/Requirements: Unsupported match suffix."); - } break; - case 0x0000000B: { - _parse_certificate_slot(pos, token, rq_offset + rq_size); - _parse_key(pos, token, rq_offset + rq_size); - token += " "; - ERR_FAIL_COND_V_MSG(!_parse_match(pos, token, rq_offset + rq_size), list, "CodeSign/Requirements: Unsupported match suffix."); - } break; - case 0x0000000C: { - _parse_certificate_slot(pos, token, rq_offset + rq_size); - token += " trusted"; - } break; - case 0x0000000D: { - token = "anchor trusted"; - } break; - case 0x0000000E: { - _parse_certificate_slot(pos, token, rq_offset + rq_size); - _parse_oid_key(pos, token, rq_offset + rq_size); - token += " "; - ERR_FAIL_COND_V_MSG(!_parse_match(pos, token, rq_offset + rq_size), list, "CodeSign/Requirements: Unsupported match suffix."); - } break; - case 0x0000000F: { - token = "anchor apple generic"; - } break; - default: { - ERR_FAIL_V_MSG(list, "CodeSign/Requirements: Invalid requirement token."); - } break; - } - tokens.push_back(token); - } - - // Polish to infix notation (w/o bracket optimization). - for (List<String>::Element *E = tokens.back(); E; E = E->prev()) { - if (E->get() == "and") { - ERR_FAIL_COND_V_MSG(!E->next() || !E->next()->next(), list, "CodeSign/Requirements: Invalid token sequence."); - String token = "(" + E->next()->get() + " and " + E->next()->next()->get() + ")"; - tokens.erase(E->next()->next()); - tokens.erase(E->next()); - E->get() = token; - } else if (E->get() == "or") { - ERR_FAIL_COND_V_MSG(!E->next() || !E->next()->next(), list, "CodeSign/Requirements: Invalid token sequence."); - String token = "(" + E->next()->get() + " or " + E->next()->next()->get() + ")"; - tokens.erase(E->next()->next()); - tokens.erase(E->next()); - E->get() = token; - } - } - - if (tokens.size() == 1) { - list.push_back(out + tokens.front()->get()); - } else { - ERR_FAIL_V_MSG(list, "CodeSign/Requirements: Invalid token sequence."); - } - } - - return list; -#undef _R -} - -PackedByteArray CodeSignRequirements::get_hash_sha1() const { - PackedByteArray hash; - hash.resize(0x14); - - CryptoCore::SHA1Context ctx; - ctx.start(); - ctx.update(blob.ptr(), blob.size()); - ctx.finish(hash.ptrw()); - - return hash; -} - -PackedByteArray CodeSignRequirements::get_hash_sha256() const { - PackedByteArray hash; - hash.resize(0x20); - - CryptoCore::SHA256Context ctx; - ctx.start(); - ctx.update(blob.ptr(), blob.size()); - ctx.finish(hash.ptrw()); - - return hash; -} - -int CodeSignRequirements::get_size() const { - return blob.size(); -} - -void CodeSignRequirements::write_to_file(Ref<FileAccess> p_file) const { - ERR_FAIL_COND_MSG(p_file.is_null(), "CodeSign/Requirements: Invalid file handle."); - p_file->store_buffer(blob.ptr(), blob.size()); -} - -/*************************************************************************/ -/* CodeSignEntitlementsText */ -/*************************************************************************/ - -CodeSignEntitlementsText::CodeSignEntitlementsText() { - blob.append_array({ 0xFA, 0xDE, 0x71, 0x71 }); // Text Entitlements set magic. - blob.append_array({ 0x00, 0x00, 0x00, 0x08 }); // Length (8 bytes). -} - -CodeSignEntitlementsText::CodeSignEntitlementsText(const String &p_string) { - CharString utf8 = p_string.utf8(); - blob.append_array({ 0xFA, 0xDE, 0x71, 0x71 }); // Text Entitlements set magic. - for (int i = 3; i >= 0; i--) { - uint8_t x = ((utf8.length() + 8) >> i * 8) & 0xFF; // Size. - blob.push_back(x); - } - for (int i = 0; i < utf8.length(); i++) { // Write data. - blob.push_back(utf8[i]); - } -} - -PackedByteArray CodeSignEntitlementsText::get_hash_sha1() const { - PackedByteArray hash; - hash.resize(0x14); - - CryptoCore::SHA1Context ctx; - ctx.start(); - ctx.update(blob.ptr(), blob.size()); - ctx.finish(hash.ptrw()); - - return hash; -} - -PackedByteArray CodeSignEntitlementsText::get_hash_sha256() const { - PackedByteArray hash; - hash.resize(0x20); - - CryptoCore::SHA256Context ctx; - ctx.start(); - ctx.update(blob.ptr(), blob.size()); - ctx.finish(hash.ptrw()); - - return hash; -} - -int CodeSignEntitlementsText::get_size() const { - return blob.size(); -} - -void CodeSignEntitlementsText::write_to_file(Ref<FileAccess> p_file) const { - ERR_FAIL_COND_MSG(p_file.is_null(), "CodeSign/EntitlementsText: Invalid file handle."); - p_file->store_buffer(blob.ptr(), blob.size()); -} - -/*************************************************************************/ -/* CodeSignEntitlementsBinary */ -/*************************************************************************/ - -CodeSignEntitlementsBinary::CodeSignEntitlementsBinary() { - blob.append_array({ 0xFA, 0xDE, 0x71, 0x72 }); // Binary Entitlements magic. - blob.append_array({ 0x00, 0x00, 0x00, 0x08 }); // Length (8 bytes). -} - -CodeSignEntitlementsBinary::CodeSignEntitlementsBinary(const String &p_string) { - PList pl = PList(p_string); - - PackedByteArray asn1 = pl.save_asn1(); - blob.append_array({ 0xFA, 0xDE, 0x71, 0x72 }); // Binary Entitlements magic. - uint32_t size = asn1.size() + 8; - for (int i = 3; i >= 0; i--) { - uint8_t x = (size >> i * 8) & 0xFF; // Size. - blob.push_back(x); - } - blob.append_array(asn1); // Write data. -} - -PackedByteArray CodeSignEntitlementsBinary::get_hash_sha1() const { - PackedByteArray hash; - hash.resize(0x14); - - CryptoCore::SHA1Context ctx; - ctx.start(); - ctx.update(blob.ptr(), blob.size()); - ctx.finish(hash.ptrw()); - - return hash; -} - -PackedByteArray CodeSignEntitlementsBinary::get_hash_sha256() const { - PackedByteArray hash; - hash.resize(0x20); - - CryptoCore::SHA256Context ctx; - ctx.start(); - ctx.update(blob.ptr(), blob.size()); - ctx.finish(hash.ptrw()); - - return hash; -} - -int CodeSignEntitlementsBinary::get_size() const { - return blob.size(); -} - -void CodeSignEntitlementsBinary::write_to_file(Ref<FileAccess> p_file) const { - ERR_FAIL_COND_MSG(p_file.is_null(), "CodeSign/EntitlementsBinary: Invalid file handle."); - p_file->store_buffer(blob.ptr(), blob.size()); -} - -/*************************************************************************/ -/* CodeSignCodeDirectory */ -/*************************************************************************/ - -CodeSignCodeDirectory::CodeSignCodeDirectory() { - blob.append_array({ 0xFA, 0xDE, 0x0C, 0x02 }); // Code Directory magic. - blob.append_array({ 0x00, 0x00, 0x00, 0x00 }); // Size (8 bytes). -} - -CodeSignCodeDirectory::CodeSignCodeDirectory(uint8_t p_hash_size, uint8_t p_hash_type, bool p_main, const CharString &p_id, const CharString &p_team_id, uint32_t p_page_size, uint64_t p_exe_limit, uint64_t p_code_limit) { - pages = p_code_limit / (uint64_t(1) << p_page_size); - remain = p_code_limit % (uint64_t(1) << p_page_size); - code_slots = pages + (remain > 0 ? 1 : 0); - special_slots = 7; - - int cd_size = 8 + sizeof(CodeDirectoryHeader) + (code_slots + special_slots) * p_hash_size + p_id.size() + p_team_id.size(); - int cd_off = 8 + sizeof(CodeDirectoryHeader); - blob.append_array({ 0xFA, 0xDE, 0x0C, 0x02 }); // Code Directory magic. - for (int i = 3; i >= 0; i--) { - uint8_t x = (cd_size >> i * 8) & 0xFF; // Size. - blob.push_back(x); - } - blob.resize(cd_size); - memset(blob.ptrw() + 8, 0x00, cd_size - 8); - CodeDirectoryHeader *cd = reinterpret_cast<CodeDirectoryHeader *>(blob.ptrw() + 8); - - bool is_64_cl = (p_code_limit >= std::numeric_limits<uint32_t>::max()); - - // Version and options. - cd->version = BSWAP32(0x20500); - cd->flags = BSWAP32(SIGNATURE_ADHOC | SIGNATURE_RUNTIME); - cd->special_slots = BSWAP32(special_slots); - cd->code_slots = BSWAP32(code_slots); - if (is_64_cl) { - cd->code_limit_64 = BSWAP64(p_code_limit); - } else { - cd->code_limit = BSWAP32(p_code_limit); - } - cd->hash_size = p_hash_size; - cd->hash_type = p_hash_type; - cd->page_size = p_page_size; - cd->exec_seg_base = 0x00; - cd->exec_seg_limit = BSWAP64(p_exe_limit); - cd->exec_seg_flags = 0; - if (p_main) { - cd->exec_seg_flags |= EXECSEG_MAIN_BINARY; - } - cd->exec_seg_flags = BSWAP64(cd->exec_seg_flags); - uint32_t version = (11 << 16) + (3 << 8) + 0; // Version 11.3.0 - cd->runtime = BSWAP32(version); - - // Copy ID. - cd->ident_offset = BSWAP32(cd_off); - memcpy(blob.ptrw() + cd_off, p_id.get_data(), p_id.size()); - cd_off += p_id.size(); - - // Copy Team ID. - if (p_team_id.length() > 0) { - cd->team_offset = BSWAP32(cd_off); - memcpy(blob.ptrw() + cd_off, p_team_id.get_data(), p_team_id.size()); - cd_off += p_team_id.size(); - } else { - cd->team_offset = 0; - } - - // Scatter vector. - cd->scatter_vector_offset = 0; // Not used. - - // Executable hashes offset. - cd->hash_offset = BSWAP32(cd_off + special_slots * cd->hash_size); -} - -bool CodeSignCodeDirectory::set_hash_in_slot(const PackedByteArray &p_hash, int p_slot) { - ERR_FAIL_COND_V_MSG((p_slot < -special_slots) || (p_slot >= code_slots), false, vformat("CodeSign/CodeDirectory: Invalid hash slot index: %d.", p_slot)); - CodeDirectoryHeader *cd = reinterpret_cast<CodeDirectoryHeader *>(blob.ptrw() + 8); - for (int i = 0; i < cd->hash_size; i++) { - blob.write[BSWAP32(cd->hash_offset) + p_slot * cd->hash_size + i] = p_hash[i]; - } - return true; -} - -int32_t CodeSignCodeDirectory::get_page_count() { - return pages; -} - -int32_t CodeSignCodeDirectory::get_page_remainder() { - return remain; -} - -PackedByteArray CodeSignCodeDirectory::get_hash_sha1() const { - PackedByteArray hash; - hash.resize(0x14); - - CryptoCore::SHA1Context ctx; - ctx.start(); - ctx.update(blob.ptr(), blob.size()); - ctx.finish(hash.ptrw()); - - return hash; -} - -PackedByteArray CodeSignCodeDirectory::get_hash_sha256() const { - PackedByteArray hash; - hash.resize(0x20); - - CryptoCore::SHA256Context ctx; - ctx.start(); - ctx.update(blob.ptr(), blob.size()); - ctx.finish(hash.ptrw()); - - return hash; -} - -int CodeSignCodeDirectory::get_size() const { - return blob.size(); -} - -void CodeSignCodeDirectory::write_to_file(Ref<FileAccess> p_file) const { - ERR_FAIL_COND_MSG(p_file.is_null(), "CodeSign/CodeDirectory: Invalid file handle."); - p_file->store_buffer(blob.ptr(), blob.size()); -} - -/*************************************************************************/ -/* CodeSignSignature */ -/*************************************************************************/ - -CodeSignSignature::CodeSignSignature() { - blob.append_array({ 0xFA, 0xDE, 0x0B, 0x01 }); // Signature magic. - uint32_t sign_size = 8; // Ad-hoc signature is empty. - for (int i = 3; i >= 0; i--) { - uint8_t x = (sign_size >> i * 8) & 0xFF; // Size. - blob.push_back(x); - } -} - -PackedByteArray CodeSignSignature::get_hash_sha1() const { - PackedByteArray hash; - hash.resize(0x14); - - CryptoCore::SHA1Context ctx; - ctx.start(); - ctx.update(blob.ptr(), blob.size()); - ctx.finish(hash.ptrw()); - - return hash; -} - -PackedByteArray CodeSignSignature::get_hash_sha256() const { - PackedByteArray hash; - hash.resize(0x20); - - CryptoCore::SHA256Context ctx; - ctx.start(); - ctx.update(blob.ptr(), blob.size()); - ctx.finish(hash.ptrw()); - - return hash; -} - -int CodeSignSignature::get_size() const { - return blob.size(); -} - -void CodeSignSignature::write_to_file(Ref<FileAccess> p_file) const { - ERR_FAIL_COND_MSG(p_file.is_null(), "CodeSign/Signature: Invalid file handle."); - p_file->store_buffer(blob.ptr(), blob.size()); -} - -/*************************************************************************/ -/* CodeSignSuperBlob */ -/*************************************************************************/ - -bool CodeSignSuperBlob::add_blob(const Ref<CodeSignBlob> &p_blob) { - if (p_blob.is_valid()) { - blobs.push_back(p_blob); - return true; - } else { - return false; - } -} - -int CodeSignSuperBlob::get_size() const { - int size = 12 + blobs.size() * 8; - for (int i = 0; i < blobs.size(); i++) { - if (blobs[i].is_null()) { - return 0; - } - size += blobs[i]->get_size(); - } - return size; -} - -void CodeSignSuperBlob::write_to_file(Ref<FileAccess> p_file) const { - ERR_FAIL_COND_MSG(p_file.is_null(), "CodeSign/SuperBlob: Invalid file handle."); - uint32_t size = get_size(); - uint32_t data_offset = 12 + blobs.size() * 8; - - // Write header. - p_file->store_32(BSWAP32(0xfade0cc0)); - p_file->store_32(BSWAP32(size)); - p_file->store_32(BSWAP32(blobs.size())); - - // Write index. - for (int i = 0; i < blobs.size(); i++) { - if (blobs[i].is_null()) { - return; - } - p_file->store_32(BSWAP32(blobs[i]->get_index_type())); - p_file->store_32(BSWAP32(data_offset)); - data_offset += blobs[i]->get_size(); - } - - // Write blobs. - for (int i = 0; i < blobs.size(); i++) { - blobs[i]->write_to_file(p_file); - } -} - -/*************************************************************************/ -/* CodeSign */ -/*************************************************************************/ - -PackedByteArray CodeSign::file_hash_sha1(const String &p_path) { - PackedByteArray file_hash; - Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_V_MSG(f.is_null(), PackedByteArray(), vformat("CodeSign: Can't open file: \"%s\".", p_path)); - - CryptoCore::SHA1Context ctx; - ctx.start(); - - unsigned char step[4096]; - while (true) { - uint64_t br = f->get_buffer(step, 4096); - if (br > 0) { - ctx.update(step, br); - } - if (br < 4096) { - break; - } - } - - file_hash.resize(0x14); - ctx.finish(file_hash.ptrw()); - return file_hash; -} - -PackedByteArray CodeSign::file_hash_sha256(const String &p_path) { - PackedByteArray file_hash; - Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_V_MSG(f.is_null(), PackedByteArray(), vformat("CodeSign: Can't open file: \"%s\".", p_path)); - - CryptoCore::SHA256Context ctx; - ctx.start(); - - unsigned char step[4096]; - while (true) { - uint64_t br = f->get_buffer(step, 4096); - if (br > 0) { - ctx.update(step, br); - } - if (br < 4096) { - break; - } - } - - file_hash.resize(0x20); - ctx.finish(file_hash.ptrw()); - return file_hash; -} - -Error CodeSign::_codesign_file(bool p_use_hardened_runtime, bool p_force, const String &p_info, const String &p_exe_path, const String &p_bundle_path, const String &p_ent_path, bool p_ios_bundle, String &r_error_msg) { -#define CLEANUP() \ - if (files_to_sign.size() > 1) { \ - for (int j = 0; j < files_to_sign.size(); j++) { \ - da->remove(files_to_sign[j]); \ - } \ - } - - print_verbose(vformat("CodeSign: Signing executable: %s, bundle: %s with entitlements %s", p_exe_path, p_bundle_path, p_ent_path)); - - PackedByteArray info_hash1, info_hash2; - PackedByteArray res_hash1, res_hash2; - String id; - String main_exe = p_exe_path; - - Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - if (da.is_null()) { - r_error_msg = TTR("Can't get filesystem access."); - ERR_FAIL_V_MSG(ERR_CANT_CREATE, "CodeSign: Can't get filesystem access."); - } - - // Read Info.plist. - if (!p_info.is_empty()) { - print_verbose(vformat("CodeSign: Reading bundle info...")); - PList info_plist; - if (info_plist.load_file(p_info)) { - info_hash1 = file_hash_sha1(p_info); - info_hash2 = file_hash_sha256(p_info); - if (info_hash1.is_empty() || info_hash2.is_empty()) { - r_error_msg = TTR("Failed to get Info.plist hash."); - ERR_FAIL_V_MSG(FAILED, "CodeSign: Failed to get Info.plist hash."); - } - - if (info_plist.get_root()->data_type == PList::PLNodeType::PL_NODE_TYPE_DICT && info_plist.get_root()->data_dict.has("CFBundleExecutable")) { - main_exe = p_exe_path.plus_file(String::utf8(info_plist.get_root()->data_dict["CFBundleExecutable"]->data_string.get_data())); - } else { - r_error_msg = TTR("Invalid Info.plist, no exe name."); - ERR_FAIL_V_MSG(FAILED, "CodeSign: Invalid Info.plist, no exe name."); - } - - if (info_plist.get_root()->data_type == PList::PLNodeType::PL_NODE_TYPE_DICT && info_plist.get_root()->data_dict.has("CFBundleIdentifier")) { - id = info_plist.get_root()->data_dict["CFBundleIdentifier"]->data_string.get_data(); - } else { - r_error_msg = TTR("Invalid Info.plist, no bundle id."); - ERR_FAIL_V_MSG(FAILED, "CodeSign: Invalid Info.plist, no bundle id."); - } - } else { - r_error_msg = TTR("Invalid Info.plist, can't load."); - ERR_FAIL_V_MSG(FAILED, "CodeSign: Invalid Info.plist, can't load."); - } - } - - // Extract fat binary. - Vector<String> files_to_sign; - if (LipO::is_lipo(main_exe)) { - print_verbose(vformat("CodeSign: Executable is fat, extracting...")); - String tmp_path_name = EditorPaths::get_singleton()->get_cache_dir().plus_file("_lipo"); - Error err = da->make_dir_recursive(tmp_path_name); - if (err != OK) { - r_error_msg = vformat(TTR("Failed to create \"%s\" subfolder."), tmp_path_name); - ERR_FAIL_V_MSG(FAILED, vformat("CodeSign: Failed to create \"%s\" subfolder.", tmp_path_name)); - } - LipO lip; - if (lip.open_file(main_exe)) { - for (int i = 0; i < lip.get_arch_count(); i++) { - if (!lip.extract_arch(i, tmp_path_name.plus_file("_exe_" + itos(i)))) { - CLEANUP(); - r_error_msg = TTR("Failed to extract thin binary."); - ERR_FAIL_V_MSG(FAILED, "CodeSign: Failed to extract thin binary."); - } - files_to_sign.push_back(tmp_path_name.plus_file("_exe_" + itos(i))); - } - } - } else if (MachO::is_macho(main_exe)) { - print_verbose(vformat("CodeSign: Executable is thin...")); - files_to_sign.push_back(main_exe); - } else { - r_error_msg = TTR("Invalid binary format."); - ERR_FAIL_V_MSG(FAILED, "CodeSign: Invalid binary format."); - } - - // Check if it's already signed. - if (!p_force) { - for (int i = 0; i < files_to_sign.size(); i++) { - MachO mh; - mh.open_file(files_to_sign[i]); - if (mh.is_signed()) { - CLEANUP(); - r_error_msg = TTR("Already signed!"); - ERR_FAIL_V_MSG(FAILED, "CodeSign: Already signed!"); - } - } - } - - // Generate core resources. - if (!p_bundle_path.is_empty()) { - print_verbose(vformat("CodeSign: Generating bundle CodeResources...")); - CodeSignCodeResources cr; - - if (p_ios_bundle) { - cr.add_rule1("^.*"); - cr.add_rule1("^.*\\.lproj/", "optional", 100); - cr.add_rule1("^.*\\.lproj/locversion.plist$", "omit", 1100); - cr.add_rule1("^Base\\.lproj/", "", 1010); - cr.add_rule1("^version.plist$"); - - cr.add_rule2(".*\\.dSYM($|/)", "", 11); - cr.add_rule2("^(.*/)?\\.DS_Store$", "omit", 2000); - cr.add_rule2("^.*"); - cr.add_rule2("^.*\\.lproj/", "optional", 1000); - cr.add_rule2("^.*\\.lproj/locversion.plist$", "omit", 1100); - cr.add_rule2("^Base\\.lproj/", "", 1010); - cr.add_rule2("^Info\\.plist$", "omit", 20); - cr.add_rule2("^PkgInfo$", "omit", 20); - cr.add_rule2("^embedded\\.provisionprofile$", "", 10); - cr.add_rule2("^version\\.plist$", "", 20); - - cr.add_rule2("^_MASReceipt", "omit", 2000, false); - cr.add_rule2("^_CodeSignature", "omit", 2000, false); - cr.add_rule2("^CodeResources", "omit", 2000, false); - } else { - cr.add_rule1("^Resources/"); - cr.add_rule1("^Resources/.*\\.lproj/", "optional", 1000); - cr.add_rule1("^Resources/.*\\.lproj/locversion.plist$", "omit", 1100); - cr.add_rule1("^Resources/Base\\.lproj/", "", 1010); - cr.add_rule1("^version.plist$"); - - cr.add_rule2(".*\\.dSYM($|/)", "", 11); - cr.add_rule2("^(.*/)?\\.DS_Store$", "omit", 2000); - cr.add_rule2("^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/", "nested", 10); - cr.add_rule2("^.*"); - cr.add_rule2("^Info\\.plist$", "omit", 20); - cr.add_rule2("^PkgInfo$", "omit", 20); - cr.add_rule2("^Resources/", "", 20); - cr.add_rule2("^Resources/.*\\.lproj/", "optional", 1000); - cr.add_rule2("^Resources/.*\\.lproj/locversion.plist$", "omit", 1100); - cr.add_rule2("^Resources/Base\\.lproj/", "", 1010); - cr.add_rule2("^[^/]+$", "nested", 10); - cr.add_rule2("^embedded\\.provisionprofile$", "", 10); - cr.add_rule2("^version\\.plist$", "", 20); - cr.add_rule2("^_MASReceipt", "omit", 2000, false); - cr.add_rule2("^_CodeSignature", "omit", 2000, false); - cr.add_rule2("^CodeResources", "omit", 2000, false); - } - - if (!cr.add_folder_recursive(p_bundle_path, "", main_exe)) { - CLEANUP(); - r_error_msg = TTR("Failed to process nested resources."); - ERR_FAIL_V_MSG(FAILED, "CodeSign: Failed to process nested resources."); - } - Error err = da->make_dir_recursive(p_bundle_path.plus_file("_CodeSignature")); - if (err != OK) { - CLEANUP(); - r_error_msg = TTR("Failed to create _CodeSignature subfolder."); - ERR_FAIL_V_MSG(FAILED, "CodeSign: Failed to create _CodeSignature subfolder."); - } - cr.save_to_file(p_bundle_path.plus_file("_CodeSignature").plus_file("CodeResources")); - res_hash1 = file_hash_sha1(p_bundle_path.plus_file("_CodeSignature").plus_file("CodeResources")); - res_hash2 = file_hash_sha256(p_bundle_path.plus_file("_CodeSignature").plus_file("CodeResources")); - if (res_hash1.is_empty() || res_hash2.is_empty()) { - CLEANUP(); - r_error_msg = TTR("Failed to get CodeResources hash."); - ERR_FAIL_V_MSG(FAILED, "CodeSign: Failed to get CodeResources hash."); - } - } - - // Generate common signature structures. - if (id.is_empty()) { - CryptoCore::RandomGenerator rng; - ERR_FAIL_COND_V_MSG(rng.init(), FAILED, "Failed to initialize random number generator."); - uint8_t uuid[16]; - Error err = rng.get_random_bytes(uuid, 16); - ERR_FAIL_COND_V_MSG(err, err, "Failed to generate UUID."); - id = (String("a-55554944") /*a-UUID*/ + String::hex_encode_buffer(uuid, 16)); - } - CharString uuid_str = id.utf8(); - print_verbose(vformat("CodeSign: Used bundle ID: %s", id)); - - print_verbose(vformat("CodeSign: Processing entitlements...")); - - Ref<CodeSignEntitlementsText> cet; - Ref<CodeSignEntitlementsBinary> ceb; - if (!p_ent_path.is_empty()) { - String entitlements = FileAccess::get_file_as_string(p_ent_path); - if (entitlements.is_empty()) { - CLEANUP(); - r_error_msg = TTR("Invalid entitlements file."); - ERR_FAIL_V_MSG(FAILED, "CodeSign: Invalid entitlements file."); - } - cet = Ref<CodeSignEntitlementsText>(memnew(CodeSignEntitlementsText(entitlements))); - ceb = Ref<CodeSignEntitlementsBinary>(memnew(CodeSignEntitlementsBinary(entitlements))); - } - - print_verbose(vformat("CodeSign: Generating requirements...")); - Ref<CodeSignRequirements> rq; - String team_id = ""; - rq = Ref<CodeSignRequirements>(memnew(CodeSignRequirements())); - - // Sign executables. - for (int i = 0; i < files_to_sign.size(); i++) { - MachO mh; - if (!mh.open_file(files_to_sign[i])) { - CLEANUP(); - r_error_msg = TTR("Invalid executable file."); - ERR_FAIL_V_MSG(FAILED, "CodeSign: Invalid executable file."); - } - print_verbose(vformat("CodeSign: Signing executable for cputype: %d ...", mh.get_cputype())); - - print_verbose(vformat("CodeSign: Generating CodeDirectory...")); - Ref<CodeSignCodeDirectory> cd1 = memnew(CodeSignCodeDirectory(0x14, 0x01, true, uuid_str, team_id.utf8(), 12, mh.get_exe_limit(), mh.get_code_limit())); - Ref<CodeSignCodeDirectory> cd2 = memnew(CodeSignCodeDirectory(0x20, 0x02, true, uuid_str, team_id.utf8(), 12, mh.get_exe_limit(), mh.get_code_limit())); - print_verbose(vformat("CodeSign: Calculating special slot hashes...")); - if (info_hash2.size() == 0x20) { - cd2->set_hash_in_slot(info_hash2, CodeSignCodeDirectory::SLOT_INFO_PLIST); - } - if (info_hash1.size() == 0x14) { - cd1->set_hash_in_slot(info_hash1, CodeSignCodeDirectory::SLOT_INFO_PLIST); - } - cd1->set_hash_in_slot(rq->get_hash_sha1(), CodeSignCodeDirectory::Slot::SLOT_REQUIREMENTS); - cd2->set_hash_in_slot(rq->get_hash_sha256(), CodeSignCodeDirectory::Slot::SLOT_REQUIREMENTS); - if (res_hash2.size() == 0x20) { - cd2->set_hash_in_slot(res_hash2, CodeSignCodeDirectory::SLOT_RESOURCES); - } - if (res_hash1.size() == 0x14) { - cd1->set_hash_in_slot(res_hash1, CodeSignCodeDirectory::SLOT_RESOURCES); - } - if (cet.is_valid()) { - cd1->set_hash_in_slot(cet->get_hash_sha1(), CodeSignCodeDirectory::Slot::SLOT_ENTITLEMENTS); //Text variant. - cd2->set_hash_in_slot(cet->get_hash_sha256(), CodeSignCodeDirectory::Slot::SLOT_ENTITLEMENTS); - } - if (ceb.is_valid()) { - cd1->set_hash_in_slot(ceb->get_hash_sha1(), CodeSignCodeDirectory::Slot::SLOT_DER_ENTITLEMENTS); //ASN.1 variant. - cd2->set_hash_in_slot(ceb->get_hash_sha256(), CodeSignCodeDirectory::Slot::SLOT_DER_ENTITLEMENTS); - } - - // Calculate signature size. - int sign_size = 12; // SuperBlob header. - sign_size += cd1->get_size() + 8; - sign_size += cd2->get_size() + 8; - sign_size += rq->get_size() + 8; - if (cet.is_valid()) { - sign_size += cet->get_size() + 8; - } - if (ceb.is_valid()) { - sign_size += ceb->get_size() + 8; - } - sign_size += 16; // Empty signature size. - - // Alloc/resize signature load command. - print_verbose(vformat("CodeSign: Reallocating space for the signature superblob (%d)...", sign_size)); - if (!mh.set_signature_size(sign_size)) { - CLEANUP(); - r_error_msg = TTR("Can't resize signature load command."); - ERR_FAIL_V_MSG(FAILED, "CodeSign: Can't resize signature load command."); - } - - print_verbose(vformat("CodeSign: Calculating executable code hashes...")); - // Calculate executable code hashes. - PackedByteArray buffer; - PackedByteArray hash1, hash2; - hash1.resize(0x14); - hash2.resize(0x20); - buffer.resize(1 << 12); - mh.get_file()->seek(0); - for (int32_t j = 0; j < cd2->get_page_count(); j++) { - mh.get_file()->get_buffer(buffer.ptrw(), (1 << 12)); - CryptoCore::SHA256Context ctx2; - ctx2.start(); - ctx2.update(buffer.ptr(), (1 << 12)); - ctx2.finish(hash2.ptrw()); - cd2->set_hash_in_slot(hash2, j); - - CryptoCore::SHA1Context ctx1; - ctx1.start(); - ctx1.update(buffer.ptr(), (1 << 12)); - ctx1.finish(hash1.ptrw()); - cd1->set_hash_in_slot(hash1, j); - } - if (cd2->get_page_remainder() > 0) { - mh.get_file()->get_buffer(buffer.ptrw(), cd2->get_page_remainder()); - CryptoCore::SHA256Context ctx2; - ctx2.start(); - ctx2.update(buffer.ptr(), cd2->get_page_remainder()); - ctx2.finish(hash2.ptrw()); - cd2->set_hash_in_slot(hash2, cd2->get_page_count()); - - CryptoCore::SHA1Context ctx1; - ctx1.start(); - ctx1.update(buffer.ptr(), cd1->get_page_remainder()); - ctx1.finish(hash1.ptrw()); - cd1->set_hash_in_slot(hash1, cd1->get_page_count()); - } - - print_verbose(vformat("CodeSign: Generating signature...")); - Ref<CodeSignSignature> cs; - cs = Ref<CodeSignSignature>(memnew(CodeSignSignature())); - - print_verbose(vformat("CodeSign: Writing signature superblob...")); - // Write signature data to the executable. - CodeSignSuperBlob sb = CodeSignSuperBlob(); - sb.add_blob(cd2); - sb.add_blob(cd1); - sb.add_blob(rq); - if (cet.is_valid()) { - sb.add_blob(cet); - } - if (ceb.is_valid()) { - sb.add_blob(ceb); - } - sb.add_blob(cs); - mh.get_file()->seek(mh.get_signature_offset()); - sb.write_to_file(mh.get_file()); - } - if (files_to_sign.size() > 1) { - print_verbose(vformat("CodeSign: Rebuilding fat executable...")); - LipO lip; - if (!lip.create_file(main_exe, files_to_sign)) { - CLEANUP(); - r_error_msg = TTR("Failed to create fat binary."); - ERR_FAIL_V_MSG(FAILED, "CodeSign: Failed to create fat binary."); - } - CLEANUP(); - } - FileAccess::set_unix_permissions(main_exe, 0755); // Restore unix permissions. - return OK; -#undef CLEANUP -} - -Error CodeSign::codesign(bool p_use_hardened_runtime, bool p_force, const String &p_path, const String &p_ent_path, String &r_error_msg) { - Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - if (da.is_null()) { - r_error_msg = TTR("Can't get filesystem access."); - ERR_FAIL_V_MSG(ERR_CANT_CREATE, "CodeSign: Can't get filesystem access."); - } - - if (da->dir_exists(p_path)) { - String fmw_ver = "Current"; // Framework version (default). - String info_path; - String main_exe; - String bundle_path; - bool bundle = false; - bool ios_bundle = false; - if (da->file_exists(p_path.plus_file("Contents/Info.plist"))) { - info_path = p_path.plus_file("Contents/Info.plist"); - main_exe = p_path.plus_file("Contents/MacOS"); - bundle_path = p_path.plus_file("Contents"); - bundle = true; - } else if (da->file_exists(p_path.plus_file(vformat("Versions/%s/Resources/Info.plist", fmw_ver)))) { - info_path = p_path.plus_file(vformat("Versions/%s/Resources/Info.plist", fmw_ver)); - main_exe = p_path.plus_file(vformat("Versions/%s", fmw_ver)); - bundle_path = p_path.plus_file(vformat("Versions/%s", fmw_ver)); - bundle = true; - } else if (da->file_exists(p_path.plus_file("Info.plist"))) { - info_path = p_path.plus_file("Info.plist"); - main_exe = p_path; - bundle_path = p_path; - bundle = true; - ios_bundle = true; - } - if (bundle) { - return _codesign_file(p_use_hardened_runtime, p_force, info_path, main_exe, bundle_path, p_ent_path, ios_bundle, r_error_msg); - } else { - r_error_msg = TTR("Unknown bundle type."); - ERR_FAIL_V_MSG(FAILED, "CodeSign: Unknown bundle type."); - } - } else if (da->file_exists(p_path)) { - return _codesign_file(p_use_hardened_runtime, p_force, "", p_path, "", p_ent_path, false, r_error_msg); - } else { - r_error_msg = TTR("Unknown object type."); - ERR_FAIL_V_MSG(FAILED, "CodeSign: Unknown object type."); - } -} - -#endif // MODULE_REGEX_ENABLED diff --git a/platform/osx/export/codesign.h b/platform/osx/export/codesign.h deleted file mode 100644 index 3a08c0ea86..0000000000 --- a/platform/osx/export/codesign.h +++ /dev/null @@ -1,368 +0,0 @@ -/*************************************************************************/ -/* codesign.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* 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. */ -/*************************************************************************/ - -// macOS code signature creation utility. -// -// Current implementation has the following limitation: -// - Only version 11.3.0 signatures are supported. -// - Only "framework" and "app" bundle types are supported. -// - Page hash array scattering is not supported. -// - Reading and writing binary property lists i snot supported (third-party frameworks with binary Info.plist will not work unless .plist is converted to text format). -// - Requirements code generator is not implemented (only hard-coded requirements for the ad-hoc signing is supported). -// - RFC5652/CMS blob generation is not implemented, supports ad-hoc signing only. - -#ifndef CODESIGN_H -#define CODESIGN_H - -#include "core/crypto/crypto_core.h" -#include "core/io/dir_access.h" -#include "core/io/file_access.h" -#include "core/object/ref_counted.h" - -#include "modules/modules_enabled.gen.h" // For regex. -#ifdef MODULE_REGEX_ENABLED -#include "modules/regex/regex.h" -#endif - -#include "plist.h" - -#ifdef MODULE_REGEX_ENABLED - -/*************************************************************************/ -/* CodeSignCodeResources */ -/*************************************************************************/ - -class CodeSignCodeResources { -public: - enum class CRMatch { - CR_MATCH_NO, - CR_MATCH_YES, - CR_MATCH_NESTED, - CR_MATCH_OPTIONAL, - }; - -private: - struct CRFile { - String name; - String hash; - String hash2; - bool optional; - bool nested; - String requirements; - }; - - struct CRRule { - String file_pattern; - String key; - int weight; - bool store; - CRRule() { - weight = 1; - store = true; - } - CRRule(const String &p_file_pattern, const String &p_key, int p_weight, bool p_store) { - file_pattern = p_file_pattern; - key = p_key; - weight = p_weight; - store = p_store; - } - }; - - Vector<CRRule> rules1; - Vector<CRRule> rules2; - - Vector<CRFile> files1; - Vector<CRFile> files2; - - String hash_sha1_base64(const String &p_path); - String hash_sha256_base64(const String &p_path); - -public: - void add_rule1(const String &p_rule, const String &p_key = "", int p_weight = 0, bool p_store = true); - void add_rule2(const String &p_rule, const String &p_key = "", int p_weight = 0, bool p_store = true); - - CRMatch match_rules1(const String &p_path) const; - CRMatch match_rules2(const String &p_path) const; - - bool add_file1(const String &p_root, const String &p_path); - bool add_file2(const String &p_root, const String &p_path); - bool add_nested_file(const String &p_root, const String &p_path, const String &p_exepath); - - bool add_folder_recursive(const String &p_root, const String &p_path = "", const String &p_main_exe_path = ""); - - bool save_to_file(const String &p_path); -}; - -/*************************************************************************/ -/* CodeSignBlob */ -/*************************************************************************/ - -class CodeSignBlob : public RefCounted { -public: - virtual PackedByteArray get_hash_sha1() const = 0; - virtual PackedByteArray get_hash_sha256() const = 0; - - virtual int get_size() const = 0; - virtual uint32_t get_index_type() const = 0; - - virtual void write_to_file(Ref<FileAccess> p_file) const = 0; -}; - -/*************************************************************************/ -/* CodeSignRequirements */ -/*************************************************************************/ - -// Note: Proper code generator is not implemented (any we probably won't ever need it), just a hardcoded bytecode for the limited set of cases. - -class CodeSignRequirements : public CodeSignBlob { - PackedByteArray blob; - - static inline size_t PAD(size_t s, size_t a) { - return (s % a == 0) ? 0 : (a - s % a); - } - - _FORCE_INLINE_ void _parse_certificate_slot(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const; - _FORCE_INLINE_ void _parse_key(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const; - _FORCE_INLINE_ void _parse_oid_key(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const; - _FORCE_INLINE_ void _parse_hash_string(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const; - _FORCE_INLINE_ void _parse_value(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const; - _FORCE_INLINE_ void _parse_date(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const; - _FORCE_INLINE_ bool _parse_match(uint32_t &r_pos, String &r_out, uint32_t p_rq_size) const; - -public: - CodeSignRequirements(); - CodeSignRequirements(const PackedByteArray &p_data); - - Vector<String> parse_requirements() const; - - virtual PackedByteArray get_hash_sha1() const override; - virtual PackedByteArray get_hash_sha256() const override; - - virtual int get_size() const override; - - virtual uint32_t get_index_type() const override { return 0x00000002; }; - virtual void write_to_file(Ref<FileAccess> p_file) const override; -}; - -/*************************************************************************/ -/* CodeSignEntitlementsText */ -/*************************************************************************/ - -// PList formatted entitlements. - -class CodeSignEntitlementsText : public CodeSignBlob { - PackedByteArray blob; - -public: - CodeSignEntitlementsText(); - CodeSignEntitlementsText(const String &p_string); - - virtual PackedByteArray get_hash_sha1() const override; - virtual PackedByteArray get_hash_sha256() const override; - - virtual int get_size() const override; - - virtual uint32_t get_index_type() const override { return 0x00000005; }; - virtual void write_to_file(Ref<FileAccess> p_file) const override; -}; - -/*************************************************************************/ -/* CodeSignEntitlementsBinary */ -/*************************************************************************/ - -// ASN.1 serialized entitlements. - -class CodeSignEntitlementsBinary : public CodeSignBlob { - PackedByteArray blob; - -public: - CodeSignEntitlementsBinary(); - CodeSignEntitlementsBinary(const String &p_string); - - virtual PackedByteArray get_hash_sha1() const override; - virtual PackedByteArray get_hash_sha256() const override; - - virtual int get_size() const override; - - virtual uint32_t get_index_type() const override { return 0x00000007; }; - virtual void write_to_file(Ref<FileAccess> p_file) const override; -}; - -/*************************************************************************/ -/* CodeSignCodeDirectory */ -/*************************************************************************/ - -// Code Directory, runtime options, code segment and special structure hashes. - -class CodeSignCodeDirectory : public CodeSignBlob { -public: - enum Slot { - SLOT_INFO_PLIST = -1, - SLOT_REQUIREMENTS = -2, - SLOT_RESOURCES = -3, - SLOT_APP_SPECIFIC = -4, // Unused. - SLOT_ENTITLEMENTS = -5, - SLOT_RESERVER1 = -6, // Unused. - SLOT_DER_ENTITLEMENTS = -7, - }; - - enum CodeSignExecSegFlags { - EXECSEG_MAIN_BINARY = 0x1, - EXECSEG_ALLOW_UNSIGNED = 0x10, - EXECSEG_DEBUGGER = 0x20, - EXECSEG_JIT = 0x40, - EXECSEG_SKIP_LV = 0x80, - EXECSEG_CAN_LOAD_CDHASH = 0x100, - EXECSEG_CAN_EXEC_CDHASH = 0x200, - }; - - enum CodeSignatureFlags { - SIGNATURE_HOST = 0x0001, - SIGNATURE_ADHOC = 0x0002, - SIGNATURE_TASK_ALLOW = 0x0004, - SIGNATURE_INSTALLER = 0x0008, - SIGNATURE_FORCED_LV = 0x0010, - SIGNATURE_INVALID_ALLOWED = 0x0020, - SIGNATURE_FORCE_HARD = 0x0100, - SIGNATURE_FORCE_KILL = 0x0200, - SIGNATURE_FORCE_EXPIRATION = 0x0400, - SIGNATURE_RESTRICT = 0x0800, - SIGNATURE_ENFORCEMENT = 0x1000, - SIGNATURE_LIBRARY_VALIDATION = 0x2000, - SIGNATURE_ENTITLEMENTS_VALIDATED = 0x4000, - SIGNATURE_NVRAM_UNRESTRICTED = 0x8000, - SIGNATURE_RUNTIME = 0x10000, - SIGNATURE_LINKER_SIGNED = 0x20000, - }; - -private: - PackedByteArray blob; - - struct CodeDirectoryHeader { - uint32_t version; // Using version 0x0020500. - uint32_t flags; // // Option flags. - uint32_t hash_offset; // Slot zero offset. - uint32_t ident_offset; // Identifier string offset. - uint32_t special_slots; // Nr. of slots with negative index. - uint32_t code_slots; // Nr. of slots with index >= 0, (code_limit / page_size). - uint32_t code_limit; // Everything before code signature load command offset. - uint8_t hash_size; // 20 (SHA-1) or 32 (SHA-256). - uint8_t hash_type; // 1 (SHA-1) or 2 (SHA-256). - uint8_t platform; // Not used. - uint8_t page_size; // Page size, power of two, 2^12 (4096). - uint32_t spare2; // Not used. - // Version 0x20100 - uint32_t scatter_vector_offset; // Set to 0 and ignore. - // Version 0x20200 - uint32_t team_offset; // Team id string offset. - // Version 0x20300 - uint32_t spare3; // Not used. - uint64_t code_limit_64; // Set to 0 and ignore. - // Version 0x20400 - uint64_t exec_seg_base; // Start of the signed code segmet. - uint64_t exec_seg_limit; // Code segment (__TEXT) vmsize. - uint64_t exec_seg_flags; // Executable segment flags. - // Version 0x20500 - uint32_t runtime; // Runtime version. - uint32_t pre_encrypt_offset; // Set to 0 and ignore. - }; - - int32_t pages = 0; - int32_t remain = 0; - int32_t code_slots = 0; - int32_t special_slots = 0; - -public: - CodeSignCodeDirectory(); - CodeSignCodeDirectory(uint8_t p_hash_size, uint8_t p_hash_type, bool p_main, const CharString &p_id, const CharString &p_team_id, uint32_t p_page_size, uint64_t p_exe_limit, uint64_t p_code_limit); - - int32_t get_page_count(); - int32_t get_page_remainder(); - - bool set_hash_in_slot(const PackedByteArray &p_hash, int p_slot); - - virtual PackedByteArray get_hash_sha1() const override; - virtual PackedByteArray get_hash_sha256() const override; - - virtual int get_size() const override; - virtual uint32_t get_index_type() const override { return 0x00000000; }; - - virtual void write_to_file(Ref<FileAccess> p_file) const override; -}; - -/*************************************************************************/ -/* CodeSignSignature */ -/*************************************************************************/ - -class CodeSignSignature : public CodeSignBlob { - PackedByteArray blob; - -public: - CodeSignSignature(); - - virtual PackedByteArray get_hash_sha1() const override; - virtual PackedByteArray get_hash_sha256() const override; - - virtual int get_size() const override; - virtual uint32_t get_index_type() const override { return 0x00010000; }; - - virtual void write_to_file(Ref<FileAccess> p_file) const override; -}; - -/*************************************************************************/ -/* CodeSignSuperBlob */ -/*************************************************************************/ - -class CodeSignSuperBlob { - Vector<Ref<CodeSignBlob>> blobs; - -public: - bool add_blob(const Ref<CodeSignBlob> &p_blob); - - int get_size() const; - void write_to_file(Ref<FileAccess> p_file) const; -}; - -/*************************************************************************/ -/* CodeSign */ -/*************************************************************************/ - -class CodeSign { - static PackedByteArray file_hash_sha1(const String &p_path); - static PackedByteArray file_hash_sha256(const String &p_path); - static Error _codesign_file(bool p_use_hardened_runtime, bool p_force, const String &p_info, const String &p_exe_path, const String &p_bundle_path, const String &p_ent_path, bool p_ios_bundle, String &r_error_msg); - -public: - static Error codesign(bool p_use_hardened_runtime, bool p_force, const String &p_path, const String &p_ent_path, String &r_error_msg); -}; - -#endif // MODULE_REGEX_ENABLED - -#endif // CODESIGN_H diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp deleted file mode 100644 index bd35b39e9e..0000000000 --- a/platform/osx/export/export.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/*************************************************************************/ -/* export.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "export.h" - -#include "export_plugin.h" - -void register_osx_exporter() { - EDITOR_DEF("export/macos/force_builtin_codesign", false); - EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::BOOL, "export/macos/force_builtin_codesign", PROPERTY_HINT_NONE)); - - Ref<EditorExportPlatformOSX> platform; - platform.instantiate(); - - EditorExport::get_singleton()->add_export_platform(platform); -} diff --git a/platform/osx/export/export.h b/platform/osx/export/export.h deleted file mode 100644 index b386337a09..0000000000 --- a/platform/osx/export/export.h +++ /dev/null @@ -1,36 +0,0 @@ -/*************************************************************************/ -/* export.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* 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. */ -/*************************************************************************/ - -#ifndef OSX_EXPORT_H -#define OSX_EXPORT_H - -void register_osx_exporter(); - -#endif // OSX_EXPORT_H diff --git a/platform/osx/export/export_plugin.cpp b/platform/osx/export/export_plugin.cpp deleted file mode 100644 index a22d7e5e3d..0000000000 --- a/platform/osx/export/export_plugin.cpp +++ /dev/null @@ -1,1673 +0,0 @@ -/*************************************************************************/ -/* export_plugin.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "export_plugin.h" - -#include "codesign.h" - -#include "editor/editor_node.h" -#include "editor/editor_paths.h" - -#include "modules/modules_enabled.gen.h" // For regex. - -void EditorExportPlatformOSX::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) { - if (p_preset->get("texture_format/s3tc")) { - r_features->push_back("s3tc"); - } - if (p_preset->get("texture_format/etc")) { - r_features->push_back("etc"); - } - if (p_preset->get("texture_format/etc2")) { - r_features->push_back("etc2"); - } - - r_features->push_back("64"); -} - -bool EditorExportPlatformOSX::get_export_option_visibility(const String &p_option, const HashMap<StringName, Variant> &p_options) const { - // These options are not supported by built-in codesign, used on non macOS host. - if (!OS::get_singleton()->has_feature("macos")) { - if (p_option == "codesign/identity" || p_option == "codesign/timestamp" || p_option == "codesign/hardened_runtime" || p_option == "codesign/custom_options" || p_option.begins_with("notarization/")) { - return false; - } - } - - // These entitlements are required to run managed code, and are always enabled in Mono builds. - if (Engine::get_singleton()->has_singleton("GodotSharp")) { - if (p_option == "codesign/entitlements/allow_jit_code_execution" || p_option == "codesign/entitlements/allow_unsigned_executable_memory" || p_option == "codesign/entitlements/allow_dyld_environment_variables") { - return false; - } - } - return true; -} - -void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options) { - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); - - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "debug/export_console_script", PROPERTY_HINT_ENUM, "No,Debug Only,Debug and Release"), 1)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.png,*.icns"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/bundle_identifier", PROPERTY_HINT_PLACEHOLDER_TEXT, "com.example.game"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/signature"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/app_category", PROPERTY_HINT_ENUM, "Business,Developer-tools,Education,Entertainment,Finance,Games,Action-games,Adventure-games,Arcade-games,Board-games,Card-games,Casino-games,Dice-games,Educational-games,Family-games,Kids-games,Music-games,Puzzle-games,Racing-games,Role-playing-games,Simulation-games,Sports-games,Strategy-games,Trivia-games,Word-games,Graphics-design,Healthcare-fitness,Lifestyle,Medical,Music,News,Photography,Productivity,Reference,Social-networking,Sports,Travel,Utilities,Video,Weather"), "Games")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version"), "1.0")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version"), "1.0")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::DICTIONARY, "application/copyright_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary())); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "display/high_res"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/microphone_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the microphone"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::DICTIONARY, "privacy/microphone_usage_description_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary())); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/camera_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the camera"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::DICTIONARY, "privacy/camera_usage_description_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary())); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/location_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the location information"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::DICTIONARY, "privacy/location_usage_description_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary())); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/address_book_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the address book"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::DICTIONARY, "privacy/address_book_usage_description_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary())); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/calendar_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the calendar"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::DICTIONARY, "privacy/calendar_usage_description_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary())); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/photos_library_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the photo library"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::DICTIONARY, "privacy/photos_library_usage_description_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary())); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/desktop_folder_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use Desktop folder"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::DICTIONARY, "privacy/desktop_folder_usage_description_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary())); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/documents_folder_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use Documents folder"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::DICTIONARY, "privacy/documents_folder_usage_description_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary())); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/downloads_folder_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use Downloads folder"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::DICTIONARY, "privacy/downloads_folder_usage_description_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary())); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/network_volumes_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use network volumes"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::DICTIONARY, "privacy/network_volumes_usage_description_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary())); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/removable_volumes_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use removable volumes"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::DICTIONARY, "privacy/removable_volumes_usage_description_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary())); - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/enable"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_PLACEHOLDER_TEXT, "Type: Name (ID)"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/timestamp"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/replace_existing_signature"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/hardened_runtime"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/entitlements/custom_file", PROPERTY_HINT_GLOBAL_FILE, "*.plist"), "")); - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_jit_code_execution"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_unsigned_executable_memory"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_dyld_environment_variables"), false)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/disable_library_validation"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/audio_input"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/camera"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/location"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/address_book"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/calendars"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/photos_library"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/apple_events"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/debugging"), false)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/enabled"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/network_server"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/network_client"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/device_usb"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/device_bluetooth"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_downloads", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_pictures", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_music", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_movies", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0)); - r_options->push_back(ExportOption(PropertyInfo(Variant::ARRAY, "codesign/entitlements/app_sandbox/helper_executables", PROPERTY_HINT_ARRAY_TYPE, itos(Variant::STRING) + "/" + itos(PROPERTY_HINT_GLOBAL_FILE) + ":"), Array())); - - r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "codesign/custom_options"), PackedStringArray())); - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "notarization/enable"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Apple ID email"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_password", PROPERTY_HINT_PLACEHOLDER_TEXT, "Enable two-factor authentication and provide app-specific password"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_team_id", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide team ID if your Apple ID belongs to multiple teams"), "")); - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc2"), false)); -} - -void _rgba8_to_packbits_encode(int p_ch, int p_size, Vector<uint8_t> &p_source, Vector<uint8_t> &p_dest) { - int src_len = p_size * p_size; - - Vector<uint8_t> result; - result.resize(src_len * 1.25); //temp vector for rle encoded data, make it 25% larger for worst case scenario - int res_size = 0; - - uint8_t buf[128]; - int buf_size = 0; - - int i = 0; - while (i < src_len) { - uint8_t cur = p_source.ptr()[i * 4 + p_ch]; - - if (i < src_len - 2) { - if ((p_source.ptr()[(i + 1) * 4 + p_ch] == cur) && (p_source.ptr()[(i + 2) * 4 + p_ch] == cur)) { - if (buf_size > 0) { - result.write[res_size++] = (uint8_t)(buf_size - 1); - memcpy(&result.write[res_size], &buf, buf_size); - res_size += buf_size; - buf_size = 0; - } - - uint8_t lim = i + 130 >= src_len ? src_len - i - 1 : 130; - bool hit_lim = true; - - for (int j = 3; j <= lim; j++) { - if (p_source.ptr()[(i + j) * 4 + p_ch] != cur) { - hit_lim = false; - i = i + j - 1; - result.write[res_size++] = (uint8_t)(j - 3 + 0x80); - result.write[res_size++] = cur; - break; - } - } - if (hit_lim) { - result.write[res_size++] = (uint8_t)(lim - 3 + 0x80); - result.write[res_size++] = cur; - i = i + lim; - } - } else { - buf[buf_size++] = cur; - if (buf_size == 128) { - result.write[res_size++] = (uint8_t)(buf_size - 1); - memcpy(&result.write[res_size], &buf, buf_size); - res_size += buf_size; - buf_size = 0; - } - } - } else { - buf[buf_size++] = cur; - result.write[res_size++] = (uint8_t)(buf_size - 1); - memcpy(&result.write[res_size], &buf, buf_size); - res_size += buf_size; - buf_size = 0; - } - - i++; - } - - int ofs = p_dest.size(); - p_dest.resize(p_dest.size() + res_size); - memcpy(&p_dest.write[ofs], result.ptr(), res_size); -} - -void EditorExportPlatformOSX::_make_icon(const Ref<Image> &p_icon, Vector<uint8_t> &p_data) { - Ref<ImageTexture> it = memnew(ImageTexture); - - Vector<uint8_t> data; - - data.resize(8); - data.write[0] = 'i'; - data.write[1] = 'c'; - data.write[2] = 'n'; - data.write[3] = 's'; - - struct MacOSIconInfo { - const char *name; - const char *mask_name; - bool is_png; - int size; - }; - - static const MacOSIconInfo icon_infos[] = { - { "ic10", "", true, 1024 }, //1024×1024 32-bit PNG and 512×512@2x 32-bit "retina" PNG - { "ic09", "", true, 512 }, //512×512 32-bit PNG - { "ic14", "", true, 512 }, //256×256@2x 32-bit "retina" PNG - { "ic08", "", true, 256 }, //256×256 32-bit PNG - { "ic13", "", true, 256 }, //128×128@2x 32-bit "retina" PNG - { "ic07", "", true, 128 }, //128×128 32-bit PNG - { "ic12", "", true, 64 }, //32×32@2× 32-bit "retina" PNG - { "ic11", "", true, 32 }, //16×16@2× 32-bit "retina" PNG - { "il32", "l8mk", false, 32 }, //32×32 24-bit RLE + 8-bit uncompressed mask - { "is32", "s8mk", false, 16 } //16×16 24-bit RLE + 8-bit uncompressed mask - }; - - for (uint64_t i = 0; i < (sizeof(icon_infos) / sizeof(icon_infos[0])); ++i) { - Ref<Image> copy = p_icon; // does this make sense? doesn't this just increase the reference count instead of making a copy? Do we even need a copy? - copy->convert(Image::FORMAT_RGBA8); - copy->resize(icon_infos[i].size, icon_infos[i].size); - - if (icon_infos[i].is_png) { - // Encode PNG icon. - it->set_image(copy); - String path = EditorPaths::get_singleton()->get_cache_dir().plus_file("icon.png"); - ResourceSaver::save(path, it); - - { - Ref<FileAccess> f = FileAccess::open(path, FileAccess::READ); - if (f.is_null()) { - // Clean up generated file. - DirAccess::remove_file_or_error(path); - add_message(EXPORT_MESSAGE_ERROR, TTR("Icon Creation"), vformat(TTR("Could not open icon file \"%s\"."), path)); - return; - } - - int ofs = data.size(); - uint64_t len = f->get_length(); - data.resize(data.size() + len + 8); - f->get_buffer(&data.write[ofs + 8], len); - len += 8; - len = BSWAP32(len); - memcpy(&data.write[ofs], icon_infos[i].name, 4); - encode_uint32(len, &data.write[ofs + 4]); - } - - // Clean up generated file. - DirAccess::remove_file_or_error(path); - - } else { - Vector<uint8_t> src_data = copy->get_data(); - - //encode 24bit RGB RLE icon - { - int ofs = data.size(); - data.resize(data.size() + 8); - - _rgba8_to_packbits_encode(0, icon_infos[i].size, src_data, data); // encode R - _rgba8_to_packbits_encode(1, icon_infos[i].size, src_data, data); // encode G - _rgba8_to_packbits_encode(2, icon_infos[i].size, src_data, data); // encode B - - int len = data.size() - ofs; - len = BSWAP32(len); - memcpy(&data.write[ofs], icon_infos[i].name, 4); - encode_uint32(len, &data.write[ofs + 4]); - } - - //encode 8bit mask uncompressed icon - { - int ofs = data.size(); - int len = copy->get_width() * copy->get_height(); - data.resize(data.size() + len + 8); - - for (int j = 0; j < len; j++) { - data.write[ofs + 8 + j] = src_data.ptr()[j * 4 + 3]; - } - len += 8; - len = BSWAP32(len); - memcpy(&data.write[ofs], icon_infos[i].mask_name, 4); - encode_uint32(len, &data.write[ofs + 4]); - } - } - } - - uint32_t total_len = data.size(); - total_len = BSWAP32(total_len); - encode_uint32(total_len, &data.write[4]); - - p_data = data; -} - -void EditorExportPlatformOSX::_fix_plist(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &plist, const String &p_binary) { - String str; - String strnew; - str.parse_utf8((const char *)plist.ptr(), plist.size()); - Vector<String> lines = str.split("\n"); - for (int i = 0; i < lines.size(); i++) { - if (lines[i].find("$binary") != -1) { - strnew += lines[i].replace("$binary", p_binary) + "\n"; - } else if (lines[i].find("$name") != -1) { - strnew += lines[i].replace("$name", ProjectSettings::get_singleton()->get("application/config/name")) + "\n"; - } else if (lines[i].find("$bundle_identifier") != -1) { - strnew += lines[i].replace("$bundle_identifier", p_preset->get("application/bundle_identifier")) + "\n"; - } else if (lines[i].find("$short_version") != -1) { - strnew += lines[i].replace("$short_version", p_preset->get("application/short_version")) + "\n"; - } else if (lines[i].find("$version") != -1) { - strnew += lines[i].replace("$version", p_preset->get("application/version")) + "\n"; - } else if (lines[i].find("$signature") != -1) { - strnew += lines[i].replace("$signature", p_preset->get("application/signature")) + "\n"; - } else if (lines[i].find("$app_category") != -1) { - String cat = p_preset->get("application/app_category"); - strnew += lines[i].replace("$app_category", cat.to_lower()) + "\n"; - } else if (lines[i].find("$copyright") != -1) { - strnew += lines[i].replace("$copyright", p_preset->get("application/copyright")) + "\n"; - } else if (lines[i].find("$highres") != -1) { - strnew += lines[i].replace("$highres", p_preset->get("display/high_res") ? "\t<true/>" : "\t<false/>") + "\n"; - } else if (lines[i].find("$usage_descriptions") != -1) { - String descriptions; - if (!((String)p_preset->get("privacy/microphone_usage_description")).is_empty()) { - descriptions += "\t<key>NSMicrophoneUsageDescription</key>\n"; - descriptions += "\t<string>" + (String)p_preset->get("privacy/microphone_usage_description") + "</string>\n"; - } - if (!((String)p_preset->get("privacy/camera_usage_description")).is_empty()) { - descriptions += "\t<key>NSCameraUsageDescription</key>\n"; - descriptions += "\t<string>" + (String)p_preset->get("privacy/camera_usage_description") + "</string>\n"; - } - if (!((String)p_preset->get("privacy/location_usage_description")).is_empty()) { - descriptions += "\t<key>NSLocationUsageDescription</key>\n"; - descriptions += "\t<string>" + (String)p_preset->get("privacy/location_usage_description") + "</string>\n"; - } - if (!((String)p_preset->get("privacy/address_book_usage_description")).is_empty()) { - descriptions += "\t<key>NSContactsUsageDescription</key>\n"; - descriptions += "\t<string>" + (String)p_preset->get("privacy/address_book_usage_description") + "</string>\n"; - } - if (!((String)p_preset->get("privacy/calendar_usage_description")).is_empty()) { - descriptions += "\t<key>NSCalendarsUsageDescription</key>\n"; - descriptions += "\t<string>" + (String)p_preset->get("privacy/calendar_usage_description") + "</string>\n"; - } - if (!((String)p_preset->get("privacy/photos_library_usage_description")).is_empty()) { - descriptions += "\t<key>NSPhotoLibraryUsageDescription</key>\n"; - descriptions += "\t<string>" + (String)p_preset->get("privacy/photos_library_usage_description") + "</string>\n"; - } - if (!((String)p_preset->get("privacy/desktop_folder_usage_description")).is_empty()) { - descriptions += "\t<key>NSDesktopFolderUsageDescription</key>\n"; - descriptions += "\t<string>" + (String)p_preset->get("privacy/desktop_folder_usage_description") + "</string>\n"; - } - if (!((String)p_preset->get("privacy/documents_folder_usage_description")).is_empty()) { - descriptions += "\t<key>NSDocumentsFolderUsageDescription</key>\n"; - descriptions += "\t<string>" + (String)p_preset->get("privacy/documents_folder_usage_description") + "</string>\n"; - } - if (!((String)p_preset->get("privacy/downloads_folder_usage_description")).is_empty()) { - descriptions += "\t<key>NSDownloadsFolderUsageDescription</key>\n"; - descriptions += "\t<string>" + (String)p_preset->get("privacy/downloads_folder_usage_description") + "</string>\n"; - } - if (!((String)p_preset->get("privacy/network_volumes_usage_description")).is_empty()) { - descriptions += "\t<key>NSNetworkVolumesUsageDescription</key>\n"; - descriptions += "\t<string>" + (String)p_preset->get("privacy/network_volumes_usage_description") + "</string>\n"; - } - if (!((String)p_preset->get("privacy/removable_volumes_usage_description")).is_empty()) { - descriptions += "\t<key>NSRemovableVolumesUsageDescription</key>\n"; - descriptions += "\t<string>" + (String)p_preset->get("privacy/removable_volumes_usage_description") + "</string>\n"; - } - if (!descriptions.is_empty()) { - strnew += lines[i].replace("$usage_descriptions", descriptions); - } - } else { - strnew += lines[i] + "\n"; - } - } - - CharString cs = strnew.utf8(); - plist.resize(cs.size() - 1); - for (int i = 0; i < cs.size() - 1; i++) { - plist.write[i] = cs[i]; - } -} - -/** - * If we're running the OSX version of the Godot editor we'll: - * - export our application bundle to a temporary folder - * - attempt to code sign it - * - and then wrap it up in a DMG - */ - -Error EditorExportPlatformOSX::_notarize(const Ref<EditorExportPreset> &p_preset, const String &p_path) { -#ifdef OSX_ENABLED - List<String> args; - - args.push_back("altool"); - args.push_back("--notarize-app"); - - args.push_back("--primary-bundle-id"); - args.push_back(p_preset->get("application/bundle_identifier")); - - args.push_back("--username"); - args.push_back(p_preset->get("notarization/apple_id_name")); - - args.push_back("--password"); - args.push_back(p_preset->get("notarization/apple_id_password")); - - args.push_back("--type"); - args.push_back("osx"); - - if (p_preset->get("notarization/apple_team_id")) { - args.push_back("--asc-provider"); - args.push_back(p_preset->get("notarization/apple_team_id")); - } - - args.push_back("--file"); - args.push_back(p_path); - - String str; - Error err = OS::get_singleton()->execute("xcrun", args, &str, nullptr, true); - if (err != OK || (str.find("not found") != -1) || (str.find("not recognized") != -1)) { - add_message(EXPORT_MESSAGE_WARNING, TTR("Notarization"), TTR("Could not start xcrun executable.")); - return err; - } - - print_verbose("altool (" + p_path + "):\n" + str); - int rq_offset = str.find("RequestUUID"); - if (rq_offset == -1) { - add_message(EXPORT_MESSAGE_WARNING, TTR("Notarization"), TTR("Notarization failed.")); - return FAILED; - } else { - int next_nl = str.find("\n", rq_offset); - String request_uuid = (next_nl == -1) ? str.substr(rq_offset + 14, -1) : str.substr(rq_offset + 14, next_nl - rq_offset - 14); - add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), vformat(TTR("Notarization request UUID: \"%s\""), request_uuid)); - add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), TTR("The notarization process generally takes less than an hour. When the process is completed, you'll receive an email.")); - add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), "\t" + TTR("You can check progress manually by opening a Terminal and running the following command:")); - add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), "\t\t\"xcrun altool --notarization-history 0 -u <your email> -p <app-specific pwd>\""); - add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), "\t" + TTR("Run the following command to staple the notarization ticket to the exported application (optional):")); - add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), "\t\t\"xcrun stapler staple <app path>\""); - } - -#endif - - return OK; -} - -Error EditorExportPlatformOSX::_code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path, bool p_warn) { - bool force_builtin_codesign = EditorSettings::get_singleton()->get("export/macos/force_builtin_codesign"); - bool ad_hoc = (p_preset->get("codesign/identity") == "" || p_preset->get("codesign/identity") == "-"); - - if ((!FileAccess::exists("/usr/bin/codesign") && !FileAccess::exists("/bin/codesign")) || force_builtin_codesign) { - print_verbose("using built-in codesign..."); -#ifdef MODULE_REGEX_ENABLED - -#ifdef OSX_ENABLED - if (p_preset->get("codesign/timestamp") && p_warn) { - add_message(EXPORT_MESSAGE_INFO, TTR("Code Signing"), TTR("Timestamping is not compatible with ad-hoc signature, and was disabled!")); - } - if (p_preset->get("codesign/hardened_runtime") && p_warn) { - add_message(EXPORT_MESSAGE_INFO, TTR("Code Signing"), TTR("Hardened Runtime is not compatible with ad-hoc signature, and was disabled!")); - } -#endif - - String error_msg; - Error err = CodeSign::codesign(false, p_preset->get("codesign/replace_existing_signature"), p_path, p_ent_path, error_msg); - if (err != OK) { - add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), vformat(TTR("Built-in CodeSign failed with error \"%s\"."), error_msg)); - return FAILED; - } -#else - add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("Built-in CodeSign require regex module.")); -#endif - return OK; - } else { - print_verbose("using external codesign..."); - List<String> args; - if (p_preset->get("codesign/timestamp")) { - if (ad_hoc) { - if (p_warn) { - add_message(EXPORT_MESSAGE_INFO, TTR("Code Signing"), TTR("Timestamping is not compatible with ad-hoc signature, and was disabled!")); - } - } else { - args.push_back("--timestamp"); - } - } - if (p_preset->get("codesign/hardened_runtime")) { - if (ad_hoc) { - if (p_warn) { - add_message(EXPORT_MESSAGE_INFO, TTR("Code Signing"), TTR("Hardened Runtime is not compatible with ad-hoc signature, and was disabled!")); - } - } else { - args.push_back("--options"); - args.push_back("runtime"); - } - } - - if (p_path.get_extension() != "dmg") { - args.push_back("--entitlements"); - args.push_back(p_ent_path); - } - - PackedStringArray user_args = p_preset->get("codesign/custom_options"); - for (int i = 0; i < user_args.size(); i++) { - String user_arg = user_args[i].strip_edges(); - if (!user_arg.is_empty()) { - args.push_back(user_arg); - } - } - - args.push_back("-s"); - if (ad_hoc) { - args.push_back("-"); - } else { - args.push_back(p_preset->get("codesign/identity")); - } - - args.push_back("-v"); /* provide some more feedback */ - - if (p_preset->get("codesign/replace_existing_signature")) { - args.push_back("-f"); - } - - args.push_back(p_path); - - String str; - Error err = OS::get_singleton()->execute("codesign", args, &str, nullptr, true); - if (err != OK || (str.find("not found") != -1) || (str.find("not recognized") != -1)) { - add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("Could not start codesign executable, make sure Xcode command line tools are installed.")); - return err; - } - - print_verbose("codesign (" + p_path + "):\n" + str); - if (str.find("no identity found") != -1) { - add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("No identity found.")); - return FAILED; - } - if ((str.find("unrecognized blob type") != -1) || (str.find("cannot read entitlement data") != -1)) { - add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("Invalid entitlements file.")); - return FAILED; - } - return OK; - } -} - -Error EditorExportPlatformOSX::_code_sign_directory(const Ref<EditorExportPreset> &p_preset, const String &p_path, - const String &p_ent_path, bool p_should_error_on_non_code) { -#ifdef OSX_ENABLED - static Vector<String> extensions_to_sign; - - if (extensions_to_sign.is_empty()) { - extensions_to_sign.push_back("dylib"); - extensions_to_sign.push_back("framework"); - } - - Error dir_access_error; - Ref<DirAccess> dir_access{ DirAccess::open(p_path, &dir_access_error) }; - - if (dir_access_error != OK) { - return dir_access_error; - } - - dir_access->list_dir_begin(); - String current_file{ dir_access->get_next() }; - while (!current_file.is_empty()) { - String current_file_path{ p_path.plus_file(current_file) }; - - if (current_file == ".." || current_file == ".") { - current_file = dir_access->get_next(); - continue; - } - - if (extensions_to_sign.find(current_file.get_extension()) > -1) { - Error code_sign_error{ _code_sign(p_preset, current_file_path, p_ent_path, false) }; - if (code_sign_error != OK) { - return code_sign_error; - } - } else if (dir_access->current_is_dir()) { - Error code_sign_error{ _code_sign_directory(p_preset, current_file_path, p_ent_path, p_should_error_on_non_code) }; - if (code_sign_error != OK) { - return code_sign_error; - } - } else if (p_should_error_on_non_code) { - add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), vformat(TTR("Cannot sign file %s."), current_file)); - return Error::FAILED; - } - - current_file = dir_access->get_next(); - } -#endif - - return OK; -} - -Error EditorExportPlatformOSX::_copy_and_sign_files(Ref<DirAccess> &dir_access, const String &p_src_path, - const String &p_in_app_path, bool p_sign_enabled, - const Ref<EditorExportPreset> &p_preset, const String &p_ent_path, - bool p_should_error_on_non_code_sign) { - Error err{ OK }; - if (dir_access->dir_exists(p_src_path)) { -#ifndef UNIX_ENABLED - add_message(EXPORT_MESSAGE_INFO, TTR("Export"), vformat(TTR("Relative symlinks are not supported, exported \"%s\" might be broken!"), p_src_path.get_file())); -#endif - print_verbose("export framework: " + p_src_path + " -> " + p_in_app_path); - err = dir_access->make_dir_recursive(p_in_app_path); - if (err == OK) { - err = dir_access->copy_dir(p_src_path, p_in_app_path, -1, true); - } - } else { - print_verbose("export dylib: " + p_src_path + " -> " + p_in_app_path); - err = dir_access->copy(p_src_path, p_in_app_path); - } - if (err == OK && p_sign_enabled) { - if (dir_access->dir_exists(p_src_path) && p_src_path.get_extension().is_empty()) { - // If it is a directory, find and sign all dynamic libraries. - err = _code_sign_directory(p_preset, p_in_app_path, p_ent_path, p_should_error_on_non_code_sign); - } else { - err = _code_sign(p_preset, p_in_app_path, p_ent_path, false); - } - } - return err; -} - -Error EditorExportPlatformOSX::_export_osx_plugins_for(Ref<EditorExportPlugin> p_editor_export_plugin, - const String &p_app_path_name, Ref<DirAccess> &dir_access, - bool p_sign_enabled, const Ref<EditorExportPreset> &p_preset, - const String &p_ent_path) { - Error error{ OK }; - const Vector<String> &osx_plugins{ p_editor_export_plugin->get_osx_plugin_files() }; - for (int i = 0; i < osx_plugins.size(); ++i) { - String src_path{ ProjectSettings::get_singleton()->globalize_path(osx_plugins[i]) }; - String path_in_app{ p_app_path_name + "/Contents/PlugIns/" + src_path.get_file() }; - error = _copy_and_sign_files(dir_access, src_path, path_in_app, p_sign_enabled, p_preset, p_ent_path, false); - if (error != OK) { - break; - } - } - return error; -} - -Error EditorExportPlatformOSX::_create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name) { - List<String> args; - - if (FileAccess::exists(p_dmg_path)) { - OS::get_singleton()->move_to_trash(p_dmg_path); - } - - args.push_back("create"); - args.push_back(p_dmg_path); - args.push_back("-volname"); - args.push_back(p_pkg_name); - args.push_back("-fs"); - args.push_back("HFS+"); - args.push_back("-srcfolder"); - args.push_back(p_app_path_name); - - String str; - Error err = OS::get_singleton()->execute("hdiutil", args, &str, nullptr, true); - if (err != OK) { - add_message(EXPORT_MESSAGE_ERROR, TTR("DMG Creation"), TTR("Could not start hdiutil executable.")); - return err; - } - - print_verbose("hdiutil returned: " + str); - if (str.find("create failed") != -1) { - if (str.find("File exists") != -1) { - add_message(EXPORT_MESSAGE_ERROR, TTR("DMG Creation"), TTR("`hdiutil create` failed - file exists.")); - } else { - add_message(EXPORT_MESSAGE_ERROR, TTR("DMG Creation"), TTR("`hdiutil create` failed.")); - } - return FAILED; - } - - return OK; -} - -Error EditorExportPlatformOSX::_export_debug_script(const Ref<EditorExportPreset> &p_preset, const String &p_app_name, const String &p_pkg_name, const String &p_path) { - Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::WRITE); - if (f.is_null()) { - add_message(EXPORT_MESSAGE_ERROR, TTR("Debug Script Export"), vformat(TTR("Could not open file \"%s\"."), p_path)); - return ERR_CANT_CREATE; - } - - f->store_line("#!/bin/sh"); - f->store_line("echo -ne '\\033c\\033]0;" + p_app_name + "\\a'"); - f->store_line("function realpath() { python -c \"import os,sys; print(os.path.realpath(sys.argv[1]))\" \"$0\"; }"); - f->store_line("base_path=\"$(dirname \"$(realpath \"$0\")\")\""); - f->store_line("\"$base_path/" + p_pkg_name + "\" \"$@\""); - - return OK; -} - -Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { - ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); - - String src_pkg_name; - - EditorProgress ep("export", "Exporting for OSX", 3, true); - - if (p_debug) { - src_pkg_name = p_preset->get("custom_template/debug"); - } else { - src_pkg_name = p_preset->get("custom_template/release"); - } - - if (src_pkg_name.is_empty()) { - String err; - src_pkg_name = find_export_template("osx.zip", &err); - if (src_pkg_name.is_empty()) { - add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), TTR("Export template not found.")); - return ERR_FILE_NOT_FOUND; - } - } - - if (!DirAccess::exists(p_path.get_base_dir())) { - add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), TTR("The given export path doesn't exist.")); - return ERR_FILE_BAD_PATH; - } - - Ref<FileAccess> io_fa; - zlib_filefunc_def io = zipio_create_io(&io_fa); - - if (ep.step(TTR("Creating app bundle"), 0)) { - return ERR_SKIP; - } - - unzFile src_pkg_zip = unzOpen2(src_pkg_name.utf8().get_data(), &io); - if (!src_pkg_zip) { - add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), vformat(TTR("Could not find template app to export: \"%s\"."), src_pkg_name)); - return ERR_FILE_NOT_FOUND; - } - - int ret = unzGoToFirstFile(src_pkg_zip); - - String binary_to_use = "godot_osx_" + String(p_debug ? "debug" : "release") + ".64"; - - String pkg_name; - if (String(ProjectSettings::get_singleton()->get("application/config/name")) != "") { - pkg_name = String(ProjectSettings::get_singleton()->get("application/config/name")); - } else { - pkg_name = "Unnamed"; - } - - pkg_name = OS::get_singleton()->get_safe_dir_name(pkg_name); - - String export_format; - if (use_dmg() && p_path.ends_with("dmg")) { - export_format = "dmg"; - } else if (p_path.ends_with("zip")) { - export_format = "zip"; - } else if (p_path.ends_with("app")) { - export_format = "app"; - } else { - add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Invalid export format.")); - return ERR_CANT_CREATE; - } - - // Create our application bundle. - String tmp_app_dir_name = pkg_name + ".app"; - String tmp_base_path_name; - String tmp_app_path_name; - String scr_path; - if (export_format == "app") { - tmp_base_path_name = p_path.get_base_dir(); - tmp_app_path_name = p_path; - scr_path = p_path.get_basename() + ".command"; - } else { - tmp_base_path_name = EditorPaths::get_singleton()->get_cache_dir().plus_file(pkg_name); - tmp_app_path_name = tmp_base_path_name.plus_file(tmp_app_dir_name); - scr_path = tmp_base_path_name.plus_file(pkg_name + ".command"); - } - - print_verbose("Exporting to " + tmp_app_path_name); - - Error err = OK; - - Ref<DirAccess> tmp_app_dir = DirAccess::create_for_path(tmp_base_path_name); - if (tmp_app_dir.is_null()) { - add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not create directory: \"%s\"."), tmp_base_path_name)); - err = ERR_CANT_CREATE; - } - - DirAccess::remove_file_or_error(scr_path); - if (DirAccess::exists(tmp_app_path_name)) { - String old_dir = tmp_app_dir->get_current_dir(); - if (tmp_app_dir->change_dir(tmp_app_path_name) == OK) { - tmp_app_dir->erase_contents_recursive(); - tmp_app_dir->change_dir(old_dir); - } - } - - Array helpers = p_preset->get("codesign/entitlements/app_sandbox/helper_executables"); - - // Create our folder structure. - if (err == OK) { - print_verbose("Creating " + tmp_app_path_name + "/Contents/MacOS"); - err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/MacOS"); - if (err != OK) { - add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not create directory \"%s\"."), tmp_app_path_name + "/Contents/MacOS")); - } - } - - if (err == OK) { - print_verbose("Creating " + tmp_app_path_name + "/Contents/Frameworks"); - err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/Frameworks"); - if (err != OK) { - add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not create directory \"%s\"."), tmp_app_path_name + "/Contents/Frameworks")); - } - } - - if ((err == OK) && helpers.size() > 0) { - print_line("Creating " + tmp_app_path_name + "/Contents/Helpers"); - err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/Helpers"); - if (err != OK) { - add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not create directory \"%s\"."), tmp_app_path_name + "/Contents/Helpers")); - } - } - - if (err == OK) { - print_verbose("Creating " + tmp_app_path_name + "/Contents/Resources"); - err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/Resources"); - if (err != OK) { - add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not create directory \"%s\"."), tmp_app_path_name + "/Contents/Resources")); - } - } - - Dictionary appnames = ProjectSettings::get_singleton()->get("application/config/name_localized"); - Dictionary microphone_usage_descriptions = p_preset->get("privacy/microphone_usage_description_localized"); - Dictionary camera_usage_descriptions = p_preset->get("privacy/camera_usage_description_localized"); - Dictionary location_usage_descriptions = p_preset->get("privacy/location_usage_description_localized"); - Dictionary address_book_usage_descriptions = p_preset->get("privacy/address_book_usage_description_localized"); - Dictionary calendar_usage_descriptions = p_preset->get("privacy/calendar_usage_description_localized"); - Dictionary photos_library_usage_descriptions = p_preset->get("privacy/photos_library_usage_description_localized"); - Dictionary desktop_folder_usage_descriptions = p_preset->get("privacy/desktop_folder_usage_description_localized"); - Dictionary documents_folder_usage_descriptions = p_preset->get("privacy/documents_folder_usage_description_localized"); - Dictionary downloads_folder_usage_descriptions = p_preset->get("privacy/downloads_folder_usage_description_localized"); - Dictionary network_volumes_usage_descriptions = p_preset->get("privacy/network_volumes_usage_description_localized"); - Dictionary removable_volumes_usage_descriptions = p_preset->get("privacy/removable_volumes_usage_description_localized"); - Dictionary copyrights = p_preset->get("application/copyright_localized"); - - Vector<String> translations = ProjectSettings::get_singleton()->get("internationalization/locale/translations"); - if (translations.size() > 0) { - { - String fname = tmp_app_path_name + "/Contents/Resources/en.lproj"; - tmp_app_dir->make_dir_recursive(fname); - Ref<FileAccess> f = FileAccess::open(fname + "/InfoPlist.strings", FileAccess::WRITE); - f->store_line("/* Localized versions of Info.plist keys */"); - f->store_line(""); - f->store_line("CFBundleDisplayName = \"" + ProjectSettings::get_singleton()->get("application/config/name").operator String() + "\";"); - if (!((String)p_preset->get("privacy/microphone_usage_description")).is_empty()) { - f->store_line("NSMicrophoneUsageDescription = \"" + p_preset->get("privacy/microphone_usage_description").operator String() + "\";"); - } - if (!((String)p_preset->get("privacy/camera_usage_description")).is_empty()) { - f->store_line("NSCameraUsageDescription = \"" + p_preset->get("privacy/camera_usage_description").operator String() + "\";"); - } - if (!((String)p_preset->get("privacy/location_usage_description")).is_empty()) { - f->store_line("NSLocationUsageDescription = \"" + p_preset->get("privacy/location_usage_description").operator String() + "\";"); - } - if (!((String)p_preset->get("privacy/address_book_usage_description")).is_empty()) { - f->store_line("NSContactsUsageDescription = \"" + p_preset->get("privacy/address_book_usage_description").operator String() + "\";"); - } - if (!((String)p_preset->get("privacy/calendar_usage_description")).is_empty()) { - f->store_line("NSCalendarsUsageDescription = \"" + p_preset->get("privacy/calendar_usage_description").operator String() + "\";"); - } - if (!((String)p_preset->get("privacy/photos_library_usage_description")).is_empty()) { - f->store_line("NSPhotoLibraryUsageDescription = \"" + p_preset->get("privacy/photos_library_usage_description").operator String() + "\";"); - } - if (!((String)p_preset->get("privacy/desktop_folder_usage_description")).is_empty()) { - f->store_line("NSDesktopFolderUsageDescription = \"" + p_preset->get("privacy/desktop_folder_usage_description").operator String() + "\";"); - } - if (!((String)p_preset->get("privacy/documents_folder_usage_description")).is_empty()) { - f->store_line("NSDocumentsFolderUsageDescription = \"" + p_preset->get("privacy/documents_folder_usage_description").operator String() + "\";"); - } - if (!((String)p_preset->get("privacy/downloads_folder_usage_description")).is_empty()) { - f->store_line("NSDownloadsFolderUsageDescription = \"" + p_preset->get("privacy/downloads_folder_usage_description").operator String() + "\";"); - } - if (!((String)p_preset->get("privacy/network_volumes_usage_description")).is_empty()) { - f->store_line("NSNetworkVolumesUsageDescription = \"" + p_preset->get("privacy/network_volumes_usage_description").operator String() + "\";"); - } - if (!((String)p_preset->get("privacy/removable_volumes_usage_description")).is_empty()) { - f->store_line("NSRemovableVolumesUsageDescription = \"" + p_preset->get("privacy/removable_volumes_usage_description").operator String() + "\";"); - } - f->store_line("NSHumanReadableCopyright = \"" + p_preset->get("application/copyright").operator String() + "\";"); - } - - for (const String &E : translations) { - Ref<Translation> tr = ResourceLoader::load(E); - if (tr.is_valid()) { - String lang = tr->get_locale(); - String fname = tmp_app_path_name + "/Contents/Resources/" + lang + ".lproj"; - tmp_app_dir->make_dir_recursive(fname); - Ref<FileAccess> f = FileAccess::open(fname + "/InfoPlist.strings", FileAccess::WRITE); - f->store_line("/* Localized versions of Info.plist keys */"); - f->store_line(""); - if (appnames.has(lang)) { - f->store_line("CFBundleDisplayName = \"" + appnames[lang].operator String() + "\";"); - } - if (microphone_usage_descriptions.has(lang)) { - f->store_line("NSMicrophoneUsageDescription = \"" + microphone_usage_descriptions[lang].operator String() + "\";"); - } - if (camera_usage_descriptions.has(lang)) { - f->store_line("NSCameraUsageDescription = \"" + camera_usage_descriptions[lang].operator String() + "\";"); - } - if (location_usage_descriptions.has(lang)) { - f->store_line("NSLocationUsageDescription = \"" + location_usage_descriptions[lang].operator String() + "\";"); - } - if (address_book_usage_descriptions.has(lang)) { - f->store_line("NSContactsUsageDescription = \"" + address_book_usage_descriptions[lang].operator String() + "\";"); - } - if (calendar_usage_descriptions.has(lang)) { - f->store_line("NSCalendarsUsageDescription = \"" + calendar_usage_descriptions[lang].operator String() + "\";"); - } - if (photos_library_usage_descriptions.has(lang)) { - f->store_line("NSPhotoLibraryUsageDescription = \"" + photos_library_usage_descriptions[lang].operator String() + "\";"); - } - if (desktop_folder_usage_descriptions.has(lang)) { - f->store_line("NSDesktopFolderUsageDescription = \"" + desktop_folder_usage_descriptions[lang].operator String() + "\";"); - } - if (documents_folder_usage_descriptions.has(lang)) { - f->store_line("NSDocumentsFolderUsageDescription = \"" + documents_folder_usage_descriptions[lang].operator String() + "\";"); - } - if (downloads_folder_usage_descriptions.has(lang)) { - f->store_line("NSDownloadsFolderUsageDescription = \"" + downloads_folder_usage_descriptions[lang].operator String() + "\";"); - } - if (network_volumes_usage_descriptions.has(lang)) { - f->store_line("NSNetworkVolumesUsageDescription = \"" + network_volumes_usage_descriptions[lang].operator String() + "\";"); - } - if (removable_volumes_usage_descriptions.has(lang)) { - f->store_line("NSRemovableVolumesUsageDescription = \"" + removable_volumes_usage_descriptions[lang].operator String() + "\";"); - } - if (copyrights.has(lang)) { - f->store_line("NSHumanReadableCopyright = \"" + copyrights[lang].operator String() + "\";"); - } - } - } - } - - // Now process our template. - bool found_binary = false; - Vector<String> dylibs_found; - - while (ret == UNZ_OK && err == OK) { - bool is_execute = false; - - // Get filename. - unz_file_info info; - char fname[16384]; - ret = unzGetCurrentFileInfo(src_pkg_zip, &info, fname, 16384, nullptr, 0, nullptr, 0); - if (ret != UNZ_OK) { - break; - } - - String file = String::utf8(fname); - - Vector<uint8_t> data; - data.resize(info.uncompressed_size); - - // Read. - unzOpenCurrentFile(src_pkg_zip); - unzReadCurrentFile(src_pkg_zip, data.ptrw(), data.size()); - unzCloseCurrentFile(src_pkg_zip); - - // Write. - file = file.replace_first("osx_template.app/", ""); - - if (((info.external_fa >> 16L) & 0120000) == 0120000) { -#ifndef UNIX_ENABLED - add_message(EXPORT_MESSAGE_INFO, TTR("Export"), TTR("Relative symlinks are not supported on this OS, the exported project might be broken!")); -#endif - // Handle symlinks in the archive. - file = tmp_app_path_name.plus_file(file); - if (err == OK) { - err = tmp_app_dir->make_dir_recursive(file.get_base_dir()); - if (err != OK) { - add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not create directory \"%s\"."), file.get_base_dir())); - } - } - if (err == OK) { - String lnk_data = String::utf8((const char *)data.ptr(), data.size()); - err = tmp_app_dir->create_link(lnk_data, file); - if (err != OK) { - add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not created symlink \"%s\" -> \"%s\"."), lnk_data, file)); - } - print_verbose(vformat("ADDING SYMLINK %s => %s\n", file, lnk_data)); - } - - ret = unzGoToNextFile(src_pkg_zip); - continue; // next - } - - if (file == "Contents/Info.plist") { - _fix_plist(p_preset, data, pkg_name); - } - - if (file.begins_with("Contents/MacOS/godot_")) { - if (file != "Contents/MacOS/" + binary_to_use) { - ret = unzGoToNextFile(src_pkg_zip); - continue; // skip - } - found_binary = true; - is_execute = true; - file = "Contents/MacOS/" + pkg_name; - } - - if (file == "Contents/Resources/icon.icns") { - // See if there is an icon. - String iconpath; - if (p_preset->get("application/icon") != "") { - iconpath = p_preset->get("application/icon"); - } else { - iconpath = ProjectSettings::get_singleton()->get("application/config/icon"); - } - - if (!iconpath.is_empty()) { - if (iconpath.get_extension() == "icns") { - Ref<FileAccess> icon = FileAccess::open(iconpath, FileAccess::READ); - if (icon.is_valid()) { - data.resize(icon->get_length()); - icon->get_buffer(&data.write[0], icon->get_length()); - } - } else { - Ref<Image> icon; - icon.instantiate(); - icon->load(iconpath); - if (!icon->is_empty()) { - _make_icon(icon, data); - } - } - } - } - - if (data.size() > 0) { - if (file.find("/data.mono.osx.64.release_debug/") != -1) { - if (!p_debug) { - ret = unzGoToNextFile(src_pkg_zip); - continue; // skip - } - file = file.replace("/data.mono.osx.64.release_debug/", "/GodotSharp/"); - } - if (file.find("/data.mono.osx.64.release/") != -1) { - if (p_debug) { - ret = unzGoToNextFile(src_pkg_zip); - continue; // skip - } - file = file.replace("/data.mono.osx.64.release/", "/GodotSharp/"); - } - - if (file.ends_with(".dylib")) { - dylibs_found.push_back(file); - } - - print_verbose("ADDING: " + file + " size: " + itos(data.size())); - - // Write it into our application bundle. - file = tmp_app_path_name.plus_file(file); - if (err == OK) { - err = tmp_app_dir->make_dir_recursive(file.get_base_dir()); - if (err != OK) { - add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not create directory \"%s\"."), file.get_base_dir())); - } - } - if (err == OK) { - Ref<FileAccess> f = FileAccess::open(file, FileAccess::WRITE); - if (f.is_valid()) { - f->store_buffer(data.ptr(), data.size()); - f.unref(); - if (is_execute) { - // chmod with 0755 if the file is executable. - FileAccess::set_unix_permissions(file, 0755); - } - } else { - add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not open \"%s\"."), file)); - err = ERR_CANT_CREATE; - } - } - } - - ret = unzGoToNextFile(src_pkg_zip); - } - - // We're done with our source zip. - unzClose(src_pkg_zip); - - if (!found_binary) { - add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Requested template binary \"%s\" not found. It might be missing from your template archive."), binary_to_use)); - err = ERR_FILE_NOT_FOUND; - } - - // Save console script. - if (err == OK) { - int con_scr = p_preset->get("debug/export_console_script"); - if ((con_scr == 1 && p_debug) || (con_scr == 2)) { - err = _export_debug_script(p_preset, pkg_name, tmp_app_path_name.get_file() + "/Contents/MacOS/" + pkg_name, scr_path); - FileAccess::set_unix_permissions(scr_path, 0755); - if (err != OK) { - add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Could not create console script.")); - } - } - } - - if (err == OK) { - if (ep.step(TTR("Making PKG"), 1)) { - return ERR_SKIP; - } - - String pack_path = tmp_app_path_name + "/Contents/Resources/" + pkg_name + ".pck"; - Vector<SharedObject> shared_objects; - err = save_pack(p_preset, p_debug, pack_path, &shared_objects); - - // See if we can code sign our new package. - bool sign_enabled = p_preset->get("codesign/enable"); - - String ent_path = p_preset->get("codesign/entitlements/custom_file"); - String hlp_ent_path = EditorPaths::get_singleton()->get_cache_dir().plus_file(pkg_name + "_helper.entitlements"); - if (sign_enabled && (ent_path.is_empty())) { - ent_path = EditorPaths::get_singleton()->get_cache_dir().plus_file(pkg_name + ".entitlements"); - - Ref<FileAccess> ent_f = FileAccess::open(ent_path, FileAccess::WRITE); - if (ent_f.is_valid()) { - ent_f->store_line("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); - ent_f->store_line("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">"); - ent_f->store_line("<plist version=\"1.0\">"); - ent_f->store_line("<dict>"); - if (Engine::get_singleton()->has_singleton("GodotSharp")) { - // These entitlements are required to run managed code, and are always enabled in Mono builds. - ent_f->store_line("<key>com.apple.security.cs.allow-jit</key>"); - ent_f->store_line("<true/>"); - ent_f->store_line("<key>com.apple.security.cs.allow-unsigned-executable-memory</key>"); - ent_f->store_line("<true/>"); - ent_f->store_line("<key>com.apple.security.cs.allow-dyld-environment-variables</key>"); - ent_f->store_line("<true/>"); - } else { - if ((bool)p_preset->get("codesign/entitlements/allow_jit_code_execution")) { - ent_f->store_line("<key>com.apple.security.cs.allow-jit</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/allow_unsigned_executable_memory")) { - ent_f->store_line("<key>com.apple.security.cs.allow-unsigned-executable-memory</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/allow_dyld_environment_variables")) { - ent_f->store_line("<key>com.apple.security.cs.allow-dyld-environment-variables</key>"); - ent_f->store_line("<true/>"); - } - } - - if ((bool)p_preset->get("codesign/entitlements/disable_library_validation")) { - ent_f->store_line("<key>com.apple.security.cs.disable-library-validation</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/audio_input")) { - ent_f->store_line("<key>com.apple.security.device.audio-input</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/camera")) { - ent_f->store_line("<key>com.apple.security.device.camera</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/location")) { - ent_f->store_line("<key>com.apple.security.personal-information.location</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/address_book")) { - ent_f->store_line("<key>com.apple.security.personal-information.addressbook</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/calendars")) { - ent_f->store_line("<key>com.apple.security.personal-information.calendars</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/photos_library")) { - ent_f->store_line("<key>com.apple.security.personal-information.photos-library</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/apple_events")) { - ent_f->store_line("<key>com.apple.security.automation.apple-events</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/debugging")) { - ent_f->store_line("<key>com.apple.security.get-task-allow</key>"); - ent_f->store_line("<true/>"); - } - - if ((bool)p_preset->get("codesign/entitlements/app_sandbox/enabled")) { - ent_f->store_line("<key>com.apple.security.app-sandbox</key>"); - ent_f->store_line("<true/>"); - - if ((bool)p_preset->get("codesign/entitlements/app_sandbox/network_server")) { - ent_f->store_line("<key>com.apple.security.network.server</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/app_sandbox/network_client")) { - ent_f->store_line("<key>com.apple.security.network.client</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/app_sandbox/device_usb")) { - ent_f->store_line("<key>com.apple.security.device.usb</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/app_sandbox/device_bluetooth")) { - ent_f->store_line("<key>com.apple.security.device.bluetooth</key>"); - ent_f->store_line("<true/>"); - } - if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_downloads") == 1) { - ent_f->store_line("<key>com.apple.security.files.downloads.read-only</key>"); - ent_f->store_line("<true/>"); - } - if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_downloads") == 2) { - ent_f->store_line("<key>com.apple.security.files.downloads.read-write</key>"); - ent_f->store_line("<true/>"); - } - if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_pictures") == 1) { - ent_f->store_line("<key>com.apple.security.files.pictures.read-only</key>"); - ent_f->store_line("<true/>"); - } - if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_pictures") == 2) { - ent_f->store_line("<key>com.apple.security.files.pictures.read-write</key>"); - ent_f->store_line("<true/>"); - } - if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_music") == 1) { - ent_f->store_line("<key>com.apple.security.files.music.read-only</key>"); - ent_f->store_line("<true/>"); - } - if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_music") == 2) { - ent_f->store_line("<key>com.apple.security.files.music.read-write</key>"); - ent_f->store_line("<true/>"); - } - if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_movies") == 1) { - ent_f->store_line("<key>com.apple.security.files.movies.read-only</key>"); - ent_f->store_line("<true/>"); - } - if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_movies") == 2) { - ent_f->store_line("<key>com.apple.security.files.movies.read-write</key>"); - ent_f->store_line("<true/>"); - } - } - - ent_f->store_line("</dict>"); - ent_f->store_line("</plist>"); - } else { - add_message(EXPORT_MESSAGE_ERROR, TTR("Code Signing"), TTR("Could not create entitlements file.")); - err = ERR_CANT_CREATE; - } - - if ((err == OK) && helpers.size() > 0) { - ent_f = FileAccess::open(hlp_ent_path, FileAccess::WRITE); - if (ent_f.is_valid()) { - ent_f->store_line("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); - ent_f->store_line("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">"); - ent_f->store_line("<plist version=\"1.0\">"); - ent_f->store_line("<dict>"); - ent_f->store_line("<key>com.apple.security.app-sandbox</key>"); - ent_f->store_line("<true/>"); - ent_f->store_line("<key>com.apple.security.inherit</key>"); - ent_f->store_line("<true/>"); - ent_f->store_line("</dict>"); - ent_f->store_line("</plist>"); - } else { - add_message(EXPORT_MESSAGE_ERROR, TTR("Code Signing"), TTR("Could not create helper entitlements file.")); - err = ERR_CANT_CREATE; - } - } - } - - if ((err == OK) && helpers.size() > 0) { - Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - for (int i = 0; i < helpers.size(); i++) { - String hlp_path = helpers[i]; - err = da->copy(hlp_path, tmp_app_path_name + "/Contents/Helpers/" + hlp_path.get_file()); - if (err == OK && sign_enabled) { - err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Helpers/" + hlp_path.get_file(), hlp_ent_path, false); - } - FileAccess::set_unix_permissions(tmp_app_path_name + "/Contents/Helpers/" + hlp_path.get_file(), 0755); - } - } - - bool ad_hoc = true; - if (err == OK) { -#ifdef OSX_ENABLED - String sign_identity = p_preset->get("codesign/identity"); -#else - String sign_identity = "-"; -#endif - ad_hoc = (sign_identity == "" || sign_identity == "-"); - bool lib_validation = p_preset->get("codesign/entitlements/disable_library_validation"); - if ((!dylibs_found.is_empty() || !shared_objects.is_empty()) && sign_enabled && ad_hoc && !lib_validation) { - add_message(EXPORT_MESSAGE_ERROR, TTR("Code Signing"), TTR("Ad-hoc signed applications require the 'Disable Library Validation' entitlement to load dynamic libraries.")); - err = ERR_CANT_CREATE; - } - } - - if (err == OK) { - Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - for (int i = 0; i < shared_objects.size(); i++) { - String src_path = ProjectSettings::get_singleton()->globalize_path(shared_objects[i].path); - if (shared_objects[i].target.is_empty()) { - String path_in_app = tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file(); - err = _copy_and_sign_files(da, src_path, path_in_app, sign_enabled, p_preset, ent_path, true); - } else { - String path_in_app = tmp_app_path_name.plus_file(shared_objects[i].target).plus_file(src_path.get_file()); - err = _copy_and_sign_files(da, src_path, path_in_app, sign_enabled, p_preset, ent_path, false); - } - if (err != OK) { - break; - } - } - - Vector<Ref<EditorExportPlugin>> export_plugins{ EditorExport::get_singleton()->get_export_plugins() }; - for (int i = 0; i < export_plugins.size(); ++i) { - err = _export_osx_plugins_for(export_plugins[i], tmp_app_path_name, da, sign_enabled, p_preset, ent_path); - if (err != OK) { - break; - } - } - } - - if (sign_enabled) { - for (int i = 0; i < dylibs_found.size(); i++) { - if (err == OK) { - err = _code_sign(p_preset, tmp_app_path_name + "/" + dylibs_found[i], ent_path, false); - } - } - } - - if (err == OK && sign_enabled) { - if (ep.step(TTR("Code signing bundle"), 2)) { - return ERR_SKIP; - } - err = _code_sign(p_preset, tmp_app_path_name, ent_path); - } - - if (export_format == "dmg") { - // Create a DMG. - if (err == OK) { - if (ep.step(TTR("Making DMG"), 3)) { - return ERR_SKIP; - } - err = _create_dmg(p_path, pkg_name, tmp_base_path_name); - } - // Sign DMG. - if (err == OK && sign_enabled && !ad_hoc) { - if (ep.step(TTR("Code signing DMG"), 3)) { - return ERR_SKIP; - } - err = _code_sign(p_preset, p_path, ent_path, false); - } - } else if (export_format == "zip") { - // Create ZIP. - if (err == OK) { - if (ep.step(TTR("Making ZIP"), 3)) { - return ERR_SKIP; - } - if (FileAccess::exists(p_path)) { - OS::get_singleton()->move_to_trash(p_path); - } - - Ref<FileAccess> io_fa_dst; - zlib_filefunc_def io_dst = zipio_create_io(&io_fa_dst); - zipFile zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io_dst); - - _zip_folder_recursive(zip, tmp_base_path_name, "", pkg_name); - - zipClose(zip, nullptr); - } - } - -#ifdef OSX_ENABLED - bool noto_enabled = p_preset->get("notarization/enable"); - if (err == OK && noto_enabled) { - if (export_format == "app") { - add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), TTR("Notarization requires the app to be archived first, select the DMG or ZIP export format instead.")); - } else { - if (ep.step(TTR("Sending archive for notarization"), 4)) { - return ERR_SKIP; - } - err = _notarize(p_preset, p_path); - } - } -#endif - - // Clean up temporary entitlements files. - DirAccess::remove_file_or_error(hlp_ent_path); - - // Clean up temporary .app dir and generated entitlements. - if ((String)(p_preset->get("codesign/entitlements/custom_file")) == "") { - tmp_app_dir->remove(ent_path); - } - if (export_format != "app") { - if (tmp_app_dir->change_dir(tmp_base_path_name) == OK) { - tmp_app_dir->erase_contents_recursive(); - tmp_app_dir->change_dir(".."); - tmp_app_dir->remove(pkg_name); - } - } - } - - return err; -} - -void EditorExportPlatformOSX::_zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name) { - String dir = p_folder.is_empty() ? p_root_path : p_root_path.plus_file(p_folder); - - Ref<DirAccess> da = DirAccess::open(dir); - da->list_dir_begin(); - String f = da->get_next(); - while (!f.is_empty()) { - if (f == "." || f == "..") { - f = da->get_next(); - continue; - } - if (da->is_link(f)) { - OS::Time time = OS::get_singleton()->get_time(); - OS::Date date = OS::get_singleton()->get_date(); - - zip_fileinfo zipfi; - zipfi.tmz_date.tm_hour = time.hour; - zipfi.tmz_date.tm_mday = date.day; - zipfi.tmz_date.tm_min = time.minute; - zipfi.tmz_date.tm_mon = date.month - 1; // Note: "tm" month range - 0..11, Godot month range - 1..12, https://www.cplusplus.com/reference/ctime/tm/ - zipfi.tmz_date.tm_sec = time.second; - zipfi.tmz_date.tm_year = date.year; - zipfi.dosDate = 0; - // 0120000: symbolic link type - // 0000755: permissions rwxr-xr-x - // 0000644: permissions rw-r--r-- - uint32_t _mode = 0120644; - zipfi.external_fa = (_mode << 16L) | !(_mode & 0200); - zipfi.internal_fa = 0; - - zipOpenNewFileInZip4(p_zip, - p_folder.plus_file(f).utf8().get_data(), - &zipfi, - nullptr, - 0, - nullptr, - 0, - nullptr, - Z_DEFLATED, - Z_DEFAULT_COMPRESSION, - 0, - -MAX_WBITS, - DEF_MEM_LEVEL, - Z_DEFAULT_STRATEGY, - nullptr, - 0, - 0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions - 0); - - String target = da->read_link(f); - zipWriteInFileInZip(p_zip, target.utf8().get_data(), target.utf8().size()); - zipCloseFileInZip(p_zip); - } else if (da->current_is_dir()) { - _zip_folder_recursive(p_zip, p_root_path, p_folder.plus_file(f), p_pkg_name); - } else { - bool is_executable = (p_folder.ends_with("MacOS") && (f == p_pkg_name)) || p_folder.ends_with("Helpers") || f.ends_with(".command"); - - OS::Time time = OS::get_singleton()->get_time(); - OS::Date date = OS::get_singleton()->get_date(); - - zip_fileinfo zipfi; - zipfi.tmz_date.tm_hour = time.hour; - zipfi.tmz_date.tm_mday = date.day; - zipfi.tmz_date.tm_min = time.minute; - zipfi.tmz_date.tm_mon = date.month - 1; // Note: "tm" month range - 0..11, Godot month range - 1..12, https://www.cplusplus.com/reference/ctime/tm/ - zipfi.tmz_date.tm_sec = time.second; - zipfi.tmz_date.tm_year = date.year; - zipfi.dosDate = 0; - // 0100000: regular file type - // 0000755: permissions rwxr-xr-x - // 0000644: permissions rw-r--r-- - uint32_t _mode = (is_executable ? 0100755 : 0100644); - zipfi.external_fa = (_mode << 16L) | !(_mode & 0200); - zipfi.internal_fa = 0; - - zipOpenNewFileInZip4(p_zip, - p_folder.plus_file(f).utf8().get_data(), - &zipfi, - nullptr, - 0, - nullptr, - 0, - nullptr, - Z_DEFLATED, - Z_DEFAULT_COMPRESSION, - 0, - -MAX_WBITS, - DEF_MEM_LEVEL, - Z_DEFAULT_STRATEGY, - nullptr, - 0, - 0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions - 0); - - Ref<FileAccess> fa = FileAccess::open(dir.plus_file(f), FileAccess::READ); - if (fa.is_null()) { - add_message(EXPORT_MESSAGE_ERROR, TTR("ZIP Creation"), vformat(TTR("Could not open file to read from path \"%s\"."), dir.plus_file(f))); - return; - } - const int bufsize = 16384; - uint8_t buf[bufsize]; - - while (true) { - uint64_t got = fa->get_buffer(buf, bufsize); - if (got == 0) { - break; - } - zipWriteInFileInZip(p_zip, buf, got); - } - - zipCloseFileInZip(p_zip); - } - f = da->get_next(); - } - da->list_dir_end(); -} - -bool EditorExportPlatformOSX::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { - String err; - bool valid = false; - - // Look for export templates (custom templates). - bool dvalid = false; - bool rvalid = false; - - if (p_preset->get("custom_template/debug") != "") { - dvalid = FileAccess::exists(p_preset->get("custom_template/debug")); - if (!dvalid) { - err += TTR("Custom debug template not found.") + "\n"; - } - } - if (p_preset->get("custom_template/release") != "") { - rvalid = FileAccess::exists(p_preset->get("custom_template/release")); - if (!rvalid) { - err += TTR("Custom release template not found.") + "\n"; - } - } - - // Look for export templates (official templates, check only is custom templates are not set). - if (!dvalid || !rvalid) { - dvalid = exists_export_template("osx.zip", &err); - rvalid = dvalid; // Both in the same ZIP. - } - - valid = dvalid || rvalid; - r_missing_templates = !valid; - - String identifier = p_preset->get("application/bundle_identifier"); - String pn_err; - if (!is_package_name_valid(identifier, &pn_err)) { - err += TTR("Invalid bundle identifier:") + " " + pn_err + "\n"; - valid = false; - } - - bool sign_enabled = p_preset->get("codesign/enable"); - -#ifdef OSX_ENABLED - bool noto_enabled = p_preset->get("notarization/enable"); - bool ad_hoc = ((p_preset->get("codesign/identity") == "") || (p_preset->get("codesign/identity") == "-")); - - if (!ad_hoc && (bool)EditorSettings::get_singleton()->get("export/macos/force_builtin_codesign")) { - err += TTR("Warning: Built-in \"codesign\" is selected in the Editor Settings. Code signing is limited to ad-hoc signature only.") + "\n"; - } - if (!ad_hoc && !FileAccess::exists("/usr/bin/codesign") && !FileAccess::exists("/bin/codesign")) { - err += TTR("Warning: Xcode command line tools are not installed, using built-in \"codesign\". Code signing is limited to ad-hoc signature only.") + "\n"; - } - - if (noto_enabled) { - if (ad_hoc) { - err += TTR("Notarization: Notarization with an ad-hoc signature is not supported.") + "\n"; - valid = false; - } - if (!sign_enabled) { - err += TTR("Notarization: Code signing is required for notarization.") + "\n"; - valid = false; - } - if (!(bool)p_preset->get("codesign/hardened_runtime")) { - err += TTR("Notarization: Hardened runtime is required for notarization.") + "\n"; - valid = false; - } - if (!(bool)p_preset->get("codesign/timestamp")) { - err += TTR("Notarization: Timestamping is required for notarization.") + "\n"; - valid = false; - } - if (p_preset->get("notarization/apple_id_name") == "") { - err += TTR("Notarization: Apple ID name not specified.") + "\n"; - valid = false; - } - if (p_preset->get("notarization/apple_id_password") == "") { - err += TTR("Notarization: Apple ID password not specified.") + "\n"; - valid = false; - } - } else { - err += TTR("Warning: Notarization is disabled. The exported project will be blocked by Gatekeeper if it's downloaded from an unknown source.") + "\n"; - if (!sign_enabled) { - err += TTR("Code signing is disabled. The exported project will not run on Macs with enabled Gatekeeper and Apple Silicon powered Macs.") + "\n"; - } else { - if ((bool)p_preset->get("codesign/hardened_runtime") && ad_hoc) { - err += TTR("Hardened Runtime is not compatible with ad-hoc signature, and will be disabled!") + "\n"; - } - if ((bool)p_preset->get("codesign/timestamp") && ad_hoc) { - err += TTR("Timestamping is not compatible with ad-hoc signature, and will be disabled!") + "\n"; - } - } - } -#else - err += TTR("Warning: Notarization is not supported from this OS. The exported project will be blocked by Gatekeeper if it's downloaded from an unknown source.") + "\n"; - if (!sign_enabled) { - err += TTR("Code signing is disabled. The exported project will not run on Macs with enabled Gatekeeper and Apple Silicon powered Macs.") + "\n"; - } -#endif - - if (sign_enabled) { - if ((bool)p_preset->get("codesign/entitlements/audio_input") && ((String)p_preset->get("privacy/microphone_usage_description")).is_empty()) { - err += TTR("Privacy: Microphone access is enabled, but usage description is not specified.") + "\n"; - valid = false; - } - if ((bool)p_preset->get("codesign/entitlements/camera") && ((String)p_preset->get("privacy/camera_usage_description")).is_empty()) { - err += TTR("Privacy: Camera access is enabled, but usage description is not specified.") + "\n"; - valid = false; - } - if ((bool)p_preset->get("codesign/entitlements/location") && ((String)p_preset->get("privacy/location_usage_description")).is_empty()) { - err += TTR("Privacy: Location information access is enabled, but usage description is not specified.") + "\n"; - valid = false; - } - if ((bool)p_preset->get("codesign/entitlements/address_book") && ((String)p_preset->get("privacy/address_book_usage_description")).is_empty()) { - err += TTR("Privacy: Address book access is enabled, but usage description is not specified.") + "\n"; - valid = false; - } - if ((bool)p_preset->get("codesign/entitlements/calendars") && ((String)p_preset->get("privacy/calendar_usage_description")).is_empty()) { - err += TTR("Privacy: Calendar access is enabled, but usage description is not specified.") + "\n"; - valid = false; - } - if ((bool)p_preset->get("codesign/entitlements/photos_library") && ((String)p_preset->get("privacy/photos_library_usage_description")).is_empty()) { - err += TTR("Privacy: Photo library access is enabled, but usage description is not specified.") + "\n"; - valid = false; - } - } - - if (!err.is_empty()) { - r_error = err; - } - return valid; -} - -EditorExportPlatformOSX::EditorExportPlatformOSX() { - logo = ImageTexture::create_from_image(memnew(Image(_osx_logo))); -} - -EditorExportPlatformOSX::~EditorExportPlatformOSX() { -} diff --git a/platform/osx/export/export_plugin.h b/platform/osx/export/export_plugin.h deleted file mode 100644 index ec97d4139f..0000000000 --- a/platform/osx/export/export_plugin.h +++ /dev/null @@ -1,137 +0,0 @@ -/*************************************************************************/ -/* export_plugin.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* 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. */ -/*************************************************************************/ - -#ifndef OSX_EXPORT_PLUGIN_H -#define OSX_EXPORT_PLUGIN_H - -#include "core/config/project_settings.h" -#include "core/io/dir_access.h" -#include "core/io/file_access.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/version.h" -#include "editor/editor_export.h" -#include "editor/editor_settings.h" -#include "platform/osx/logo.gen.h" - -#include <sys/stat.h> - -class EditorExportPlatformOSX : public EditorExportPlatform { - GDCLASS(EditorExportPlatformOSX, EditorExportPlatform); - - int version_code = 0; - - Ref<ImageTexture> logo; - - void _fix_plist(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &plist, const String &p_binary); - void _make_icon(const Ref<Image> &p_icon, Vector<uint8_t> &p_data); - - Error _notarize(const Ref<EditorExportPreset> &p_preset, const String &p_path); - Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path, bool p_warn = true); - Error _code_sign_directory(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path, bool p_should_error_on_non_code = true); - Error _copy_and_sign_files(Ref<DirAccess> &dir_access, const String &p_src_path, const String &p_in_app_path, - bool p_sign_enabled, const Ref<EditorExportPreset> &p_preset, const String &p_ent_path, - bool p_should_error_on_non_code_sign); - Error _export_osx_plugins_for(Ref<EditorExportPlugin> p_editor_export_plugin, const String &p_app_path_name, - Ref<DirAccess> &dir_access, bool p_sign_enabled, const Ref<EditorExportPreset> &p_preset, - const String &p_ent_path); - Error _create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name); - void _zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name); - Error _export_debug_script(const Ref<EditorExportPreset> &p_preset, const String &p_app_name, const String &p_pkg_name, const String &p_path); - - bool use_codesign() const { return true; } -#ifdef OSX_ENABLED - bool use_dmg() const { return true; } -#else - bool use_dmg() const { return false; } -#endif - - bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const { - String pname = p_package; - - if (pname.length() == 0) { - if (r_error) { - *r_error = TTR("Identifier is missing."); - } - return false; - } - - for (int i = 0; i < pname.length(); i++) { - char32_t c = pname[i]; - if (!(is_ascii_alphanumeric_char(c) || c == '-' || c == '.')) { - if (r_error) { - *r_error = vformat(TTR("The character '%s' is not allowed in Identifier."), String::chr(c)); - } - return false; - } - } - - return true; - } - -protected: - virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) override; - virtual void get_export_options(List<ExportOption> *r_options) override; - virtual bool get_export_option_visibility(const String &p_option, const HashMap<StringName, Variant> &p_options) const override; - -public: - virtual String get_name() const override { return "macOS"; } - virtual String get_os_name() const override { return "macOS"; } - virtual Ref<Texture2D> get_logo() const override { return logo; } - - virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override { - List<String> list; - if (use_dmg()) { - list.push_back("dmg"); - } - list.push_back("zip"); - list.push_back("app"); - return list; - } - virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; - - virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override; - - virtual void get_platform_features(List<String> *r_features) override { - r_features->push_back("pc"); - r_features->push_back("s3tc"); - r_features->push_back("macos"); - } - - virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, HashSet<String> &p_features) override { - } - - EditorExportPlatformOSX(); - ~EditorExportPlatformOSX(); -}; - -#endif diff --git a/platform/osx/export/lipo.cpp b/platform/osx/export/lipo.cpp deleted file mode 100644 index 82baf18c52..0000000000 --- a/platform/osx/export/lipo.cpp +++ /dev/null @@ -1,236 +0,0 @@ -/*************************************************************************/ -/* lipo.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "modules/modules_enabled.gen.h" // For regex. - -#include "lipo.h" - -#ifdef MODULE_REGEX_ENABLED - -bool LipO::is_lipo(const String &p_path) { - Ref<FileAccess> fb = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_V_MSG(fb.is_null(), false, vformat("LipO: Can't open file: \"%s\".", p_path)); - uint32_t magic = fb->get_32(); - return (magic == 0xbebafeca || magic == 0xcafebabe || magic == 0xbfbafeca || magic == 0xcafebabf); -} - -bool LipO::create_file(const String &p_output_path, const PackedStringArray &p_files) { - close(); - - fa = FileAccess::open(p_output_path, FileAccess::WRITE); - ERR_FAIL_COND_V_MSG(fa.is_null(), false, vformat("LipO: Can't open file: \"%s\".", p_output_path)); - - uint64_t max_size = 0; - for (int i = 0; i < p_files.size(); i++) { - MachO mh; - if (!mh.open_file(p_files[i])) { - ERR_FAIL_V_MSG(false, vformat("LipO: Invalid MachO file: \"%s.\"", p_files[i])); - } - - FatArch arch; - arch.cputype = mh.get_cputype(); - arch.cpusubtype = mh.get_cpusubtype(); - arch.offset = 0; - arch.size = mh.get_size(); - arch.align = mh.get_align(); - max_size += arch.size; - - archs.push_back(arch); - - Ref<FileAccess> fb = FileAccess::open(p_files[i], FileAccess::READ); - if (fb.is_null()) { - close(); - ERR_FAIL_V_MSG(false, vformat("LipO: Can't open file: \"%s.\"", p_files[i])); - } - } - - // Write header. - bool is_64 = (max_size >= std::numeric_limits<uint32_t>::max()); - if (is_64) { - fa->store_32(0xbfbafeca); - } else { - fa->store_32(0xbebafeca); - } - fa->store_32(BSWAP32(archs.size())); - uint64_t offset = archs.size() * (is_64 ? 32 : 20) + 8; - for (int i = 0; i < archs.size(); i++) { - archs.write[i].offset = offset + PAD(offset, uint64_t(1) << archs[i].align); - if (is_64) { - fa->store_32(BSWAP32(archs[i].cputype)); - fa->store_32(BSWAP32(archs[i].cpusubtype)); - fa->store_64(BSWAP64(archs[i].offset)); - fa->store_64(BSWAP64(archs[i].size)); - fa->store_32(BSWAP32(archs[i].align)); - fa->store_32(0); - } else { - fa->store_32(BSWAP32(archs[i].cputype)); - fa->store_32(BSWAP32(archs[i].cpusubtype)); - fa->store_32(BSWAP32(archs[i].offset)); - fa->store_32(BSWAP32(archs[i].size)); - fa->store_32(BSWAP32(archs[i].align)); - } - offset = archs[i].offset + archs[i].size; - } - - // Write files and padding. - for (int i = 0; i < archs.size(); i++) { - Ref<FileAccess> fb = FileAccess::open(p_files[i], FileAccess::READ); - if (fb.is_null()) { - close(); - ERR_FAIL_V_MSG(false, vformat("LipO: Can't open file: \"%s.\"", p_files[i])); - } - uint64_t cur = fa->get_position(); - for (uint64_t j = cur; j < archs[i].offset; j++) { - fa->store_8(0); - } - int pages = archs[i].size / 4096; - int remain = archs[i].size % 4096; - unsigned char step[4096]; - for (int j = 0; j < pages; j++) { - uint64_t br = fb->get_buffer(step, 4096); - if (br > 0) { - fa->store_buffer(step, br); - } - } - uint64_t br = fb->get_buffer(step, remain); - if (br > 0) { - fa->store_buffer(step, br); - } - } - return true; -} - -bool LipO::open_file(const String &p_path) { - close(); - - fa = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_V_MSG(fa.is_null(), false, vformat("LipO: Can't open file: \"%s\".", p_path)); - - uint32_t magic = fa->get_32(); - if (magic == 0xbebafeca) { - // 32-bit fat binary, bswap. - uint32_t nfat_arch = BSWAP32(fa->get_32()); - for (uint32_t i = 0; i < nfat_arch; i++) { - FatArch arch; - arch.cputype = BSWAP32(fa->get_32()); - arch.cpusubtype = BSWAP32(fa->get_32()); - arch.offset = BSWAP32(fa->get_32()); - arch.size = BSWAP32(fa->get_32()); - arch.align = BSWAP32(fa->get_32()); - - archs.push_back(arch); - } - } else if (magic == 0xcafebabe) { - // 32-bit fat binary. - uint32_t nfat_arch = fa->get_32(); - for (uint32_t i = 0; i < nfat_arch; i++) { - FatArch arch; - arch.cputype = fa->get_32(); - arch.cpusubtype = fa->get_32(); - arch.offset = fa->get_32(); - arch.size = fa->get_32(); - arch.align = fa->get_32(); - - archs.push_back(arch); - } - } else if (magic == 0xbfbafeca) { - // 64-bit fat binary, bswap. - uint32_t nfat_arch = BSWAP32(fa->get_32()); - for (uint32_t i = 0; i < nfat_arch; i++) { - FatArch arch; - arch.cputype = BSWAP32(fa->get_32()); - arch.cpusubtype = BSWAP32(fa->get_32()); - arch.offset = BSWAP64(fa->get_64()); - arch.size = BSWAP64(fa->get_64()); - arch.align = BSWAP32(fa->get_32()); - fa->get_32(); // Skip, reserved. - - archs.push_back(arch); - } - } else if (magic == 0xcafebabf) { - // 64-bit fat binary. - uint32_t nfat_arch = fa->get_32(); - for (uint32_t i = 0; i < nfat_arch; i++) { - FatArch arch; - arch.cputype = fa->get_32(); - arch.cpusubtype = fa->get_32(); - arch.offset = fa->get_64(); - arch.size = fa->get_64(); - arch.align = fa->get_32(); - fa->get_32(); // Skip, reserved. - - archs.push_back(arch); - } - } else { - close(); - ERR_FAIL_V_MSG(false, vformat("LipO: Invalid fat binary: \"%s\".", p_path)); - } - return true; -} - -int LipO::get_arch_count() const { - ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "LipO: File not opened."); - return archs.size(); -} - -bool LipO::extract_arch(int p_index, const String &p_path) { - ERR_FAIL_COND_V_MSG(fa.is_null(), false, "LipO: File not opened."); - ERR_FAIL_INDEX_V(p_index, archs.size(), false); - - Ref<FileAccess> fb = FileAccess::open(p_path, FileAccess::WRITE); - ERR_FAIL_COND_V_MSG(fb.is_null(), false, vformat("LipO: Can't open file: \"%s\".", p_path)); - - fa->seek(archs[p_index].offset); - - int pages = archs[p_index].size / 4096; - int remain = archs[p_index].size % 4096; - unsigned char step[4096]; - for (int i = 0; i < pages; i++) { - uint64_t br = fa->get_buffer(step, 4096); - if (br > 0) { - fb->store_buffer(step, br); - } - } - uint64_t br = fa->get_buffer(step, remain); - if (br > 0) { - fb->store_buffer(step, br); - } - return true; -} - -void LipO::close() { - archs.clear(); -} - -LipO::~LipO() { - close(); -} - -#endif // MODULE_REGEX_ENABLED diff --git a/platform/osx/export/lipo.h b/platform/osx/export/lipo.h deleted file mode 100644 index 0e419be17e..0000000000 --- a/platform/osx/export/lipo.h +++ /dev/null @@ -1,76 +0,0 @@ -/*************************************************************************/ -/* lipo.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* 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. */ -/*************************************************************************/ - -// Universal / Universal 2 fat binary file creator and extractor. - -#ifndef LIPO_H -#define LIPO_H - -#include "core/io/file_access.h" -#include "core/object/ref_counted.h" -#include "modules/modules_enabled.gen.h" // For regex. - -#include "macho.h" - -#ifdef MODULE_REGEX_ENABLED - -class LipO : public RefCounted { - struct FatArch { - uint32_t cputype; - uint32_t cpusubtype; - uint64_t offset; - uint64_t size; - uint32_t align; - }; - - Ref<FileAccess> fa; - Vector<FatArch> archs; - - static inline size_t PAD(size_t s, size_t a) { - return (a - s % a); - } - -public: - static bool is_lipo(const String &p_path); - - bool create_file(const String &p_output_path, const PackedStringArray &p_files); - - bool open_file(const String &p_path); - int get_arch_count() const; - bool extract_arch(int p_index, const String &p_path); - - void close(); - - ~LipO(); -}; - -#endif // MODULE_REGEX_ENABLED - -#endif // LIPO_H diff --git a/platform/osx/export/macho.cpp b/platform/osx/export/macho.cpp deleted file mode 100644 index e6e67eff06..0000000000 --- a/platform/osx/export/macho.cpp +++ /dev/null @@ -1,548 +0,0 @@ -/*************************************************************************/ -/* macho.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "modules/modules_enabled.gen.h" // For regex. - -#include "macho.h" - -#ifdef MODULE_REGEX_ENABLED - -uint32_t MachO::seg_align(uint64_t p_vmaddr, uint32_t p_min, uint32_t p_max) { - uint32_t align = p_max; - if (p_vmaddr != 0) { - uint64_t seg_align = 1; - align = 0; - while ((seg_align & p_vmaddr) == 0) { - seg_align = seg_align << 1; - align++; - } - align = CLAMP(align, p_min, p_max); - } - return align; -} - -bool MachO::alloc_signature(uint64_t p_size) { - ERR_FAIL_COND_V_MSG(fa.is_null(), false, "MachO: File not opened."); - if (signature_offset != 0) { - // Nothing to do, already have signature load command. - return true; - } - if (lc_limit == 0 || lc_limit + 16 > exe_base) { - ERR_FAIL_V_MSG(false, "MachO: Can't allocate signature load command, please use \"codesign_allocate\" utility first."); - } else { - // Add signature load command. - signature_offset = lc_limit; - - fa->seek(lc_limit); - LoadCommandHeader lc; - lc.cmd = LC_CODE_SIGNATURE; - lc.cmdsize = 16; - if (swap) { - lc.cmdsize = BSWAP32(lc.cmdsize); - } - fa->store_buffer((const uint8_t *)&lc, sizeof(LoadCommandHeader)); - - uint32_t lc_offset = fa->get_length() + PAD(fa->get_length(), 16); - uint32_t lc_size = 0; - if (swap) { - lc_offset = BSWAP32(lc_offset); - lc_size = BSWAP32(lc_size); - } - fa->store_32(lc_offset); - fa->store_32(lc_size); - - // Write new command number. - fa->seek(0x10); - uint32_t ncmds = fa->get_32(); - uint32_t cmdssize = fa->get_32(); - if (swap) { - ncmds = BSWAP32(ncmds); - cmdssize = BSWAP32(cmdssize); - } - ncmds += 1; - cmdssize += 16; - if (swap) { - ncmds = BSWAP32(ncmds); - cmdssize = BSWAP32(cmdssize); - } - fa->seek(0x10); - fa->store_32(ncmds); - fa->store_32(cmdssize); - - lc_limit = lc_limit + sizeof(LoadCommandHeader) + 8; - - return true; - } -} - -bool MachO::is_macho(const String &p_path) { - Ref<FileAccess> fb = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_V_MSG(fb.is_null(), false, vformat("MachO: Can't open file: \"%s\".", p_path)); - uint32_t magic = fb->get_32(); - return (magic == 0xcefaedfe || magic == 0xfeedface || magic == 0xcffaedfe || magic == 0xfeedfacf); -} - -bool MachO::open_file(const String &p_path) { - fa = FileAccess::open(p_path, FileAccess::READ_WRITE); - ERR_FAIL_COND_V_MSG(fa.is_null(), false, vformat("MachO: Can't open file: \"%s\".", p_path)); - uint32_t magic = fa->get_32(); - MachHeader mach_header; - - // Read MachO header. - swap = (magic == 0xcffaedfe || magic == 0xcefaedfe); - if (magic == 0xcefaedfe || magic == 0xfeedface) { - // Thin 32-bit binary. - fa->get_buffer((uint8_t *)&mach_header, sizeof(MachHeader)); - } else if (magic == 0xcffaedfe || magic == 0xfeedfacf) { - // Thin 64-bit binary. - fa->get_buffer((uint8_t *)&mach_header, sizeof(MachHeader)); - fa->get_32(); // Skip extra reserved field. - } else { - ERR_FAIL_V_MSG(false, vformat("MachO: File is not a valid MachO binary: \"%s\".", p_path)); - } - - if (swap) { - mach_header.ncmds = BSWAP32(mach_header.ncmds); - mach_header.cpusubtype = BSWAP32(mach_header.cpusubtype); - mach_header.cputype = BSWAP32(mach_header.cputype); - } - cpusubtype = mach_header.cpusubtype; - cputype = mach_header.cputype; - align = 0; - exe_base = std::numeric_limits<uint64_t>::max(); - exe_limit = 0; - lc_limit = 0; - link_edit_offset = 0; - signature_offset = 0; - - // Read load commands. - for (uint32_t i = 0; i < mach_header.ncmds; i++) { - LoadCommandHeader lc; - fa->get_buffer((uint8_t *)&lc, sizeof(LoadCommandHeader)); - if (swap) { - lc.cmd = BSWAP32(lc.cmd); - lc.cmdsize = BSWAP32(lc.cmdsize); - } - uint64_t ps = fa->get_position(); - switch (lc.cmd) { - case LC_SEGMENT: { - LoadCommandSegment lc_seg; - fa->get_buffer((uint8_t *)&lc_seg, sizeof(LoadCommandSegment)); - if (swap) { - lc_seg.nsects = BSWAP32(lc_seg.nsects); - lc_seg.vmaddr = BSWAP32(lc_seg.vmaddr); - lc_seg.vmsize = BSWAP32(lc_seg.vmsize); - } - align = MAX(align, seg_align(lc_seg.vmaddr, 2, 15)); - if (String(lc_seg.segname) == "__TEXT") { - exe_limit = MAX(exe_limit, lc_seg.vmsize); - for (uint32_t j = 0; j < lc_seg.nsects; j++) { - Section lc_sect; - fa->get_buffer((uint8_t *)&lc_sect, sizeof(Section)); - if (String(lc_sect.sectname) == "__text") { - if (swap) { - exe_base = MIN(exe_base, BSWAP32(lc_sect.offset)); - } else { - exe_base = MIN(exe_base, lc_sect.offset); - } - } - if (swap) { - align = MAX(align, BSWAP32(lc_sect.align)); - } else { - align = MAX(align, lc_sect.align); - } - } - } else if (String(lc_seg.segname) == "__LINKEDIT") { - link_edit_offset = ps - 8; - } - } break; - case LC_SEGMENT_64: { - LoadCommandSegment64 lc_seg; - fa->get_buffer((uint8_t *)&lc_seg, sizeof(LoadCommandSegment64)); - if (swap) { - lc_seg.nsects = BSWAP32(lc_seg.nsects); - lc_seg.vmaddr = BSWAP64(lc_seg.vmaddr); - lc_seg.vmsize = BSWAP64(lc_seg.vmsize); - } - align = MAX(align, seg_align(lc_seg.vmaddr, 3, 15)); - if (String(lc_seg.segname) == "__TEXT") { - exe_limit = MAX(exe_limit, lc_seg.vmsize); - for (uint32_t j = 0; j < lc_seg.nsects; j++) { - Section64 lc_sect; - fa->get_buffer((uint8_t *)&lc_sect, sizeof(Section64)); - if (String(lc_sect.sectname) == "__text") { - if (swap) { - exe_base = MIN(exe_base, BSWAP32(lc_sect.offset)); - } else { - exe_base = MIN(exe_base, lc_sect.offset); - } - if (swap) { - align = MAX(align, BSWAP32(lc_sect.align)); - } else { - align = MAX(align, lc_sect.align); - } - } - } - } else if (String(lc_seg.segname) == "__LINKEDIT") { - link_edit_offset = ps - 8; - } - } break; - case LC_CODE_SIGNATURE: { - signature_offset = ps - 8; - } break; - default: { - } break; - } - fa->seek(ps + lc.cmdsize - 8); - lc_limit = ps + lc.cmdsize - 8; - } - - if (exe_limit == 0 || lc_limit == 0) { - ERR_FAIL_V_MSG(false, vformat("MachO: No load commands or executable code found: \"%s\".", p_path)); - } - - return true; -} - -uint64_t MachO::get_exe_base() { - ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "MachO: File not opened."); - return exe_base; -} - -uint64_t MachO::get_exe_limit() { - ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "MachO: File not opened."); - return exe_limit; -} - -int32_t MachO::get_align() { - ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "MachO: File not opened."); - return align; -} - -uint32_t MachO::get_cputype() { - ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "MachO: File not opened."); - return cputype; -} - -uint32_t MachO::get_cpusubtype() { - ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "MachO: File not opened."); - return cpusubtype; -} - -uint64_t MachO::get_size() { - ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "MachO: File not opened."); - return fa->get_length(); -} - -uint64_t MachO::get_signature_offset() { - ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "MachO: File not opened."); - ERR_FAIL_COND_V_MSG(signature_offset == 0, 0, "MachO: No signature load command."); - - fa->seek(signature_offset + 8); - if (swap) { - return BSWAP32(fa->get_32()); - } else { - return fa->get_32(); - } -} - -uint64_t MachO::get_code_limit() { - ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "MachO: File not opened."); - - if (signature_offset == 0) { - return fa->get_length() + PAD(fa->get_length(), 16); - } else { - return get_signature_offset(); - } -} - -uint64_t MachO::get_signature_size() { - ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "MachO: File not opened."); - ERR_FAIL_COND_V_MSG(signature_offset == 0, 0, "MachO: No signature load command."); - - fa->seek(signature_offset + 12); - if (swap) { - return BSWAP32(fa->get_32()); - } else { - return fa->get_32(); - } -} - -bool MachO::is_signed() { - ERR_FAIL_COND_V_MSG(fa.is_null(), false, "MachO: File not opened."); - if (signature_offset == 0) { - return false; - } - - fa->seek(get_signature_offset()); - uint32_t magic = BSWAP32(fa->get_32()); - if (magic != 0xfade0cc0) { - return false; // No SuperBlob found. - } - fa->get_32(); // Skip size field, unused. - uint32_t count = BSWAP32(fa->get_32()); - for (uint32_t i = 0; i < count; i++) { - uint32_t index_type = BSWAP32(fa->get_32()); - uint32_t offset = BSWAP32(fa->get_32()); - if (index_type == 0x00000000) { // CodeDirectory index type. - fa->seek(get_signature_offset() + offset + 12); - uint32_t flags = BSWAP32(fa->get_32()); - if (flags & 0x20000) { - return false; // Found CD, linker-signed. - } else { - return true; // Found CD, not linker-signed. - } - } - } - return false; // No CD found. -} - -PackedByteArray MachO::get_cdhash_sha1() { - ERR_FAIL_COND_V_MSG(fa.is_null(), PackedByteArray(), "MachO: File not opened."); - if (signature_offset == 0) { - return PackedByteArray(); - } - - fa->seek(get_signature_offset()); - uint32_t magic = BSWAP32(fa->get_32()); - if (magic != 0xfade0cc0) { - return PackedByteArray(); // No SuperBlob found. - } - fa->get_32(); // Skip size field, unused. - uint32_t count = BSWAP32(fa->get_32()); - for (uint32_t i = 0; i < count; i++) { - fa->get_32(); // Index type, skip. - uint32_t offset = BSWAP32(fa->get_32()); - uint64_t pos = fa->get_position(); - - fa->seek(get_signature_offset() + offset); - uint32_t cdmagic = BSWAP32(fa->get_32()); - uint32_t cdsize = BSWAP32(fa->get_32()); - if (cdmagic == 0xfade0c02) { // CodeDirectory. - fa->seek(get_signature_offset() + offset + 36); - uint8_t hash_size = fa->get_8(); - uint8_t hash_type = fa->get_8(); - if (hash_size == 0x14 && hash_type == 0x01) { /* SHA-1 */ - PackedByteArray hash; - hash.resize(0x14); - - fa->seek(get_signature_offset() + offset); - PackedByteArray blob; - blob.resize(cdsize); - fa->get_buffer(blob.ptrw(), cdsize); - - CryptoCore::SHA1Context ctx; - ctx.start(); - ctx.update(blob.ptr(), blob.size()); - ctx.finish(hash.ptrw()); - - return hash; - } - } - fa->seek(pos); - } - return PackedByteArray(); -} - -PackedByteArray MachO::get_cdhash_sha256() { - ERR_FAIL_COND_V_MSG(fa.is_null(), PackedByteArray(), "MachO: File not opened."); - if (signature_offset == 0) { - return PackedByteArray(); - } - - fa->seek(get_signature_offset()); - uint32_t magic = BSWAP32(fa->get_32()); - if (magic != 0xfade0cc0) { - return PackedByteArray(); // No SuperBlob found. - } - fa->get_32(); // Skip size field, unused. - uint32_t count = BSWAP32(fa->get_32()); - for (uint32_t i = 0; i < count; i++) { - fa->get_32(); // Index type, skip. - uint32_t offset = BSWAP32(fa->get_32()); - uint64_t pos = fa->get_position(); - - fa->seek(get_signature_offset() + offset); - uint32_t cdmagic = BSWAP32(fa->get_32()); - uint32_t cdsize = BSWAP32(fa->get_32()); - if (cdmagic == 0xfade0c02) { // CodeDirectory. - fa->seek(get_signature_offset() + offset + 36); - uint8_t hash_size = fa->get_8(); - uint8_t hash_type = fa->get_8(); - if (hash_size == 0x20 && hash_type == 0x02) { /* SHA-256 */ - PackedByteArray hash; - hash.resize(0x20); - - fa->seek(get_signature_offset() + offset); - PackedByteArray blob; - blob.resize(cdsize); - fa->get_buffer(blob.ptrw(), cdsize); - - CryptoCore::SHA256Context ctx; - ctx.start(); - ctx.update(blob.ptr(), blob.size()); - ctx.finish(hash.ptrw()); - - return hash; - } - } - fa->seek(pos); - } - return PackedByteArray(); -} - -PackedByteArray MachO::get_requirements() { - ERR_FAIL_COND_V_MSG(fa.is_null(), PackedByteArray(), "MachO: File not opened."); - if (signature_offset == 0) { - return PackedByteArray(); - } - - fa->seek(get_signature_offset()); - uint32_t magic = BSWAP32(fa->get_32()); - if (magic != 0xfade0cc0) { - return PackedByteArray(); // No SuperBlob found. - } - fa->get_32(); // Skip size field, unused. - uint32_t count = BSWAP32(fa->get_32()); - for (uint32_t i = 0; i < count; i++) { - fa->get_32(); // Index type, skip. - uint32_t offset = BSWAP32(fa->get_32()); - uint64_t pos = fa->get_position(); - - fa->seek(get_signature_offset() + offset); - uint32_t rqmagic = BSWAP32(fa->get_32()); - uint32_t rqsize = BSWAP32(fa->get_32()); - if (rqmagic == 0xfade0c01) { // Requirements. - PackedByteArray blob; - fa->seek(get_signature_offset() + offset); - blob.resize(rqsize); - fa->get_buffer(blob.ptrw(), rqsize); - return blob; - } - fa->seek(pos); - } - return PackedByteArray(); -} - -const Ref<FileAccess> MachO::get_file() const { - return fa; -} - -Ref<FileAccess> MachO::get_file() { - return fa; -} - -bool MachO::set_signature_size(uint64_t p_size) { - ERR_FAIL_COND_V_MSG(fa.is_null(), false, "MachO: File not opened."); - - // Ensure signature load command exists. - ERR_FAIL_COND_V_MSG(link_edit_offset == 0, false, "MachO: No __LINKEDIT segment found."); - ERR_FAIL_COND_V_MSG(!alloc_signature(p_size), false, "MachO: Can't allocate signature load command."); - - // Update signature load command. - uint64_t old_size = get_signature_size(); - uint64_t new_size = p_size + PAD(p_size, 16384); - - if (new_size <= old_size) { - fa->seek(get_signature_offset()); - for (uint64_t i = 0; i < old_size; i++) { - fa->store_8(0x00); - } - return true; - } - - fa->seek(signature_offset + 12); - if (swap) { - fa->store_32(BSWAP32(new_size)); - } else { - fa->store_32(new_size); - } - - uint64_t end = get_signature_offset() + new_size; - - // Update "__LINKEDIT" segment. - LoadCommandHeader lc; - fa->seek(link_edit_offset); - fa->get_buffer((uint8_t *)&lc, sizeof(LoadCommandHeader)); - if (swap) { - lc.cmd = BSWAP32(lc.cmd); - lc.cmdsize = BSWAP32(lc.cmdsize); - } - switch (lc.cmd) { - case LC_SEGMENT: { - LoadCommandSegment lc_seg; - fa->get_buffer((uint8_t *)&lc_seg, sizeof(LoadCommandSegment)); - if (swap) { - lc_seg.vmsize = BSWAP32(lc_seg.vmsize); - lc_seg.filesize = BSWAP32(lc_seg.filesize); - lc_seg.fileoff = BSWAP32(lc_seg.fileoff); - } - - lc_seg.vmsize = end - lc_seg.fileoff; - lc_seg.vmsize += PAD(lc_seg.vmsize, 4096); - lc_seg.filesize = end - lc_seg.fileoff; - - if (swap) { - lc_seg.vmsize = BSWAP32(lc_seg.vmsize); - lc_seg.filesize = BSWAP32(lc_seg.filesize); - } - fa->seek(link_edit_offset + 8); - fa->store_buffer((const uint8_t *)&lc_seg, sizeof(LoadCommandSegment)); - } break; - case LC_SEGMENT_64: { - LoadCommandSegment64 lc_seg; - fa->get_buffer((uint8_t *)&lc_seg, sizeof(LoadCommandSegment64)); - if (swap) { - lc_seg.vmsize = BSWAP64(lc_seg.vmsize); - lc_seg.filesize = BSWAP64(lc_seg.filesize); - lc_seg.fileoff = BSWAP64(lc_seg.fileoff); - } - lc_seg.vmsize = end - lc_seg.fileoff; - lc_seg.vmsize += PAD(lc_seg.vmsize, 4096); - lc_seg.filesize = end - lc_seg.fileoff; - if (swap) { - lc_seg.vmsize = BSWAP64(lc_seg.vmsize); - lc_seg.filesize = BSWAP64(lc_seg.filesize); - } - fa->seek(link_edit_offset + 8); - fa->store_buffer((const uint8_t *)&lc_seg, sizeof(LoadCommandSegment64)); - } break; - default: { - ERR_FAIL_V_MSG(false, "MachO: Invalid __LINKEDIT segment type."); - } break; - } - fa->seek(get_signature_offset()); - for (uint64_t i = 0; i < new_size; i++) { - fa->store_8(0x00); - } - return true; -} - -#endif // MODULE_REGEX_ENABLED diff --git a/platform/osx/export/macho.h b/platform/osx/export/macho.h deleted file mode 100644 index 6cfc3c44f5..0000000000 --- a/platform/osx/export/macho.h +++ /dev/null @@ -1,215 +0,0 @@ -/*************************************************************************/ -/* macho.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* 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. */ -/*************************************************************************/ - -// Mach-O binary object file format parser and editor. - -#ifndef MACHO_H -#define MACHO_H - -#include "core/crypto/crypto.h" -#include "core/crypto/crypto_core.h" -#include "core/io/file_access.h" -#include "core/object/ref_counted.h" -#include "modules/modules_enabled.gen.h" // For regex. - -#ifdef MODULE_REGEX_ENABLED - -class MachO : public RefCounted { - struct MachHeader { - uint32_t cputype; - uint32_t cpusubtype; - uint32_t filetype; - uint32_t ncmds; - uint32_t sizeofcmds; - uint32_t flags; - }; - - enum LoadCommandID { - LC_SEGMENT = 0x00000001, - LC_SYMTAB = 0x00000002, - LC_SYMSEG = 0x00000003, - LC_THREAD = 0x00000004, - LC_UNIXTHREAD = 0x00000005, - LC_LOADFVMLIB = 0x00000006, - LC_IDFVMLIB = 0x00000007, - LC_IDENT = 0x00000008, - LC_FVMFILE = 0x00000009, - LC_PREPAGE = 0x0000000a, - LC_DYSYMTAB = 0x0000000b, - LC_LOAD_DYLIB = 0x0000000c, - LC_ID_DYLIB = 0x0000000d, - LC_LOAD_DYLINKER = 0x0000000e, - LC_ID_DYLINKER = 0x0000000f, - LC_PREBOUND_DYLIB = 0x00000010, - LC_ROUTINES = 0x00000011, - LC_SUB_FRAMEWORK = 0x00000012, - LC_SUB_UMBRELLA = 0x00000013, - LC_SUB_CLIENT = 0x00000014, - LC_SUB_LIBRARY = 0x00000015, - LC_TWOLEVEL_HINTS = 0x00000016, - LC_PREBIND_CKSUM = 0x00000017, - LC_LOAD_WEAK_DYLIB = 0x80000018, - LC_SEGMENT_64 = 0x00000019, - LC_ROUTINES_64 = 0x0000001a, - LC_UUID = 0x0000001b, - LC_RPATH = 0x8000001c, - LC_CODE_SIGNATURE = 0x0000001d, - LC_SEGMENT_SPLIT_INFO = 0x0000001e, - LC_REEXPORT_DYLIB = 0x8000001f, - LC_LAZY_LOAD_DYLIB = 0x00000020, - LC_ENCRYPTION_INFO = 0x00000021, - LC_DYLD_INFO = 0x00000022, - LC_DYLD_INFO_ONLY = 0x80000022, - LC_LOAD_UPWARD_DYLIB = 0x80000023, - LC_VERSION_MIN_MACOSX = 0x00000024, - LC_VERSION_MIN_IPHONEOS = 0x00000025, - LC_FUNCTION_STARTS = 0x00000026, - LC_DYLD_ENVIRONMENT = 0x00000027, - LC_MAIN = 0x80000028, - LC_DATA_IN_CODE = 0x00000029, - LC_SOURCE_VERSION = 0x0000002a, - LC_DYLIB_CODE_SIGN_DRS = 0x0000002b, - LC_ENCRYPTION_INFO_64 = 0x0000002c, - LC_LINKER_OPTION = 0x0000002d, - LC_LINKER_OPTIMIZATION_HINT = 0x0000002e, - LC_VERSION_MIN_TVOS = 0x0000002f, - LC_VERSION_MIN_WATCHOS = 0x00000030, - }; - - struct LoadCommandHeader { - uint32_t cmd; - uint32_t cmdsize; - }; - - struct LoadCommandSegment { - char segname[16]; - uint32_t vmaddr; - uint32_t vmsize; - uint32_t fileoff; - uint32_t filesize; - uint32_t maxprot; - uint32_t initprot; - uint32_t nsects; - uint32_t flags; - }; - - struct LoadCommandSegment64 { - char segname[16]; - uint64_t vmaddr; - uint64_t vmsize; - uint64_t fileoff; - uint64_t filesize; - uint32_t maxprot; - uint32_t initprot; - uint32_t nsects; - uint32_t flags; - }; - - struct Section { - char sectname[16]; - char segname[16]; - uint32_t addr; - uint32_t size; - uint32_t offset; - uint32_t align; - uint32_t reloff; - uint32_t nreloc; - uint32_t flags; - uint32_t reserved1; - uint32_t reserved2; - }; - - struct Section64 { - char sectname[16]; - char segname[16]; - uint64_t addr; - uint64_t size; - uint32_t offset; - uint32_t align; - uint32_t reloff; - uint32_t nreloc; - uint32_t flags; - uint32_t reserved1; - uint32_t reserved2; - uint32_t reserved3; - }; - - Ref<FileAccess> fa; - bool swap = false; - - uint64_t lc_limit = 0; - - uint64_t exe_limit = 0; - uint64_t exe_base = std::numeric_limits<uint64_t>::max(); // Start of first __text section. - uint32_t align = 0; - uint32_t cputype = 0; - uint32_t cpusubtype = 0; - - uint64_t link_edit_offset = 0; // __LINKEDIT segment offset. - uint64_t signature_offset = 0; // Load command offset. - - uint32_t seg_align(uint64_t p_vmaddr, uint32_t p_min, uint32_t p_max); - bool alloc_signature(uint64_t p_size); - - static inline size_t PAD(size_t s, size_t a) { - return (a - s % a); - } - -public: - static bool is_macho(const String &p_path); - - bool open_file(const String &p_path); - - uint64_t get_exe_base(); - uint64_t get_exe_limit(); - int32_t get_align(); - uint32_t get_cputype(); - uint32_t get_cpusubtype(); - uint64_t get_size(); - uint64_t get_code_limit(); - - uint64_t get_signature_offset(); - bool is_signed(); - - PackedByteArray get_cdhash_sha1(); - PackedByteArray get_cdhash_sha256(); - - PackedByteArray get_requirements(); - - const Ref<FileAccess> get_file() const; - Ref<FileAccess> get_file(); - - uint64_t get_signature_size(); - bool set_signature_size(uint64_t p_size); -}; - -#endif // MODULE_REGEX_ENABLED - -#endif // MACHO_H diff --git a/platform/osx/export/plist.cpp b/platform/osx/export/plist.cpp deleted file mode 100644 index 36de9dd34b..0000000000 --- a/platform/osx/export/plist.cpp +++ /dev/null @@ -1,570 +0,0 @@ -/*************************************************************************/ -/* plist.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "modules/modules_enabled.gen.h" // For regex. - -#include "plist.h" - -#ifdef MODULE_REGEX_ENABLED - -Ref<PListNode> PListNode::new_array() { - Ref<PListNode> node = memnew(PListNode()); - ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>()); - node->data_type = PList::PLNodeType::PL_NODE_TYPE_ARRAY; - return node; -} - -Ref<PListNode> PListNode::new_dict() { - Ref<PListNode> node = memnew(PListNode()); - ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>()); - node->data_type = PList::PLNodeType::PL_NODE_TYPE_DICT; - return node; -} - -Ref<PListNode> PListNode::new_string(const String &p_string) { - Ref<PListNode> node = memnew(PListNode()); - ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>()); - node->data_type = PList::PLNodeType::PL_NODE_TYPE_STRING; - node->data_string = p_string.utf8(); - return node; -} - -Ref<PListNode> PListNode::new_data(const String &p_string) { - Ref<PListNode> node = memnew(PListNode()); - ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>()); - node->data_type = PList::PLNodeType::PL_NODE_TYPE_DATA; - node->data_string = p_string.utf8(); - return node; -} - -Ref<PListNode> PListNode::new_date(const String &p_string) { - Ref<PListNode> node = memnew(PListNode()); - ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>()); - node->data_type = PList::PLNodeType::PL_NODE_TYPE_DATE; - node->data_string = p_string.utf8(); - return node; -} - -Ref<PListNode> PListNode::new_bool(bool p_bool) { - Ref<PListNode> node = memnew(PListNode()); - ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>()); - node->data_type = PList::PLNodeType::PL_NODE_TYPE_BOOLEAN; - node->data_bool = p_bool; - return node; -} - -Ref<PListNode> PListNode::new_int(int32_t p_int) { - Ref<PListNode> node = memnew(PListNode()); - ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>()); - node->data_type = PList::PLNodeType::PL_NODE_TYPE_INTEGER; - node->data_int = p_int; - return node; -} - -Ref<PListNode> PListNode::new_real(float p_real) { - Ref<PListNode> node = memnew(PListNode()); - ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>()); - node->data_type = PList::PLNodeType::PL_NODE_TYPE_REAL; - node->data_real = p_real; - return node; -} - -bool PListNode::push_subnode(const Ref<PListNode> &p_node, const String &p_key) { - ERR_FAIL_COND_V(p_node.is_null(), false); - if (data_type == PList::PLNodeType::PL_NODE_TYPE_DICT) { - ERR_FAIL_COND_V(p_key.is_empty(), false); - ERR_FAIL_COND_V(data_dict.has(p_key), false); - data_dict[p_key] = p_node; - return true; - } else if (data_type == PList::PLNodeType::PL_NODE_TYPE_ARRAY) { - data_array.push_back(p_node); - return true; - } else { - ERR_FAIL_V_MSG(false, "PList: Invalid parent node type, should be DICT or ARRAY."); - } -} - -size_t PListNode::get_asn1_size(uint8_t p_len_octets) const { - // Get size of all data, excluding type and size information. - switch (data_type) { - case PList::PLNodeType::PL_NODE_TYPE_NIL: { - return 0; - } break; - case PList::PLNodeType::PL_NODE_TYPE_DATA: - case PList::PLNodeType::PL_NODE_TYPE_DATE: { - ERR_FAIL_V_MSG(0, "PList: DATE and DATA nodes are not supported by ASN.1 serialization."); - } break; - case PList::PLNodeType::PL_NODE_TYPE_STRING: { - return data_string.length(); - } break; - case PList::PLNodeType::PL_NODE_TYPE_BOOLEAN: { - return 1; - } break; - case PList::PLNodeType::PL_NODE_TYPE_INTEGER: - case PList::PLNodeType::PL_NODE_TYPE_REAL: { - return 4; - } break; - case PList::PLNodeType::PL_NODE_TYPE_ARRAY: { - size_t size = 0; - for (int i = 0; i < data_array.size(); i++) { - size += 1 + _asn1_size_len(p_len_octets) + data_array[i]->get_asn1_size(p_len_octets); - } - return size; - } break; - case PList::PLNodeType::PL_NODE_TYPE_DICT: { - size_t size = 0; - - for (const KeyValue<String, Ref<PListNode>> &E : data_dict) { - size += 1 + _asn1_size_len(p_len_octets); // Sequence. - size += 1 + _asn1_size_len(p_len_octets) + E.key.utf8().length(); //Key. - size += 1 + _asn1_size_len(p_len_octets) + E.value->get_asn1_size(p_len_octets); // Value. - } - return size; - } break; - default: { - return 0; - } break; - } -} - -int PListNode::_asn1_size_len(uint8_t p_len_octets) { - if (p_len_octets > 1) { - return p_len_octets + 1; - } else { - return 1; - } -} - -void PListNode::store_asn1_size(PackedByteArray &p_stream, uint8_t p_len_octets) const { - uint32_t size = get_asn1_size(p_len_octets); - if (p_len_octets > 1) { - p_stream.push_back(0x80 + p_len_octets); - } - for (int i = p_len_octets - 1; i >= 0; i--) { - uint8_t x = (size >> i * 8) & 0xFF; - p_stream.push_back(x); - } -} - -bool PListNode::store_asn1(PackedByteArray &p_stream, uint8_t p_len_octets) const { - // Convert to binary ASN1 stream. - bool valid = true; - switch (data_type) { - case PList::PLNodeType::PL_NODE_TYPE_NIL: { - // Nothing to store. - } break; - case PList::PLNodeType::PL_NODE_TYPE_DATE: - case PList::PLNodeType::PL_NODE_TYPE_DATA: { - ERR_FAIL_V_MSG(false, "PList: DATE and DATA nodes are not supported by ASN.1 serialization."); - } break; - case PList::PLNodeType::PL_NODE_TYPE_STRING: { - p_stream.push_back(0x0C); - store_asn1_size(p_stream, p_len_octets); - for (int i = 0; i < data_string.size(); i++) { - p_stream.push_back(data_string[i]); - } - } break; - case PList::PLNodeType::PL_NODE_TYPE_BOOLEAN: { - p_stream.push_back(0x01); - store_asn1_size(p_stream, p_len_octets); - if (data_bool) { - p_stream.push_back(0x01); - } else { - p_stream.push_back(0x00); - } - } break; - case PList::PLNodeType::PL_NODE_TYPE_INTEGER: { - p_stream.push_back(0x02); - store_asn1_size(p_stream, p_len_octets); - for (int i = 4; i >= 0; i--) { - uint8_t x = (data_int >> i * 8) & 0xFF; - p_stream.push_back(x); - } - } break; - case PList::PLNodeType::PL_NODE_TYPE_REAL: { - p_stream.push_back(0x03); - store_asn1_size(p_stream, p_len_octets); - for (int i = 4; i >= 0; i--) { - uint8_t x = (data_int >> i * 8) & 0xFF; - p_stream.push_back(x); - } - } break; - case PList::PLNodeType::PL_NODE_TYPE_ARRAY: { - p_stream.push_back(0x30); // Sequence. - store_asn1_size(p_stream, p_len_octets); - for (int i = 0; i < data_array.size(); i++) { - valid = valid && data_array[i]->store_asn1(p_stream, p_len_octets); - } - } break; - case PList::PLNodeType::PL_NODE_TYPE_DICT: { - p_stream.push_back(0x31); // Set. - store_asn1_size(p_stream, p_len_octets); - for (const KeyValue<String, Ref<PListNode>> &E : data_dict) { - CharString cs = E.key.utf8(); - uint32_t size = cs.length(); - - // Sequence. - p_stream.push_back(0x30); - uint32_t seq_size = 2 * (1 + _asn1_size_len(p_len_octets)) + size + E.value->get_asn1_size(p_len_octets); - if (p_len_octets > 1) { - p_stream.push_back(0x80 + p_len_octets); - } - for (int i = p_len_octets - 1; i >= 0; i--) { - uint8_t x = (seq_size >> i * 8) & 0xFF; - p_stream.push_back(x); - } - // Key. - p_stream.push_back(0x0C); - if (p_len_octets > 1) { - p_stream.push_back(0x80 + p_len_octets); - } - for (int i = p_len_octets - 1; i >= 0; i--) { - uint8_t x = (size >> i * 8) & 0xFF; - p_stream.push_back(x); - } - for (uint32_t i = 0; i < size; i++) { - p_stream.push_back(cs[i]); - } - // Value. - valid = valid && E.value->store_asn1(p_stream, p_len_octets); - } - } break; - } - return valid; -} - -void PListNode::store_text(String &p_stream, uint8_t p_indent) const { - // Convert to text XML stream. - switch (data_type) { - case PList::PLNodeType::PL_NODE_TYPE_NIL: { - // Nothing to store. - } break; - case PList::PLNodeType::PL_NODE_TYPE_DATA: { - p_stream += String("\t").repeat(p_indent); - p_stream += "<data>\n"; - p_stream += String("\t").repeat(p_indent); - p_stream += data_string + "\n"; - p_stream += String("\t").repeat(p_indent); - p_stream += "</data>\n"; - } break; - case PList::PLNodeType::PL_NODE_TYPE_DATE: { - p_stream += String("\t").repeat(p_indent); - p_stream += "<date>"; - p_stream += data_string; - p_stream += "</date>\n"; - } break; - case PList::PLNodeType::PL_NODE_TYPE_STRING: { - p_stream += String("\t").repeat(p_indent); - p_stream += "<string>"; - p_stream += String::utf8(data_string); - p_stream += "</string>\n"; - } break; - case PList::PLNodeType::PL_NODE_TYPE_BOOLEAN: { - p_stream += String("\t").repeat(p_indent); - if (data_bool) { - p_stream += "<true/>\n"; - } else { - p_stream += "<false/>\n"; - } - } break; - case PList::PLNodeType::PL_NODE_TYPE_INTEGER: { - p_stream += String("\t").repeat(p_indent); - p_stream += "<integer>"; - p_stream += itos(data_int); - p_stream += "</integer>\n"; - } break; - case PList::PLNodeType::PL_NODE_TYPE_REAL: { - p_stream += String("\t").repeat(p_indent); - p_stream += "<real>"; - p_stream += rtos(data_real); - p_stream += "</real>\n"; - } break; - case PList::PLNodeType::PL_NODE_TYPE_ARRAY: { - p_stream += String("\t").repeat(p_indent); - p_stream += "<array>\n"; - for (int i = 0; i < data_array.size(); i++) { - data_array[i]->store_text(p_stream, p_indent + 1); - } - p_stream += String("\t").repeat(p_indent); - p_stream += "</array>\n"; - } break; - case PList::PLNodeType::PL_NODE_TYPE_DICT: { - p_stream += String("\t").repeat(p_indent); - p_stream += "<dict>\n"; - for (const KeyValue<String, Ref<PListNode>> &E : data_dict) { - p_stream += String("\t").repeat(p_indent + 1); - p_stream += "<key>"; - p_stream += E.key; - p_stream += "</key>\n"; - E.value->store_text(p_stream, p_indent + 1); - } - p_stream += String("\t").repeat(p_indent); - p_stream += "</dict>\n"; - } break; - } -} - -/*************************************************************************/ - -PList::PList() { - root = PListNode::new_dict(); -} - -PList::PList(const String &p_string) { - load_string(p_string); -} - -bool PList::load_file(const String &p_filename) { - root = Ref<PListNode>(); - - Ref<FileAccess> fb = FileAccess::open(p_filename, FileAccess::READ); - if (fb.is_null()) { - return false; - } - - unsigned char magic[8]; - fb->get_buffer(magic, 8); - - if (String((const char *)magic, 8) == "bplist00") { - ERR_FAIL_V_MSG(false, "PList: Binary property lists are not supported."); - } else { - // Load text plist. - Error err; - Vector<uint8_t> array = FileAccess::get_file_as_array(p_filename, &err); - ERR_FAIL_COND_V(err != OK, false); - - String ret; - ret.parse_utf8((const char *)array.ptr(), array.size()); - return load_string(ret); - } -} - -bool PList::load_string(const String &p_string) { - root = Ref<PListNode>(); - - int pos = 0; - bool in_plist = false; - bool done_plist = false; - List<Ref<PListNode>> stack; - String key; - while (pos >= 0) { - int open_token_s = p_string.find("<", pos); - if (open_token_s == -1) { - ERR_FAIL_V_MSG(false, "PList: Unexpected end of data. No tags found."); - } - int open_token_e = p_string.find(">", open_token_s); - pos = open_token_e; - - String token = p_string.substr(open_token_s + 1, open_token_e - open_token_s - 1); - if (token.is_empty()) { - ERR_FAIL_V_MSG(false, "PList: Invalid token name."); - } - String value; - if (token[0] == '?' || token[0] == '!') { // Skip <?xml ... ?> and <!DOCTYPE ... > - int end_token_e = p_string.find(">", open_token_s); - pos = end_token_e; - continue; - } - - if (token.find("plist", 0) == 0) { - in_plist = true; - continue; - } - - if (token == "/plist") { - done_plist = true; - break; - } - - if (!in_plist) { - ERR_FAIL_V_MSG(false, "PList: Node outside of <plist> tag."); - } - - if (token == "dict") { - if (!stack.is_empty()) { - // Add subnode end enter it. - Ref<PListNode> dict = PListNode::new_dict(); - dict->data_type = PList::PLNodeType::PL_NODE_TYPE_DICT; - if (!stack.back()->get()->push_subnode(dict, key)) { - ERR_FAIL_V_MSG(false, "PList: Can't push subnode, invalid parent type."); - } - stack.push_back(dict); - } else { - // Add root node. - if (!root.is_null()) { - ERR_FAIL_V_MSG(false, "PList: Root node already set."); - } - Ref<PListNode> dict = PListNode::new_dict(); - stack.push_back(dict); - root = dict; - } - continue; - } - - if (token == "/dict") { - // Exit current dict. - if (stack.is_empty() || stack.back()->get()->data_type != PList::PLNodeType::PL_NODE_TYPE_DICT) { - ERR_FAIL_V_MSG(false, "PList: Mismatched </dict> tag."); - } - stack.pop_back(); - continue; - } - - if (token == "array") { - if (!stack.is_empty()) { - // Add subnode end enter it. - Ref<PListNode> arr = PListNode::new_array(); - if (!stack.back()->get()->push_subnode(arr, key)) { - ERR_FAIL_V_MSG(false, "PList: Can't push subnode, invalid parent type."); - } - stack.push_back(arr); - } else { - // Add root node. - if (!root.is_null()) { - ERR_FAIL_V_MSG(false, "PList: Root node already set."); - } - Ref<PListNode> arr = PListNode::new_array(); - stack.push_back(arr); - root = arr; - } - continue; - } - - if (token == "/array") { - // Exit current array. - if (stack.is_empty() || stack.back()->get()->data_type != PList::PLNodeType::PL_NODE_TYPE_ARRAY) { - ERR_FAIL_V_MSG(false, "PList: Mismatched </array> tag."); - } - stack.pop_back(); - continue; - } - - if (token[token.length() - 1] == '/') { - token = token.substr(0, token.length() - 1); - } else { - int end_token_s = p_string.find("</", pos); - if (end_token_s == -1) { - ERR_FAIL_V_MSG(false, vformat("PList: Mismatched <%s> tag.", token)); - } - int end_token_e = p_string.find(">", end_token_s); - pos = end_token_e; - String end_token = p_string.substr(end_token_s + 2, end_token_e - end_token_s - 2); - if (end_token != token) { - ERR_FAIL_V_MSG(false, vformat("PList: Mismatched <%s> and <%s> token pair.", token, end_token)); - } - value = p_string.substr(open_token_e + 1, end_token_s - open_token_e - 1); - } - if (token == "key") { - key = value; - } else { - Ref<PListNode> var = nullptr; - if (token == "true") { - var = PListNode::new_bool(true); - } else if (token == "false") { - var = PListNode::new_bool(false); - } else if (token == "integer") { - var = PListNode::new_int(value.to_int()); - } else if (token == "real") { - var = PListNode::new_real(value.to_float()); - } else if (token == "string") { - var = PListNode::new_string(value); - } else if (token == "data") { - var = PListNode::new_data(value); - } else if (token == "date") { - var = PListNode::new_date(value); - } else { - ERR_FAIL_V_MSG(false, "PList: Invalid value type."); - } - if (stack.is_empty() || !stack.back()->get()->push_subnode(var, key)) { - ERR_FAIL_V_MSG(false, "PList: Can't push subnode, invalid parent type."); - } - } - } - if (!stack.is_empty() || !done_plist) { - ERR_FAIL_V_MSG(false, "PList: Unexpected end of data. Root node is not closed."); - } - return true; -} - -PackedByteArray PList::save_asn1() const { - if (root == nullptr) { - ERR_FAIL_V_MSG(PackedByteArray(), "PList: Invalid PList, no root node."); - } - size_t size = root->get_asn1_size(1); - uint8_t len_octets = 0; - if (size < 0x80) { - len_octets = 1; - } else { - size = root->get_asn1_size(2); - if (size < 0xFFFF) { - len_octets = 2; - } else { - size = root->get_asn1_size(3); - if (size < 0xFFFFFF) { - len_octets = 3; - } else { - size = root->get_asn1_size(4); - if (size < 0xFFFFFFFF) { - len_octets = 4; - } else { - ERR_FAIL_V_MSG(PackedByteArray(), "PList: Data is too big for ASN.1 serializer, should be < 4 GiB."); - } - } - } - } - - PackedByteArray ret; - if (!root->store_asn1(ret, len_octets)) { - ERR_FAIL_V_MSG(PackedByteArray(), "PList: ASN.1 serializer error."); - } - return ret; -} - -String PList::save_text() const { - if (root == nullptr) { - ERR_FAIL_V_MSG(String(), "PList: Invalid PList, no root node."); - } - - String ret; - ret += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; - ret += "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"; - ret += "<plist version=\"1.0\">\n"; - - root->store_text(ret, 0); - - ret += "</plist>\n\n"; - return ret; -} - -Ref<PListNode> PList::get_root() { - return root; -} - -#endif // MODULE_REGEX_ENABLED diff --git a/platform/osx/export/plist.h b/platform/osx/export/plist.h deleted file mode 100644 index ba9eaec196..0000000000 --- a/platform/osx/export/plist.h +++ /dev/null @@ -1,116 +0,0 @@ -/*************************************************************************/ -/* plist.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* 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. */ -/*************************************************************************/ - -// Property list file format (application/x-plist) parser, property list ASN-1 serialization. - -#ifndef PLIST_H -#define PLIST_H - -#include "core/crypto/crypto_core.h" -#include "core/io/file_access.h" -#include "modules/modules_enabled.gen.h" // For regex. - -#ifdef MODULE_REGEX_ENABLED - -class PListNode; - -class PList : public RefCounted { - friend class PListNode; - -public: - enum PLNodeType { - PL_NODE_TYPE_NIL, - PL_NODE_TYPE_STRING, - PL_NODE_TYPE_ARRAY, - PL_NODE_TYPE_DICT, - PL_NODE_TYPE_BOOLEAN, - PL_NODE_TYPE_INTEGER, - PL_NODE_TYPE_REAL, - PL_NODE_TYPE_DATA, - PL_NODE_TYPE_DATE, - }; - -private: - Ref<PListNode> root; - -public: - PList(); - PList(const String &p_string); - - bool load_file(const String &p_filename); - bool load_string(const String &p_string); - - PackedByteArray save_asn1() const; - String save_text() const; - - Ref<PListNode> get_root(); -}; - -/*************************************************************************/ - -class PListNode : public RefCounted { - static int _asn1_size_len(uint8_t p_len_octets); - -public: - PList::PLNodeType data_type = PList::PLNodeType::PL_NODE_TYPE_NIL; - - CharString data_string; - Vector<Ref<PListNode>> data_array; - HashMap<String, Ref<PListNode>> data_dict; - union { - int32_t data_int; - bool data_bool; - float data_real; - }; - - static Ref<PListNode> new_array(); - static Ref<PListNode> new_dict(); - static Ref<PListNode> new_string(const String &p_string); - static Ref<PListNode> new_data(const String &p_string); - static Ref<PListNode> new_date(const String &p_string); - static Ref<PListNode> new_bool(bool p_bool); - static Ref<PListNode> new_int(int32_t p_int); - static Ref<PListNode> new_real(float p_real); - - bool push_subnode(const Ref<PListNode> &p_node, const String &p_key = ""); - - size_t get_asn1_size(uint8_t p_len_octets) const; - - void store_asn1_size(PackedByteArray &p_stream, uint8_t p_len_octets) const; - bool store_asn1(PackedByteArray &p_stream, uint8_t p_len_octets) const; - void store_text(String &p_stream, uint8_t p_indent) const; - - PListNode() {} - ~PListNode() {} -}; - -#endif // MODULE_REGEX_ENABLED - -#endif // PLIST_H diff --git a/platform/osx/gl_manager_osx_legacy.h b/platform/osx/gl_manager_osx_legacy.h deleted file mode 100644 index 2d4913a7a6..0000000000 --- a/platform/osx/gl_manager_osx_legacy.h +++ /dev/null @@ -1,97 +0,0 @@ -/*************************************************************************/ -/* gl_manager_osx_legacy.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* 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. */ -/*************************************************************************/ - -#ifndef GL_MANAGER_OSX_LEGACY_H -#define GL_MANAGER_OSX_LEGACY_H - -#if defined(OSX_ENABLED) && defined(GLES3_ENABLED) - -#include "core/error/error_list.h" -#include "core/os/os.h" -#include "core/templates/local_vector.h" -#include "servers/display_server.h" - -#import <AppKit/AppKit.h> -#import <ApplicationServices/ApplicationServices.h> -#import <CoreVideo/CoreVideo.h> - -class GLManager_OSX { -public: - enum ContextType { - GLES_3_0_COMPATIBLE, - }; - -private: - struct GLWindow { - int width = 0; - int height = 0; - - id window_view = nullptr; - NSOpenGLContext *context = nullptr; - }; - - RBMap<DisplayServer::WindowID, GLWindow> windows; - - NSOpenGLContext *shared_context = nullptr; - DisplayServer::WindowID current_window = DisplayServer::INVALID_WINDOW_ID; - - Error create_context(GLWindow &win); - - bool use_vsync = false; - ContextType context_type; - -public: - Error window_create(DisplayServer::WindowID p_window_id, id p_view, int p_width, int p_height); - void window_destroy(DisplayServer::WindowID p_window_id); - void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height); - - int window_get_width(DisplayServer::WindowID p_window_id = 0); - int window_get_height(DisplayServer::WindowID p_window_id = 0); - - void release_current(); - void make_current(); - void swap_buffers(); - - void window_make_current(DisplayServer::WindowID p_window_id); - - void window_update(DisplayServer::WindowID p_window_id); - void window_set_per_pixel_transparency_enabled(DisplayServer::WindowID p_window_id, bool p_enabled); - - Error initialize(); - - void set_use_vsync(bool p_use); - bool is_using_vsync() const; - - GLManager_OSX(ContextType p_context_type); - ~GLManager_OSX(); -}; - -#endif // OSX_ENABLED && GLES3_ENABLED -#endif // GL_MANAGER_OSX_LEGACY_H diff --git a/platform/osx/gl_manager_osx_legacy.mm b/platform/osx/gl_manager_osx_legacy.mm deleted file mode 100644 index c769d7f5c5..0000000000 --- a/platform/osx/gl_manager_osx_legacy.mm +++ /dev/null @@ -1,228 +0,0 @@ -/*************************************************************************/ -/* gl_manager_osx_legacy.mm */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "gl_manager_osx_legacy.h" - -#ifdef OSX_ENABLED -#ifdef GLES3_ENABLED - -#include <stdio.h> -#include <stdlib.h> - -Error GLManager_OSX::create_context(GLWindow &win) { - NSOpenGLPixelFormatAttribute attributes[] = { - NSOpenGLPFADoubleBuffer, - NSOpenGLPFAClosestPolicy, - NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core, - NSOpenGLPFAColorSize, 32, - NSOpenGLPFADepthSize, 24, - NSOpenGLPFAStencilSize, 8, - 0 - }; - - NSOpenGLPixelFormat *pixel_format = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]; - ERR_FAIL_COND_V(pixel_format == nil, ERR_CANT_CREATE); - - win.context = [[NSOpenGLContext alloc] initWithFormat:pixel_format shareContext:shared_context]; - ERR_FAIL_COND_V(win.context == nil, ERR_CANT_CREATE); - if (shared_context == nullptr) { - shared_context = win.context; - } - - [win.context setView:win.window_view]; - [win.context makeCurrentContext]; - - return OK; -} - -Error GLManager_OSX::window_create(DisplayServer::WindowID p_window_id, id p_view, int p_width, int p_height) { - GLWindow win; - win.width = p_width; - win.height = p_height; - win.window_view = p_view; - - if (create_context(win) != OK) { - return FAILED; - } - - windows[p_window_id] = win; - window_make_current(p_window_id); - - return OK; -} - -void GLManager_OSX::window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height) { - if (!windows.has(p_window_id)) { - return; - } - - GLWindow &win = windows[p_window_id]; - - win.width = p_width; - win.height = p_height; - - GLint dim[2]; - dim[0] = p_width; - dim[1] = p_height; - CGLSetParameter((CGLContextObj)[win.context CGLContextObj], kCGLCPSurfaceBackingSize, &dim[0]); - CGLEnable((CGLContextObj)[win.context CGLContextObj], kCGLCESurfaceBackingSize); - if (OS::get_singleton()->is_hidpi_allowed()) { - [win.window_view setWantsBestResolutionOpenGLSurface:YES]; - } else { - [win.window_view setWantsBestResolutionOpenGLSurface:NO]; - } - - [win.context update]; -} - -int GLManager_OSX::window_get_width(DisplayServer::WindowID p_window_id) { - if (!windows.has(p_window_id)) { - return 0; - } - - GLWindow &win = windows[p_window_id]; - return win.width; -} - -int GLManager_OSX::window_get_height(DisplayServer::WindowID p_window_id) { - if (!windows.has(p_window_id)) { - return 0; - } - - GLWindow &win = windows[p_window_id]; - return win.height; -} - -void GLManager_OSX::window_destroy(DisplayServer::WindowID p_window_id) { - if (!windows.has(p_window_id)) { - return; - } - - if (current_window == p_window_id) { - current_window = DisplayServer::INVALID_WINDOW_ID; - } - - windows.erase(p_window_id); -} - -void GLManager_OSX::release_current() { - if (current_window == DisplayServer::INVALID_WINDOW_ID) { - return; - } - - [NSOpenGLContext clearCurrentContext]; -} - -void GLManager_OSX::window_make_current(DisplayServer::WindowID p_window_id) { - if (current_window == p_window_id) { - return; - } - if (!windows.has(p_window_id)) { - return; - } - - GLWindow &win = windows[p_window_id]; - [win.context makeCurrentContext]; - - current_window = p_window_id; -} - -void GLManager_OSX::make_current() { - if (current_window == DisplayServer::INVALID_WINDOW_ID) { - return; - } - if (!windows.has(current_window)) { - return; - } - - GLWindow &win = windows[current_window]; - [win.context makeCurrentContext]; -} - -void GLManager_OSX::swap_buffers() { - for (const KeyValue<DisplayServer::WindowID, GLWindow> &E : windows) { - [E.value.context flushBuffer]; - } -} - -void GLManager_OSX::window_update(DisplayServer::WindowID p_window_id) { - if (!windows.has(p_window_id)) { - return; - } - - GLWindow &win = windows[p_window_id]; - [win.context update]; -} - -void GLManager_OSX::window_set_per_pixel_transparency_enabled(DisplayServer::WindowID p_window_id, bool p_enabled) { - if (!windows.has(p_window_id)) { - return; - } - - GLWindow &win = windows[p_window_id]; - if (p_enabled) { - GLint opacity = 0; - [win.context setValues:&opacity forParameter:NSOpenGLContextParameterSurfaceOpacity]; - } else { - GLint opacity = 1; - [win.context setValues:&opacity forParameter:NSOpenGLContextParameterSurfaceOpacity]; - } - [win.context update]; -} - -Error GLManager_OSX::initialize() { - return OK; -} - -void GLManager_OSX::set_use_vsync(bool p_use) { - use_vsync = p_use; - - CGLContextObj ctx = CGLGetCurrentContext(); - if (ctx) { - GLint swapInterval = p_use ? 1 : 0; - CGLSetParameter(ctx, kCGLCPSwapInterval, &swapInterval); - use_vsync = p_use; - } -} - -bool GLManager_OSX::is_using_vsync() const { - return use_vsync; -} - -GLManager_OSX::GLManager_OSX(ContextType p_context_type) { - context_type = p_context_type; -} - -GLManager_OSX::~GLManager_OSX() { - release_current(); -} - -#endif // GLES3_ENABLED -#endif // OSX diff --git a/platform/osx/godot_application.h b/platform/osx/godot_application.h deleted file mode 100644 index 8d48a659f3..0000000000 --- a/platform/osx/godot_application.h +++ /dev/null @@ -1,42 +0,0 @@ -/*************************************************************************/ -/* godot_application.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* 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. */ -/*************************************************************************/ - -#ifndef GODOT_APPLICATION_H -#define GODOT_APPLICATION_H - -#include "core/os/os.h" - -#import <AppKit/AppKit.h> -#import <Foundation/Foundation.h> - -@interface GodotApplication : NSApplication -@end - -#endif // GODOT_APPLICATION_H diff --git a/platform/osx/godot_application.mm b/platform/osx/godot_application.mm deleted file mode 100644 index 13313a025a..0000000000 --- a/platform/osx/godot_application.mm +++ /dev/null @@ -1,58 +0,0 @@ -/*************************************************************************/ -/* godot_application.mm */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "godot_application.h" - -#include "display_server_osx.h" - -@implementation GodotApplication - -- (void)sendEvent:(NSEvent *)event { - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (ds) { - if ([event type] == NSEventTypeLeftMouseDown || [event type] == NSEventTypeRightMouseDown || [event type] == NSEventTypeOtherMouseDown) { - if (ds->mouse_process_popups()) { - return; - } - } - ds->send_event(event); - } - - // From http://cocoadev.com/index.pl?GameKeyboardHandlingAlmost - // This works around an AppKit bug, where key up events while holding - // down the command key don't get sent to the key window. - if ([event type] == NSEventTypeKeyUp && ([event modifierFlags] & NSEventModifierFlagCommand)) { - [[self keyWindow] sendEvent:event]; - } else { - [super sendEvent:event]; - } -} - -@end diff --git a/platform/osx/godot_application_delegate.h b/platform/osx/godot_application_delegate.h deleted file mode 100644 index f5b67b580f..0000000000 --- a/platform/osx/godot_application_delegate.h +++ /dev/null @@ -1,46 +0,0 @@ -/*************************************************************************/ -/* godot_application_delegate.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* 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. */ -/*************************************************************************/ - -#ifndef GODOT_APPLICATION_DELEGATE_H -#define GODOT_APPLICATION_DELEGATE_H - -#include "core/os/os.h" - -#import <AppKit/AppKit.h> -#import <Foundation/Foundation.h> - -@interface GodotApplicationDelegate : NSObject -- (void)forceUnbundledWindowActivationHackStep1; -- (void)forceUnbundledWindowActivationHackStep2; -- (void)forceUnbundledWindowActivationHackStep3; -- (void)handleAppleEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent; -@end - -#endif // GODOT_APPLICATION_DELEGATE_H diff --git a/platform/osx/godot_application_delegate.mm b/platform/osx/godot_application_delegate.mm deleted file mode 100644 index 4d3558b273..0000000000 --- a/platform/osx/godot_application_delegate.mm +++ /dev/null @@ -1,163 +0,0 @@ -/*************************************************************************/ -/* godot_application_delegate.mm */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "godot_application_delegate.h" - -#include "display_server_osx.h" -#include "os_osx.h" - -@implementation GodotApplicationDelegate - -- (void)forceUnbundledWindowActivationHackStep1 { - // Step 1: Switch focus to macOS SystemUIServer process. - // Required to perform step 2, TransformProcessType will fail if app is already the in focus. - for (NSRunningApplication *app in [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.systemuiserver"]) { - [app activateWithOptions:NSApplicationActivateIgnoringOtherApps]; - break; - } - [self performSelector:@selector(forceUnbundledWindowActivationHackStep2) - withObject:nil - afterDelay:0.02]; -} - -- (void)forceUnbundledWindowActivationHackStep2 { - // Step 2: Register app as foreground process. - ProcessSerialNumber psn = { 0, kCurrentProcess }; - (void)TransformProcessType(&psn, kProcessTransformToForegroundApplication); - [self performSelector:@selector(forceUnbundledWindowActivationHackStep3) withObject:nil afterDelay:0.02]; -} - -- (void)forceUnbundledWindowActivationHackStep3 { - // Step 3: Switch focus back to app window. - [[NSRunningApplication currentApplication] activateWithOptions:NSApplicationActivateIgnoringOtherApps]; -} - -- (void)applicationDidFinishLaunching:(NSNotification *)notice { - NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"]; - if (nsappname == nil || isatty(STDOUT_FILENO) || isatty(STDIN_FILENO) || isatty(STDERR_FILENO)) { - // If the executable is started from terminal or is not bundled, macOS WindowServer won't register and activate app window correctly (menu and title bar are grayed out and input ignored). - [self performSelector:@selector(forceUnbundledWindowActivationHackStep1) withObject:nil afterDelay:0.02]; - } -} - -- (id)init { - self = [super init]; - - NSAppleEventManager *aem = [NSAppleEventManager sharedAppleEventManager]; - [aem setEventHandler:self andSelector:@selector(handleAppleEvent:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL]; - [aem setEventHandler:self andSelector:@selector(handleAppleEvent:withReplyEvent:) forEventClass:kCoreEventClass andEventID:kAEOpenDocuments]; - - return self; -} - -- (void)handleAppleEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent { - OS_OSX *os = (OS_OSX *)OS::get_singleton(); - if (!event || !os) { - return; - } - - List<String> args; - if (([event eventClass] == kInternetEventClass) && ([event eventID] == kAEGetURL)) { - // Opening URL scheme. - NSString *url = [[event paramDescriptorForKeyword:keyDirectObject] stringValue]; - args.push_back(vformat("--uri=\"%s\"", String::utf8([url UTF8String]))); - } - - if (([event eventClass] == kCoreEventClass) && ([event eventID] == kAEOpenDocuments)) { - // Opening file association. - NSAppleEventDescriptor *files = [event paramDescriptorForKeyword:keyDirectObject]; - if (files) { - NSInteger count = [files numberOfItems]; - for (NSInteger i = 1; i <= count; i++) { - NSURL *url = [NSURL URLWithString:[[files descriptorAtIndex:i] stringValue]]; - args.push_back(String::utf8([url.path UTF8String])); - } - } - } - - if (!args.is_empty()) { - if (os->get_main_loop()) { - // Application is already running, open a new instance with the URL/files as command line arguments. - os->create_instance(args); - } else { - // Application is just started, add to the list of command line arguments and continue. - os->set_cmdline_platform_args(args); - } - } -} - -- (void)applicationDidResignActive:(NSNotification *)notification { - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (ds) { - ds->mouse_process_popups(true); - } - if (OS::get_singleton()->get_main_loop()) { - OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT); - } -} - -- (void)applicationDidBecomeActive:(NSNotification *)notification { - if (OS::get_singleton()->get_main_loop()) { - OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN); - } -} - -- (void)globalMenuCallback:(id)sender { - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (ds) { - return ds->menu_callback(sender); - } -} - -- (NSMenu *)applicationDockMenu:(NSApplication *)sender { - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (ds) { - return ds->get_dock_menu(); - } else { - return nullptr; - } -} - -- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender { - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (ds) { - ds->send_window_event(ds->get_window(DisplayServerOSX::MAIN_WINDOW_ID), DisplayServerOSX::WINDOW_EVENT_CLOSE_REQUEST); - } - return NSTerminateCancel; -} - -- (void)showAbout:(id)sender { - OS_OSX *os = (OS_OSX *)OS::get_singleton(); - if (os && os->get_main_loop()) { - os->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_ABOUT); - } -} - -@end diff --git a/platform/osx/godot_content_view.h b/platform/osx/godot_content_view.h deleted file mode 100644 index 353305aec1..0000000000 --- a/platform/osx/godot_content_view.h +++ /dev/null @@ -1,66 +0,0 @@ -/*************************************************************************/ -/* godot_content_view.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* 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. */ -/*************************************************************************/ - -#ifndef GODOT_CONTENT_VIEW_H -#define GODOT_CONTENT_VIEW_H - -#include "servers/display_server.h" - -#import <AppKit/AppKit.h> -#import <Foundation/Foundation.h> - -#if defined(GLES3_ENABLED) -#import <AppKit/NSOpenGLView.h> -#define RootView NSOpenGLView -#else -#define RootView NSView -#endif - -#import <QuartzCore/CAMetalLayer.h> - -@interface GodotContentView : RootView <NSTextInputClient> { - DisplayServer::WindowID window_id; - NSTrackingArea *tracking_area; - NSMutableAttributedString *marked_text; - bool ime_input_event_in_progress; - bool mouse_down_control; - bool ignore_momentum_scroll; - bool last_pen_inverted; -} - -- (void)processScrollEvent:(NSEvent *)event button:(MouseButton)button factor:(double)factor; -- (void)processPanEvent:(NSEvent *)event dx:(double)dx dy:(double)dy; -- (void)processMouseEvent:(NSEvent *)event index:(MouseButton)index mask:(MouseButton)mask pressed:(bool)pressed; -- (void)setWindowID:(DisplayServer::WindowID)wid; -- (void)cancelComposition; - -@end - -#endif // GODOT_CONTENT_VIEW_H diff --git a/platform/osx/godot_content_view.mm b/platform/osx/godot_content_view.mm deleted file mode 100644 index 018b90e629..0000000000 --- a/platform/osx/godot_content_view.mm +++ /dev/null @@ -1,771 +0,0 @@ -/*************************************************************************/ -/* godot_content_view.mm */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "godot_content_view.h" - -#include "display_server_osx.h" -#include "key_mapping_osx.h" - -@implementation GodotContentView - -- (id)init { - self = [super init]; - window_id = DisplayServer::INVALID_WINDOW_ID; - tracking_area = nil; - ime_input_event_in_progress = false; - mouse_down_control = false; - ignore_momentum_scroll = false; - last_pen_inverted = false; - [self updateTrackingAreas]; - - if (@available(macOS 10.13, *)) { - [self registerForDraggedTypes:[NSArray arrayWithObject:NSPasteboardTypeFileURL]]; -#if !defined(__aarch64__) // Do not build deprectead 10.13 code on ARM. - } else { - [self registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]]; -#endif - } - marked_text = [[NSMutableAttributedString alloc] init]; - return self; -} - -- (void)setWindowID:(DisplayServerOSX::WindowID)wid { - window_id = wid; -} - -// MARK: Backing Layer - -- (CALayer *)makeBackingLayer { - return [[CAMetalLayer class] layer]; -} - -- (void)updateLayer { - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - ds->window_update(window_id); - [super updateLayer]; -} - -- (BOOL)wantsUpdateLayer { - return YES; -} - -- (BOOL)isOpaque { - return YES; -} - -// MARK: IME - -- (BOOL)hasMarkedText { - return (marked_text.length > 0); -} - -- (NSRange)markedRange { - return NSMakeRange(0, marked_text.length); -} - -- (NSRange)selectedRange { - static const NSRange kEmptyRange = { NSNotFound, 0 }; - return kEmptyRange; -} - -- (void)setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange { - if ([aString isKindOfClass:[NSAttributedString class]]) { - marked_text = [[NSMutableAttributedString alloc] initWithAttributedString:aString]; - } else { - marked_text = [[NSMutableAttributedString alloc] initWithString:aString]; - } - if (marked_text.length == 0) { - [self unmarkText]; - return; - } - - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { - return; - } - - DisplayServerOSX::WindowData &wd = ds->get_window(window_id); - if (wd.im_active) { - ime_input_event_in_progress = true; - ds->update_im_text(Point2i(selectedRange.location, selectedRange.length), String::utf8([[marked_text mutableString] UTF8String])); - } -} - -- (void)doCommandBySelector:(SEL)aSelector { - [self tryToPerform:aSelector with:self]; -} - -- (void)unmarkText { - ime_input_event_in_progress = false; - [[marked_text mutableString] setString:@""]; - - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { - return; - } - - DisplayServerOSX::WindowData &wd = ds->get_window(window_id); - if (wd.im_active) { - ds->update_im_text(Point2i(), String()); - } -} - -- (NSArray *)validAttributesForMarkedText { - return [NSArray array]; -} - -- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange { - return nil; -} - -- (NSUInteger)characterIndexForPoint:(NSPoint)aPoint { - return 0; -} - -- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange { - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { - return NSMakeRect(0, 0, 0, 0); - } - - DisplayServerOSX::WindowData &wd = ds->get_window(window_id); - const NSRect content_rect = [wd.window_view frame]; - const float scale = ds->screen_get_max_scale(); - NSRect point_in_window_rect = NSMakeRect(wd.im_position.x / scale, content_rect.size.height - (wd.im_position.y / scale) - 1, 0, 0); - NSPoint point_on_screen = [wd.window_object convertRectToScreen:point_in_window_rect].origin; - - return NSMakeRect(point_on_screen.x, point_on_screen.y, 0, 0); -} - -- (void)cancelComposition { - [self unmarkText]; - [[NSTextInputContext currentInputContext] discardMarkedText]; -} - -- (void)insertText:(id)aString { - [self insertText:aString replacementRange:NSMakeRange(0, 0)]; -} - -- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange { - NSEvent *event = [NSApp currentEvent]; - - NSString *characters; - if ([aString isKindOfClass:[NSAttributedString class]]) { - characters = [aString string]; - } else { - characters = (NSString *)aString; - } - - NSCharacterSet *ctrl_chars = [NSCharacterSet controlCharacterSet]; - NSCharacterSet *wsnl_chars = [NSCharacterSet whitespaceAndNewlineCharacterSet]; - if ([characters rangeOfCharacterFromSet:ctrl_chars].length && [characters rangeOfCharacterFromSet:wsnl_chars].length == 0) { - [[NSTextInputContext currentInputContext] discardMarkedText]; - [self cancelComposition]; - return; - } - - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { - [self cancelComposition]; - return; - } - - Char16String text; - text.resize([characters length] + 1); - [characters getCharacters:(unichar *)text.ptrw() range:NSMakeRange(0, [characters length])]; - - String u32text; - u32text.parse_utf16(text.ptr(), text.length()); - - for (int i = 0; i < u32text.length(); i++) { - const char32_t codepoint = u32text[i]; - if ((codepoint & 0xFF00) == 0xF700) { - continue; - } - - DisplayServerOSX::KeyEvent ke; - - ke.window_id = window_id; - ke.osx_state = [event modifierFlags]; - ke.pressed = true; - ke.echo = false; - ke.raw = false; // IME input event. - ke.keycode = Key::NONE; - ke.physical_keycode = Key::NONE; - ke.unicode = codepoint; - - ds->push_to_key_event_buffer(ke); - } - [self cancelComposition]; -} - -// MARK: Drag and drop - -- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender { - return NSDragOperationCopy; -} - -- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender { - return NSDragOperationCopy; -} - -- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender { - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { - return NO; - } - - DisplayServerOSX::WindowData &wd = ds->get_window(window_id); - if (!wd.drop_files_callback.is_null()) { - Vector<String> files; - NSPasteboard *pboard = [sender draggingPasteboard]; - - if (@available(macOS 10.13, *)) { - NSArray *items = pboard.pasteboardItems; - for (NSPasteboardItem *item in items) { - NSString *url = [item stringForType:NSPasteboardTypeFileURL]; - NSString *file = [NSURL URLWithString:url].path; - files.push_back(String::utf8([file UTF8String])); - } -#if !defined(__aarch64__) // Do not build deprectead 10.13 code on ARM. - } else { - NSArray *filenames = [pboard propertyListForType:NSFilenamesPboardType]; - for (NSString *file in filenames) { - files.push_back(String::utf8([file UTF8String])); - } -#endif - } - - Variant v = files; - Variant *vp = &v; - Variant ret; - Callable::CallError ce; - wd.drop_files_callback.call((const Variant **)&vp, 1, ret, ce); - } - - return NO; -} - -// MARK: Focus - -- (BOOL)canBecomeKeyView { - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { - return YES; - } - - DisplayServerOSX::WindowData &wd = ds->get_window(window_id); - return !wd.no_focus && !wd.is_popup; -} - -- (BOOL)acceptsFirstResponder { - return YES; -} - -// MARK: Mouse - -- (void)cursorUpdate:(NSEvent *)event { - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (!ds) { - return; - } - - ds->cursor_update_shape(); -} - -- (void)processMouseEvent:(NSEvent *)event index:(MouseButton)index mask:(MouseButton)mask pressed:(bool)pressed { - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { - return; - } - - DisplayServerOSX::WindowData &wd = ds->get_window(window_id); - MouseButton last_button_state = ds->mouse_get_button_state(); - - if (pressed) { - last_button_state |= mask; - } else { - last_button_state &= (MouseButton)~mask; - } - ds->mouse_set_button_state(last_button_state); - - Ref<InputEventMouseButton> mb; - mb.instantiate(); - mb->set_window_id(window_id); - ds->update_mouse_pos(wd, [event locationInWindow]); - ds->get_key_modifier_state([event modifierFlags], mb); - mb->set_button_index(index); - mb->set_pressed(pressed); - mb->set_position(wd.mouse_pos); - mb->set_global_position(wd.mouse_pos); - mb->set_button_mask(last_button_state); - if (index == MouseButton::LEFT && pressed) { - mb->set_double_click([event clickCount] == 2); - } - - Input::get_singleton()->parse_input_event(mb); -} - -- (void)mouseDown:(NSEvent *)event { - if (([event modifierFlags] & NSEventModifierFlagControl)) { - mouse_down_control = true; - [self processMouseEvent:event index:MouseButton::RIGHT mask:MouseButton::MASK_RIGHT pressed:true]; - } else { - mouse_down_control = false; - [self processMouseEvent:event index:MouseButton::LEFT mask:MouseButton::MASK_LEFT pressed:true]; - } -} - -- (void)mouseDragged:(NSEvent *)event { - [self mouseMoved:event]; -} - -- (void)mouseUp:(NSEvent *)event { - if (mouse_down_control) { - [self processMouseEvent:event index:MouseButton::RIGHT mask:MouseButton::MASK_RIGHT pressed:false]; - } else { - [self processMouseEvent:event index:MouseButton::LEFT mask:MouseButton::MASK_LEFT pressed:false]; - } -} - -- (void)mouseMoved:(NSEvent *)event { - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { - return; - } - - DisplayServerOSX::WindowData &wd = ds->get_window(window_id); - - NSPoint delta = NSMakePoint([event deltaX], [event deltaY]); - NSPoint mpos = [event locationInWindow]; - - if (ds->update_mouse_wrap(wd, delta, mpos, [event timestamp])) { - return; - } - - Ref<InputEventMouseMotion> mm; - mm.instantiate(); - - mm->set_window_id(window_id); - mm->set_button_mask(ds->mouse_get_button_state()); - ds->update_mouse_pos(wd, mpos); - mm->set_position(wd.mouse_pos); - mm->set_pressure([event pressure]); - NSEventSubtype subtype = [event subtype]; - if (subtype == NSEventSubtypeTabletPoint) { - const NSPoint p = [event tilt]; - mm->set_tilt(Vector2(p.x, p.y)); - mm->set_pen_inverted(last_pen_inverted); - } else if (subtype == NSEventSubtypeTabletProximity) { - // Check if using the eraser end of pen only on proximity event. - last_pen_inverted = [event pointingDeviceType] == NSPointingDeviceTypeEraser; - mm->set_pen_inverted(last_pen_inverted); - } - mm->set_global_position(wd.mouse_pos); - mm->set_velocity(Input::get_singleton()->get_last_mouse_velocity()); - const Vector2i relativeMotion = Vector2i(delta.x, delta.y) * ds->screen_get_max_scale(); - mm->set_relative(relativeMotion); - ds->get_key_modifier_state([event modifierFlags], mm); - - Input::get_singleton()->parse_input_event(mm); -} - -- (void)rightMouseDown:(NSEvent *)event { - [self processMouseEvent:event index:MouseButton::RIGHT mask:MouseButton::MASK_RIGHT pressed:true]; -} - -- (void)rightMouseDragged:(NSEvent *)event { - [self mouseMoved:event]; -} - -- (void)rightMouseUp:(NSEvent *)event { - [self processMouseEvent:event index:MouseButton::RIGHT mask:MouseButton::MASK_RIGHT pressed:false]; -} - -- (void)otherMouseDown:(NSEvent *)event { - if ((int)[event buttonNumber] == 2) { - [self processMouseEvent:event index:MouseButton::MIDDLE mask:MouseButton::MASK_MIDDLE pressed:true]; - } else if ((int)[event buttonNumber] == 3) { - [self processMouseEvent:event index:MouseButton::MB_XBUTTON1 mask:MouseButton::MASK_XBUTTON1 pressed:true]; - } else if ((int)[event buttonNumber] == 4) { - [self processMouseEvent:event index:MouseButton::MB_XBUTTON2 mask:MouseButton::MASK_XBUTTON2 pressed:true]; - } else { - return; - } -} - -- (void)otherMouseDragged:(NSEvent *)event { - [self mouseMoved:event]; -} - -- (void)otherMouseUp:(NSEvent *)event { - if ((int)[event buttonNumber] == 2) { - [self processMouseEvent:event index:MouseButton::MIDDLE mask:MouseButton::MASK_MIDDLE pressed:false]; - } else if ((int)[event buttonNumber] == 3) { - [self processMouseEvent:event index:MouseButton::MB_XBUTTON1 mask:MouseButton::MASK_XBUTTON1 pressed:false]; - } else if ((int)[event buttonNumber] == 4) { - [self processMouseEvent:event index:MouseButton::MB_XBUTTON2 mask:MouseButton::MASK_XBUTTON2 pressed:false]; - } else { - return; - } -} - -- (void)mouseExited:(NSEvent *)event { - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { - return; - } - - DisplayServerOSX::WindowData &wd = ds->get_window(window_id); - if (ds->mouse_get_mode() != DisplayServer::MOUSE_MODE_CAPTURED) { - ds->send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_MOUSE_EXIT); - } -} - -- (void)mouseEntered:(NSEvent *)event { - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { - return; - } - - DisplayServerOSX::WindowData &wd = ds->get_window(window_id); - if (ds->mouse_get_mode() != DisplayServer::MOUSE_MODE_CAPTURED) { - ds->send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_MOUSE_ENTER); - } - - ds->cursor_update_shape(); -} - -- (void)magnifyWithEvent:(NSEvent *)event { - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { - return; - } - - DisplayServerOSX::WindowData &wd = ds->get_window(window_id); - - Ref<InputEventMagnifyGesture> ev; - ev.instantiate(); - ev->set_window_id(window_id); - ds->get_key_modifier_state([event modifierFlags], ev); - ds->update_mouse_pos(wd, [event locationInWindow]); - ev->set_position(wd.mouse_pos); - ev->set_factor([event magnification] + 1.0); - - Input::get_singleton()->parse_input_event(ev); -} - -- (void)updateTrackingAreas { - if (tracking_area != nil) { - [self removeTrackingArea:tracking_area]; - } - - NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow | NSTrackingCursorUpdate | NSTrackingInVisibleRect; - tracking_area = [[NSTrackingArea alloc] initWithRect:[self bounds] options:options owner:self userInfo:nil]; - - [self addTrackingArea:tracking_area]; - [super updateTrackingAreas]; -} - -// MARK: Keyboard - -- (void)keyDown:(NSEvent *)event { - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { - return; - } - - DisplayServerOSX::WindowData &wd = ds->get_window(window_id); - - ignore_momentum_scroll = true; - - // Ignore all input if IME input is in progress. - if (!ime_input_event_in_progress) { - NSString *characters = [event characters]; - NSUInteger length = [characters length]; - - if (!wd.im_active && length > 0 && keycode_has_unicode(KeyMappingOSX::remap_key([event keyCode], [event modifierFlags]))) { - // Fallback unicode character handler used if IME is not active. - Char16String text; - text.resize([characters length] + 1); - [characters getCharacters:(unichar *)text.ptrw() range:NSMakeRange(0, [characters length])]; - - String u32text; - u32text.parse_utf16(text.ptr(), text.length()); - - for (int i = 0; i < u32text.length(); i++) { - const char32_t codepoint = u32text[i]; - - DisplayServerOSX::KeyEvent ke; - - ke.window_id = window_id; - ke.osx_state = [event modifierFlags]; - ke.pressed = true; - ke.echo = [event isARepeat]; - ke.keycode = KeyMappingOSX::remap_key([event keyCode], [event modifierFlags]); - ke.physical_keycode = KeyMappingOSX::translate_key([event keyCode]); - ke.raw = true; - ke.unicode = codepoint; - - ds->push_to_key_event_buffer(ke); - } - } else { - DisplayServerOSX::KeyEvent ke; - - ke.window_id = window_id; - ke.osx_state = [event modifierFlags]; - ke.pressed = true; - ke.echo = [event isARepeat]; - ke.keycode = KeyMappingOSX::remap_key([event keyCode], [event modifierFlags]); - ke.physical_keycode = KeyMappingOSX::translate_key([event keyCode]); - ke.raw = false; - ke.unicode = 0; - - ds->push_to_key_event_buffer(ke); - } - } - - // Pass events to IME handler - if (wd.im_active) { - [self interpretKeyEvents:[NSArray arrayWithObject:event]]; - } -} - -- (void)flagsChanged:(NSEvent *)event { - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { - return; - } - ignore_momentum_scroll = true; - - // Ignore all input if IME input is in progress - if (!ime_input_event_in_progress) { - DisplayServerOSX::KeyEvent ke; - - ke.window_id = window_id; - ke.echo = false; - ke.raw = true; - - int key = [event keyCode]; - int mod = [event modifierFlags]; - - if (key == 0x36 || key == 0x37) { - if (mod & NSEventModifierFlagCommand) { - mod &= ~NSEventModifierFlagCommand; - ke.pressed = true; - } else { - ke.pressed = false; - } - } else if (key == 0x38 || key == 0x3c) { - if (mod & NSEventModifierFlagShift) { - mod &= ~NSEventModifierFlagShift; - ke.pressed = true; - } else { - ke.pressed = false; - } - } else if (key == 0x3a || key == 0x3d) { - if (mod & NSEventModifierFlagOption) { - mod &= ~NSEventModifierFlagOption; - ke.pressed = true; - } else { - ke.pressed = false; - } - } else if (key == 0x3b || key == 0x3e) { - if (mod & NSEventModifierFlagControl) { - mod &= ~NSEventModifierFlagControl; - ke.pressed = true; - } else { - ke.pressed = false; - } - } else { - return; - } - - ke.osx_state = mod; - ke.keycode = KeyMappingOSX::remap_key(key, mod); - ke.physical_keycode = KeyMappingOSX::translate_key(key); - ke.unicode = 0; - - ds->push_to_key_event_buffer(ke); - } -} - -- (void)keyUp:(NSEvent *)event { - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { - return; - } - - DisplayServerOSX::WindowData &wd = ds->get_window(window_id); - - // Ignore all input if IME input is in progress. - if (!ime_input_event_in_progress) { - NSString *characters = [event characters]; - NSUInteger length = [characters length]; - - // Fallback unicode character handler used if IME is not active. - if (!wd.im_active && length > 0 && keycode_has_unicode(KeyMappingOSX::remap_key([event keyCode], [event modifierFlags]))) { - Char16String text; - text.resize([characters length] + 1); - [characters getCharacters:(unichar *)text.ptrw() range:NSMakeRange(0, [characters length])]; - - String u32text; - u32text.parse_utf16(text.ptr(), text.length()); - - for (int i = 0; i < u32text.length(); i++) { - const char32_t codepoint = u32text[i]; - DisplayServerOSX::KeyEvent ke; - - ke.window_id = window_id; - ke.osx_state = [event modifierFlags]; - ke.pressed = false; - ke.echo = [event isARepeat]; - ke.keycode = KeyMappingOSX::remap_key([event keyCode], [event modifierFlags]); - ke.physical_keycode = KeyMappingOSX::translate_key([event keyCode]); - ke.raw = true; - ke.unicode = codepoint; - - ds->push_to_key_event_buffer(ke); - } - } else { - DisplayServerOSX::KeyEvent ke; - - ke.window_id = window_id; - ke.osx_state = [event modifierFlags]; - ke.pressed = false; - ke.echo = [event isARepeat]; - ke.keycode = KeyMappingOSX::remap_key([event keyCode], [event modifierFlags]); - ke.physical_keycode = KeyMappingOSX::translate_key([event keyCode]); - ke.raw = true; - ke.unicode = 0; - - ds->push_to_key_event_buffer(ke); - } - } -} - -// MARK: Scroll and pan - -- (void)processScrollEvent:(NSEvent *)event button:(MouseButton)button factor:(double)factor { - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { - return; - } - - DisplayServerOSX::WindowData &wd = ds->get_window(window_id); - MouseButton mask = mouse_button_to_mask(button); - - Ref<InputEventMouseButton> sc; - sc.instantiate(); - - sc->set_window_id(window_id); - ds->get_key_modifier_state([event modifierFlags], sc); - sc->set_button_index(button); - sc->set_factor(factor); - sc->set_pressed(true); - sc->set_position(wd.mouse_pos); - sc->set_global_position(wd.mouse_pos); - MouseButton last_button_state = ds->mouse_get_button_state() | (MouseButton)mask; - sc->set_button_mask(last_button_state); - ds->mouse_set_button_state(last_button_state); - - Input::get_singleton()->parse_input_event(sc); - - sc.instantiate(); - sc->set_window_id(window_id); - sc->set_button_index(button); - sc->set_factor(factor); - sc->set_pressed(false); - sc->set_position(wd.mouse_pos); - sc->set_global_position(wd.mouse_pos); - last_button_state &= (MouseButton)~mask; - sc->set_button_mask(last_button_state); - ds->mouse_set_button_state(last_button_state); - - Input::get_singleton()->parse_input_event(sc); -} - -- (void)processPanEvent:(NSEvent *)event dx:(double)dx dy:(double)dy { - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { - return; - } - - DisplayServerOSX::WindowData &wd = ds->get_window(window_id); - - Ref<InputEventPanGesture> pg; - pg.instantiate(); - - pg->set_window_id(window_id); - ds->get_key_modifier_state([event modifierFlags], pg); - pg->set_position(wd.mouse_pos); - pg->set_delta(Vector2(-dx, -dy)); - - Input::get_singleton()->parse_input_event(pg); -} - -- (void)scrollWheel:(NSEvent *)event { - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { - return; - } - - DisplayServerOSX::WindowData &wd = ds->get_window(window_id); - ds->update_mouse_pos(wd, [event locationInWindow]); - - double delta_x = [event scrollingDeltaX]; - double delta_y = [event scrollingDeltaY]; - - if ([event hasPreciseScrollingDeltas]) { - delta_x *= 0.03; - delta_y *= 0.03; - } - - if ([event momentumPhase] != NSEventPhaseNone) { - if (ignore_momentum_scroll) { - return; - } - } else { - ignore_momentum_scroll = false; - } - - if ([event phase] != NSEventPhaseNone || [event momentumPhase] != NSEventPhaseNone) { - [self processPanEvent:event dx:delta_x dy:delta_y]; - } else { - if (fabs(delta_x)) { - [self processScrollEvent:event button:(0 > delta_x ? MouseButton::WHEEL_RIGHT : MouseButton::WHEEL_LEFT) factor:fabs(delta_x * 0.3)]; - } - if (fabs(delta_y)) { - [self processScrollEvent:event button:(0 < delta_y ? MouseButton::WHEEL_UP : MouseButton::WHEEL_DOWN) factor:fabs(delta_y * 0.3)]; - } - } -} - -@end diff --git a/platform/osx/godot_main_osx.mm b/platform/osx/godot_main_osx.mm deleted file mode 100644 index 722928ad60..0000000000 --- a/platform/osx/godot_main_osx.mm +++ /dev/null @@ -1,92 +0,0 @@ -/*************************************************************************/ -/* godot_main_osx.mm */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "main/main.h" - -#include "os_osx.h" - -#include <string.h> -#include <unistd.h> - -#if defined(SANITIZERS_ENABLED) -#include <sys/resource.h> -#endif - -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 - -#if defined(SANITIZERS_ENABLED) - // Note: Set stack size to be at least 30 MB (vs 8 MB default) to avoid overflow, address sanitizer can increase stack usage up to 3 times. - struct rlimit stack_lim = { 0x1E00000, 0x1E00000 }; - setrlimit(RLIMIT_STACK, &stack_lim); -#endif - - int first_arg = 1; - const char *dbg_arg = "-NSDocumentRevisionsDebugMode"; - printf("arguments\n"); - for (int i = 0; i < argc; i++) { - if (strcmp(dbg_arg, argv[i]) == 0) { - first_arg = i + 2; - } - printf("%i: %s\n", i, argv[i]); - } - -#ifdef DEBUG_ENABLED - // Lets report the path we made current after all that. - char cwd[4096]; - getcwd(cwd, 4096); - printf("Current path: %s\n", cwd); -#endif - - OS_OSX os; - Error err; - - // We must override main when testing is enabled. - TEST_MAIN_OVERRIDE - - err = Main::setup(argv[0], argc - first_arg, &argv[first_arg]); - - if (err == ERR_HELP) { // Returned by --help and --version, so success. - return 0; - } else if (err != OK) { - return 255; - } - - if (Main::start()) { - os.run(); // It is actually the OS that decides how to run. - } - - Main::cleanup(); - - return os.get_exit_code(); -} diff --git a/platform/osx/godot_menu_item.h b/platform/osx/godot_menu_item.h deleted file mode 100644 index 2c12897f10..0000000000 --- a/platform/osx/godot_menu_item.h +++ /dev/null @@ -1,61 +0,0 @@ -/*************************************************************************/ -/* godot_menu_item.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* 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. */ -/*************************************************************************/ - -#ifndef GODOT_MENU_ITEM_H -#define GODOT_MENU_ITEM_H - -#include "servers/display_server.h" - -#import <AppKit/AppKit.h> -#import <Foundation/Foundation.h> - -enum GlobalMenuCheckType { - CHECKABLE_TYPE_NONE, - CHECKABLE_TYPE_CHECK_BOX, - CHECKABLE_TYPE_RADIO_BUTTON, -}; - -@interface GodotMenuItem : NSObject { -@public - Callable callback; - Variant meta; - int id; - GlobalMenuCheckType checkable_type; - int max_states; - int state; - Ref<Image> img; -} - -@end - -@implementation GodotMenuItem -@end - -#endif // GODOT_MENU_ITEM_H diff --git a/platform/osx/godot_window.h b/platform/osx/godot_window.h deleted file mode 100644 index 16ff101142..0000000000 --- a/platform/osx/godot_window.h +++ /dev/null @@ -1,47 +0,0 @@ -/*************************************************************************/ -/* godot_window.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* 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. */ -/*************************************************************************/ - -#ifndef GODOT_WINDOW_H -#define GODOT_WINDOW_H - -#include "servers/display_server.h" - -#import <AppKit/AppKit.h> -#import <Foundation/Foundation.h> - -@interface GodotWindow : NSWindow { - DisplayServer::WindowID window_id; -} - -- (void)setWindowID:(DisplayServer::WindowID)wid; - -@end - -#endif //GODOT_WINDOW_H diff --git a/platform/osx/godot_window.mm b/platform/osx/godot_window.mm deleted file mode 100644 index d43853a94b..0000000000 --- a/platform/osx/godot_window.mm +++ /dev/null @@ -1,69 +0,0 @@ -/*************************************************************************/ -/* godot_window.mm */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "godot_window.h" - -#include "display_server_osx.h" - -@implementation GodotWindow - -- (id)init { - self = [super init]; - window_id = DisplayServer::INVALID_WINDOW_ID; - return self; -} - -- (void)setWindowID:(DisplayServerOSX::WindowID)wid { - window_id = wid; -} - -- (BOOL)canBecomeKeyWindow { - // Required for NSWindowStyleMaskBorderless windows. - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { - return YES; - } - - DisplayServerOSX::WindowData &wd = ds->get_window(window_id); - return !wd.no_focus && !wd.is_popup; -} - -- (BOOL)canBecomeMainWindow { - // Required for NSWindowStyleMaskBorderless windows. - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { - return YES; - } - - DisplayServerOSX::WindowData &wd = ds->get_window(window_id); - return !wd.no_focus && !wd.is_popup; -} - -@end diff --git a/platform/osx/godot_window_delegate.h b/platform/osx/godot_window_delegate.h deleted file mode 100644 index 8a1f681fcd..0000000000 --- a/platform/osx/godot_window_delegate.h +++ /dev/null @@ -1,47 +0,0 @@ -/*************************************************************************/ -/* godot_window_delegate.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* 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. */ -/*************************************************************************/ - -#ifndef GODOT_WINDOW_DELEGATE_H -#define GODOT_WINDOW_DELEGATE_H - -#include "servers/display_server.h" - -#import <AppKit/AppKit.h> -#import <Foundation/Foundation.h> - -@interface GodotWindowDelegate : NSObject <NSWindowDelegate> { - DisplayServer::WindowID window_id; -} - -- (void)setWindowID:(DisplayServer::WindowID)wid; - -@end - -#endif //GODOT_WINDOW_DELEGATE_H diff --git a/platform/osx/godot_window_delegate.mm b/platform/osx/godot_window_delegate.mm deleted file mode 100644 index 521127f01b..0000000000 --- a/platform/osx/godot_window_delegate.mm +++ /dev/null @@ -1,270 +0,0 @@ -/*************************************************************************/ -/* godot_window_delegate.mm */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "godot_window_delegate.h" - -#include "display_server_osx.h" - -@implementation GodotWindowDelegate - -- (void)setWindowID:(DisplayServer::WindowID)wid { - window_id = wid; -} - -- (BOOL)windowShouldClose:(id)sender { - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { - return YES; - } - - ds->send_window_event(ds->get_window(window_id), DisplayServerOSX::WINDOW_EVENT_CLOSE_REQUEST); - return NO; -} - -- (void)windowWillClose:(NSNotification *)notification { - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { - return; - } - - ds->popup_close(window_id); - - DisplayServerOSX::WindowData &wd = ds->get_window(window_id); - while (wd.transient_children.size()) { - ds->window_set_transient(*wd.transient_children.begin(), DisplayServerOSX::INVALID_WINDOW_ID); - } - - if (wd.transient_parent != DisplayServerOSX::INVALID_WINDOW_ID) { - ds->window_set_transient(window_id, DisplayServerOSX::INVALID_WINDOW_ID); - } - - ds->window_destroy(window_id); -} - -- (void)windowDidEnterFullScreen:(NSNotification *)notification { - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { - return; - } - - DisplayServerOSX::WindowData &wd = ds->get_window(window_id); - wd.fullscreen = true; - // Reset window size limits. - [wd.window_object setContentMinSize:NSMakeSize(0, 0)]; - [wd.window_object setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)]; - - // Force window resize event. - [self windowDidResize:notification]; -} - -- (void)windowDidExitFullScreen:(NSNotification *)notification { - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { - return; - } - - DisplayServerOSX::WindowData &wd = ds->get_window(window_id); - wd.fullscreen = false; - - // Set window size limits. - const float scale = ds->screen_get_max_scale(); - if (wd.min_size != Size2i()) { - Size2i size = wd.min_size / scale; - [wd.window_object setContentMinSize:NSMakeSize(size.x, size.y)]; - } - if (wd.max_size != Size2i()) { - Size2i size = wd.max_size / scale; - [wd.window_object setContentMaxSize:NSMakeSize(size.x, size.y)]; - } - - // Restore resizability state. - if (wd.resize_disabled) { - [wd.window_object setStyleMask:[wd.window_object styleMask] & ~NSWindowStyleMaskResizable]; - } - - // Restore on-top state. - if (wd.on_top) { - [wd.window_object setLevel:NSFloatingWindowLevel]; - } - - // Force window resize event. - [self windowDidResize:notification]; -} - -- (void)windowDidChangeBackingProperties:(NSNotification *)notification { - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { - return; - } - - DisplayServerOSX::WindowData &wd = ds->get_window(window_id); - - CGFloat new_scale_factor = [wd.window_object backingScaleFactor]; - CGFloat old_scale_factor = [[[notification userInfo] objectForKey:@"NSBackingPropertyOldScaleFactorKey"] doubleValue]; - - if (new_scale_factor != old_scale_factor) { - // Set new display scale and window size. - const float scale = ds->screen_get_max_scale(); - const NSRect content_rect = [wd.window_view frame]; - - wd.size.width = content_rect.size.width * scale; - wd.size.height = content_rect.size.height * scale; - - ds->send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_DPI_CHANGE); - - CALayer *layer = [wd.window_view layer]; - if (layer) { - layer.contentsScale = scale; - } - - //Force window resize event - [self windowDidResize:notification]; - } -} - -- (void)windowWillStartLiveResize:(NSNotification *)notification { - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (ds) { - ds->set_is_resizing(true); - } -} - -- (void)windowDidEndLiveResize:(NSNotification *)notification { - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (ds) { - ds->set_is_resizing(false); - } -} - -- (void)windowDidResize:(NSNotification *)notification { - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { - return; - } - - DisplayServerOSX::WindowData &wd = ds->get_window(window_id); - const NSRect content_rect = [wd.window_view frame]; - const float scale = ds->screen_get_max_scale(); - wd.size.width = content_rect.size.width * scale; - wd.size.height = content_rect.size.height * scale; - - CALayer *layer = [wd.window_view layer]; - if (layer) { - layer.contentsScale = scale; - } - - ds->window_resize(window_id, wd.size.width, wd.size.height); - - if (!wd.rect_changed_callback.is_null()) { - Variant size = Rect2i(ds->window_get_position(window_id), ds->window_get_size(window_id)); - Variant *sizep = &size; - Variant ret; - Callable::CallError ce; - wd.rect_changed_callback.call((const Variant **)&sizep, 1, ret, ce); - } -} - -- (void)windowDidMove:(NSNotification *)notification { - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { - return; - } - - DisplayServerOSX::WindowData &wd = ds->get_window(window_id); - ds->release_pressed_events(); - - if (!wd.rect_changed_callback.is_null()) { - Variant size = Rect2i(ds->window_get_position(window_id), ds->window_get_size(window_id)); - Variant *sizep = &size; - Variant ret; - Callable::CallError ce; - wd.rect_changed_callback.call((const Variant **)&sizep, 1, ret, ce); - } -} - -- (void)windowDidBecomeKey:(NSNotification *)notification { - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { - return; - } - - DisplayServerOSX::WindowData &wd = ds->get_window(window_id); - - if (ds->mouse_get_mode() == DisplayServer::MOUSE_MODE_CAPTURED) { - const NSRect content_rect = [wd.window_view frame]; - NSRect point_in_window_rect = NSMakeRect(content_rect.size.width / 2, content_rect.size.height / 2, 0, 0); - NSPoint point_on_screen = [[wd.window_view window] convertRectToScreen:point_in_window_rect].origin; - CGPoint mouse_warp_pos = { point_on_screen.x, CGDisplayBounds(CGMainDisplayID()).size.height - point_on_screen.y }; - CGWarpMouseCursorPosition(mouse_warp_pos); - } else { - ds->update_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream]); - } - - ds->set_last_focused_window(window_id); - ds->send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_FOCUS_IN); -} - -- (void)windowDidResignKey:(NSNotification *)notification { - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { - return; - } - - DisplayServerOSX::WindowData &wd = ds->get_window(window_id); - - ds->release_pressed_events(); - ds->send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_FOCUS_OUT); -} - -- (void)windowDidMiniaturize:(NSNotification *)notification { - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { - return; - } - - DisplayServerOSX::WindowData &wd = ds->get_window(window_id); - - ds->release_pressed_events(); - ds->send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_FOCUS_OUT); -} - -- (void)windowDidDeminiaturize:(NSNotification *)notification { - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { - return; - } - - DisplayServerOSX::WindowData &wd = ds->get_window(window_id); - - ds->set_last_focused_window(window_id); - ds->send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_FOCUS_IN); -} - -@end diff --git a/platform/osx/joypad_osx.cpp b/platform/osx/joypad_osx.cpp deleted file mode 100644 index be9567e17c..0000000000 --- a/platform/osx/joypad_osx.cpp +++ /dev/null @@ -1,616 +0,0 @@ -/*************************************************************************/ -/* joypad_osx.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "joypad_osx.h" - -#include <machine/endian.h> - -#define GODOT_JOY_LOOP_RUN_MODE CFSTR("GodotJoypad") - -static JoypadOSX *self = nullptr; - -joypad::joypad() { - ff_constant_force.lMagnitude = 10000; - ff_effect.dwDuration = 0; - ff_effect.dwSamplePeriod = 0; - ff_effect.dwGain = 10000; - ff_effect.dwFlags = FFEFF_OBJECTOFFSETS; - ff_effect.dwTriggerButton = FFEB_NOTRIGGER; - ff_effect.dwStartDelay = 0; - ff_effect.dwTriggerRepeatInterval = 0; - ff_effect.lpEnvelope = nullptr; - ff_effect.cbTypeSpecificParams = sizeof(FFCONSTANTFORCE); - ff_effect.lpvTypeSpecificParams = &ff_constant_force; - ff_effect.dwSize = sizeof(ff_effect); -} - -void joypad::free() { - if (device_ref) { - IOHIDDeviceUnscheduleFromRunLoop(device_ref, CFRunLoopGetCurrent(), GODOT_JOY_LOOP_RUN_MODE); - } - if (ff_device) { - FFDeviceReleaseEffect(ff_device, ff_object); - FFReleaseDevice(ff_device); - ff_device = nullptr; - memfree(ff_axes); - memfree(ff_directions); - } -} - -bool joypad::has_element(IOHIDElementCookie p_cookie, Vector<rec_element> *p_list) const { - for (int i = 0; i < p_list->size(); i++) { - if (p_cookie == p_list->get(i).cookie) { - return true; - } - } - return false; -} - -int joypad::get_hid_element_state(rec_element *p_element) const { - int value = 0; - if (p_element && p_element->ref) { - IOHIDValueRef valueRef; - if (IOHIDDeviceGetValue(device_ref, p_element->ref, &valueRef) == kIOReturnSuccess) { - value = (SInt32)IOHIDValueGetIntegerValue(valueRef); - - // Record min and max for auto calibration. - if (value < p_element->min) { - p_element->min = value; - } - if (value > p_element->max) { - p_element->max = value; - } - } - } - return value; -} - -void joypad::add_hid_element(IOHIDElementRef p_element) { - const CFTypeID elementTypeID = p_element ? CFGetTypeID(p_element) : 0; - - if (p_element && (elementTypeID == IOHIDElementGetTypeID())) { - const IOHIDElementCookie cookie = IOHIDElementGetCookie(p_element); - const uint32_t usagePage = IOHIDElementGetUsagePage(p_element); - const uint32_t usage = IOHIDElementGetUsage(p_element); - Vector<rec_element> *list = nullptr; - - switch (IOHIDElementGetType(p_element)) { - case kIOHIDElementTypeInput_Misc: - case kIOHIDElementTypeInput_Button: - case kIOHIDElementTypeInput_Axis: { - switch (usagePage) { - case kHIDPage_GenericDesktop: - switch (usage) { - case kHIDUsage_GD_X: - case kHIDUsage_GD_Y: - case kHIDUsage_GD_Z: - case kHIDUsage_GD_Rx: - case kHIDUsage_GD_Ry: - case kHIDUsage_GD_Rz: - case kHIDUsage_GD_Slider: - case kHIDUsage_GD_Dial: - case kHIDUsage_GD_Wheel: - if (!has_element(cookie, &axis_elements)) { - list = &axis_elements; - } - break; - - case kHIDUsage_GD_Hatswitch: - if (!has_element(cookie, &hat_elements)) { - list = &hat_elements; - } - break; - case kHIDUsage_GD_DPadUp: - case kHIDUsage_GD_DPadDown: - case kHIDUsage_GD_DPadRight: - case kHIDUsage_GD_DPadLeft: - case kHIDUsage_GD_Start: - case kHIDUsage_GD_Select: - if (!has_element(cookie, &button_elements)) { - list = &button_elements; - } - break; - } - break; - - case kHIDPage_Simulation: - switch (usage) { - case kHIDUsage_Sim_Rudder: - case kHIDUsage_Sim_Throttle: - case kHIDUsage_Sim_Accelerator: - case kHIDUsage_Sim_Brake: - if (!has_element(cookie, &axis_elements)) { - list = &axis_elements; - } - break; - - default: - break; - } - break; - - case kHIDPage_Button: - case kHIDPage_Consumer: - if (!has_element(cookie, &button_elements)) { - list = &button_elements; - } - break; - - default: - break; - } - } break; - - case kIOHIDElementTypeCollection: { - CFArrayRef array = IOHIDElementGetChildren(p_element); - if (array) { - add_hid_elements(array); - } - } break; - - default: - break; - } - - if (list) { // Add to list. - rec_element element; - - element.ref = p_element; - element.usage = usage; - - element.min = (SInt32)IOHIDElementGetLogicalMin(p_element); - element.max = (SInt32)IOHIDElementGetLogicalMax(p_element); - element.cookie = IOHIDElementGetCookie(p_element); - list->push_back(element); - list->sort_custom<rec_element::Comparator>(); - } - } -} - -static void hid_element_added(const void *p_value, void *p_parameter) { - joypad *joy = static_cast<joypad *>(p_parameter); - joy->add_hid_element((IOHIDElementRef)p_value); -} - -void joypad::add_hid_elements(CFArrayRef p_array) { - CFRange range = { 0, CFArrayGetCount(p_array) }; - CFArrayApplyFunction(p_array, range, hid_element_added, this); -} - -static void joypad_removed_callback(void *ctx, IOReturn res, void *sender, IOHIDDeviceRef ioHIDDeviceObject) { - self->_device_removed(res, ioHIDDeviceObject); -} - -static void joypad_added_callback(void *ctx, IOReturn res, void *sender, IOHIDDeviceRef ioHIDDeviceObject) { - self->_device_added(res, ioHIDDeviceObject); -} - -static bool is_joypad(IOHIDDeviceRef p_device_ref) { - int usage_page = 0; - int usage = 0; - CFTypeRef refCF = IOHIDDeviceGetProperty(p_device_ref, CFSTR(kIOHIDPrimaryUsagePageKey)); - if (refCF) { - CFNumberGetValue((CFNumberRef)refCF, kCFNumberSInt32Type, &usage_page); - } - if (usage_page != kHIDPage_GenericDesktop) { - return false; - } - - refCF = IOHIDDeviceGetProperty(p_device_ref, CFSTR(kIOHIDPrimaryUsageKey)); - if (refCF) { - CFNumberGetValue((CFNumberRef)refCF, kCFNumberSInt32Type, &usage); - } - if ((usage != kHIDUsage_GD_Joystick && - usage != kHIDUsage_GD_GamePad && - usage != kHIDUsage_GD_MultiAxisController)) { - return false; - } - return true; -} - -void JoypadOSX::_device_added(IOReturn p_res, IOHIDDeviceRef p_device) { - if (p_res != kIOReturnSuccess || have_device(p_device)) { - return; - } - - joypad new_joypad; - if (is_joypad(p_device)) { - configure_joypad(p_device, &new_joypad); -#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060 - if (IOHIDDeviceGetService) { -#endif - const io_service_t ioservice = IOHIDDeviceGetService(p_device); - if ((ioservice) && (FFIsForceFeedback(ioservice) == FF_OK) && new_joypad.config_force_feedback(ioservice)) { - new_joypad.ffservice = ioservice; - } -#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060 - } -#endif - device_list.push_back(new_joypad); - } - IOHIDDeviceScheduleWithRunLoop(p_device, CFRunLoopGetCurrent(), GODOT_JOY_LOOP_RUN_MODE); -} - -void JoypadOSX::_device_removed(IOReturn p_res, IOHIDDeviceRef p_device) { - int device = get_joy_ref(p_device); - ERR_FAIL_COND(device == -1); - - input->joy_connection_changed(device_list[device].id, false, ""); - device_list.write[device].free(); - device_list.remove_at(device); -} - -static String _hex_str(uint8_t p_byte) { - static const char *dict = "0123456789abcdef"; - char ret[3]; - ret[2] = 0; - - ret[0] = dict[p_byte >> 4]; - ret[1] = dict[p_byte & 0xF]; - - return ret; -} - -bool JoypadOSX::configure_joypad(IOHIDDeviceRef p_device_ref, joypad *p_joy) { - p_joy->device_ref = p_device_ref; - // Get device name. - String name; - char c_name[256]; - CFTypeRef refCF = IOHIDDeviceGetProperty(p_device_ref, CFSTR(kIOHIDProductKey)); - if (!refCF) { - refCF = IOHIDDeviceGetProperty(p_device_ref, CFSTR(kIOHIDManufacturerKey)); - } - if ((!refCF) || (!CFStringGetCString((CFStringRef)refCF, c_name, sizeof(c_name), kCFStringEncodingUTF8))) { - name = "Unidentified Joypad"; - } else { - name = c_name; - } - - int id = input->get_unused_joy_id(); - ERR_FAIL_COND_V(id == -1, false); - p_joy->id = id; - int vendor = 0; - refCF = IOHIDDeviceGetProperty(p_device_ref, CFSTR(kIOHIDVendorIDKey)); - if (refCF) { - CFNumberGetValue((CFNumberRef)refCF, kCFNumberSInt32Type, &vendor); - } - - int product_id = 0; - refCF = IOHIDDeviceGetProperty(p_device_ref, CFSTR(kIOHIDProductIDKey)); - if (refCF) { - CFNumberGetValue((CFNumberRef)refCF, kCFNumberSInt32Type, &product_id); - } - - int version = 0; - refCF = IOHIDDeviceGetProperty(p_device_ref, CFSTR(kIOHIDVersionNumberKey)); - if (refCF) { - CFNumberGetValue((CFNumberRef)refCF, kCFNumberSInt32Type, &version); - } - - if (vendor && product_id) { - char uid[128]; - sprintf(uid, "%08x%08x%08x%08x", OSSwapHostToBigInt32(3), OSSwapHostToBigInt32(vendor), OSSwapHostToBigInt32(product_id), OSSwapHostToBigInt32(version)); - input->joy_connection_changed(id, true, name, uid); - } else { - // Bluetooth device. - String guid = "05000000"; - for (int i = 0; i < 12; i++) { - if (i < name.size()) { - guid += _hex_str(name[i]); - } else { - guid += "00"; - } - } - input->joy_connection_changed(id, true, name, guid); - } - - CFArrayRef array = IOHIDDeviceCopyMatchingElements(p_device_ref, nullptr, kIOHIDOptionsTypeNone); - if (array) { - p_joy->add_hid_elements(array); - CFRelease(array); - } - // Xbox controller hat values start at 1 rather than 0. - p_joy->offset_hat = vendor == 0x45e && - (product_id == 0x0b05 || - product_id == 0x02e0 || - product_id == 0x02fd || - product_id == 0x0b13); - - return true; -} - -#define FF_ERR() \ - { \ - if (ret != FF_OK) { \ - FFReleaseDevice(ff_device); \ - ff_device = nullptr; \ - return false; \ - } \ - } -bool joypad::config_force_feedback(io_service_t p_service) { - HRESULT ret = FFCreateDevice(p_service, &ff_device); - ERR_FAIL_COND_V(ret != FF_OK, false); - - ret = FFDeviceSendForceFeedbackCommand(ff_device, FFSFFC_RESET); - FF_ERR(); - - ret = FFDeviceSendForceFeedbackCommand(ff_device, FFSFFC_SETACTUATORSON); - FF_ERR(); - - if (check_ff_features()) { - ret = FFDeviceCreateEffect(ff_device, kFFEffectType_ConstantForce_ID, &ff_effect, &ff_object); - FF_ERR(); - return true; - } - FFReleaseDevice(ff_device); - ff_device = nullptr; - return false; -} -#undef FF_ERR - -#define TEST_FF(ff) (features.supportedEffects & (ff)) -bool joypad::check_ff_features() { - FFCAPABILITIES features; - HRESULT ret = FFDeviceGetForceFeedbackCapabilities(ff_device, &features); - if (ret == FF_OK && (features.supportedEffects & FFCAP_ET_CONSTANTFORCE)) { - uint32_t val; - ret = FFDeviceGetForceFeedbackProperty(ff_device, FFPROP_FFGAIN, &val, sizeof(val)); - if (ret != FF_OK) { - return false; - } - int num_axes = features.numFfAxes; - ff_axes = (DWORD *)memalloc(sizeof(DWORD) * num_axes); - ff_directions = (LONG *)memalloc(sizeof(LONG) * num_axes); - - for (int i = 0; i < num_axes; i++) { - ff_axes[i] = features.ffAxes[i]; - ff_directions[i] = 0; - } - - ff_effect.cAxes = num_axes; - ff_effect.rgdwAxes = ff_axes; - ff_effect.rglDirection = ff_directions; - return true; - } - return false; -} - -static HatMask process_hat_value(int p_min, int p_max, int p_value, bool p_offset_hat) { - int range = (p_max - p_min + 1); - int value = p_value - p_min; - HatMask hat_value = HatMask::CENTER; - if (range == 4) { - value *= 2; - } - if (p_offset_hat) { - value -= 1; - } - - switch (value) { - case 0: - hat_value = HatMask::UP; - break; - case 1: - hat_value = (HatMask::UP | HatMask::RIGHT); - break; - case 2: - hat_value = HatMask::RIGHT; - break; - case 3: - hat_value = (HatMask::DOWN | HatMask::RIGHT); - break; - case 4: - hat_value = HatMask::DOWN; - break; - case 5: - hat_value = (HatMask::DOWN | HatMask::LEFT); - break; - case 6: - hat_value = HatMask::LEFT; - break; - case 7: - hat_value = (HatMask::UP | HatMask::LEFT); - break; - default: - hat_value = HatMask::CENTER; - break; - } - return hat_value; -} - -void JoypadOSX::poll_joypads() const { - while (CFRunLoopRunInMode(GODOT_JOY_LOOP_RUN_MODE, 0, TRUE) == kCFRunLoopRunHandledSource) { - // No-op. Pending callbacks will fire. - } -} - -static float axis_correct(int p_value, int p_min, int p_max) { - // Convert to a value between -1.0f and 1.0f. - return 2.0f * (p_value - p_min) / (p_max - p_min) - 1.0f; -} - -void JoypadOSX::process_joypads() { - poll_joypads(); - - for (int i = 0; i < device_list.size(); i++) { - joypad &joy = device_list.write[i]; - - for (int j = 0; j < joy.axis_elements.size(); j++) { - rec_element &elem = joy.axis_elements.write[j]; - int value = joy.get_hid_element_state(&elem); - input->joy_axis(joy.id, (JoyAxis)j, axis_correct(value, elem.min, elem.max)); - } - for (int j = 0; j < joy.button_elements.size(); j++) { - int value = joy.get_hid_element_state(&joy.button_elements.write[j]); - input->joy_button(joy.id, (JoyButton)j, (value >= 1)); - } - for (int j = 0; j < joy.hat_elements.size(); j++) { - rec_element &elem = joy.hat_elements.write[j]; - int value = joy.get_hid_element_state(&elem); - HatMask hat_value = process_hat_value(elem.min, elem.max, value, joy.offset_hat); - input->joy_hat(joy.id, hat_value); - } - - if (joy.ffservice) { - uint64_t timestamp = input->get_joy_vibration_timestamp(joy.id); - if (timestamp > joy.ff_timestamp) { - Vector2 strength = input->get_joy_vibration_strength(joy.id); - float duration = input->get_joy_vibration_duration(joy.id); - if (strength.x == 0 && strength.y == 0) { - joypad_vibration_stop(joy.id, timestamp); - } else { - float gain = MAX(strength.x, strength.y); - joypad_vibration_start(joy.id, gain, duration, timestamp); - } - } - } - } -} - -void JoypadOSX::joypad_vibration_start(int p_id, float p_magnitude, float p_duration, uint64_t p_timestamp) { - joypad *joy = &device_list.write[get_joy_index(p_id)]; - joy->ff_timestamp = p_timestamp; - joy->ff_effect.dwDuration = p_duration * FF_SECONDS; - joy->ff_effect.dwGain = p_magnitude * FF_FFNOMINALMAX; - FFEffectSetParameters(joy->ff_object, &joy->ff_effect, FFEP_DURATION | FFEP_GAIN); - FFEffectStart(joy->ff_object, 1, 0); -} - -void JoypadOSX::joypad_vibration_stop(int p_id, uint64_t p_timestamp) { - joypad *joy = &device_list.write[get_joy_index(p_id)]; - joy->ff_timestamp = p_timestamp; - FFEffectStop(joy->ff_object); -} - -int JoypadOSX::get_joy_index(int p_id) const { - for (int i = 0; i < device_list.size(); i++) { - if (device_list[i].id == p_id) { - return i; - } - } - return -1; -} - -int JoypadOSX::get_joy_ref(IOHIDDeviceRef p_device) const { - for (int i = 0; i < device_list.size(); i++) { - if (device_list[i].device_ref == p_device) { - return i; - } - } - return -1; -} - -bool JoypadOSX::have_device(IOHIDDeviceRef p_device) const { - for (int i = 0; i < device_list.size(); i++) { - if (device_list[i].device_ref == p_device) { - return true; - } - } - return false; -} - -static CFDictionaryRef create_match_dictionary(const UInt32 page, const UInt32 usage, int *okay) { - CFDictionaryRef retval = nullptr; - CFNumberRef pageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &page); - CFNumberRef usageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage); - - if (pageNumRef && usageNumRef) { - const void *keys[2] = { (void *)CFSTR(kIOHIDDeviceUsagePageKey), (void *)CFSTR(kIOHIDDeviceUsageKey) }; - const void *vals[2] = { (void *)pageNumRef, (void *)usageNumRef }; - retval = CFDictionaryCreate(kCFAllocatorDefault, keys, vals, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - } - - if (pageNumRef) { - CFRelease(pageNumRef); - } - if (usageNumRef) { - CFRelease(usageNumRef); - } - - if (!retval) { - *okay = 0; - } - - return retval; -} - -void JoypadOSX::config_hid_manager(CFArrayRef p_matching_array) const { - CFRunLoopRef runloop = CFRunLoopGetCurrent(); - IOReturn ret = IOHIDManagerOpen(hid_manager, kIOHIDOptionsTypeNone); - ERR_FAIL_COND(ret != kIOReturnSuccess); - - IOHIDManagerSetDeviceMatchingMultiple(hid_manager, p_matching_array); - IOHIDManagerRegisterDeviceMatchingCallback(hid_manager, joypad_added_callback, nullptr); - IOHIDManagerRegisterDeviceRemovalCallback(hid_manager, joypad_removed_callback, nullptr); - IOHIDManagerScheduleWithRunLoop(hid_manager, runloop, GODOT_JOY_LOOP_RUN_MODE); - - while (CFRunLoopRunInMode(GODOT_JOY_LOOP_RUN_MODE, 0, TRUE) == kCFRunLoopRunHandledSource) { - // No-op. Callback fires once per existing device. - } -} - -JoypadOSX::JoypadOSX(Input *in) { - self = this; - input = in; - - int okay = 1; - const void *vals[] = { - (void *)create_match_dictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick, &okay), - (void *)create_match_dictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad, &okay), - (void *)create_match_dictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_MultiAxisController, &okay), - }; - const size_t n_elements = sizeof(vals) / sizeof(vals[0]); - CFArrayRef array = okay ? CFArrayCreate(kCFAllocatorDefault, vals, n_elements, &kCFTypeArrayCallBacks) : nullptr; - - for (size_t i = 0; i < n_elements; i++) { - if (vals[i]) { - CFRelease((CFTypeRef)vals[i]); - } - } - - if (array) { - hid_manager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); - if (hid_manager) { - config_hid_manager(array); - } - CFRelease(array); - } -} - -JoypadOSX::~JoypadOSX() { - for (int i = 0; i < device_list.size(); i++) { - device_list.write[i].free(); - } - - IOHIDManagerUnscheduleFromRunLoop(hid_manager, CFRunLoopGetCurrent(), GODOT_JOY_LOOP_RUN_MODE); - IOHIDManagerClose(hid_manager, kIOHIDOptionsTypeNone); - CFRelease(hid_manager); - hid_manager = nullptr; -} diff --git a/platform/osx/joypad_osx.h b/platform/osx/joypad_osx.h deleted file mode 100644 index 3f89048ce6..0000000000 --- a/platform/osx/joypad_osx.h +++ /dev/null @@ -1,124 +0,0 @@ -/*************************************************************************/ -/* joypad_osx.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* 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. */ -/*************************************************************************/ - -#ifndef JOYPADOSX_H -#define JOYPADOSX_H - -#ifdef MACOS_10_0_4 -#import <IOKit/hidsystem/IOHIDUsageTables.h> -#else -#import <Kernel/IOKit/hidsystem/IOHIDUsageTables.h> -#endif -#import <ForceFeedback/ForceFeedback.h> -#import <ForceFeedback/ForceFeedbackConstants.h> -#import <IOKit/hid/IOHIDLib.h> - -#include "core/input/input.h" - -struct rec_element { - IOHIDElementRef ref; - IOHIDElementCookie cookie; - - uint32_t usage = 0; - - int min = 0; - int max = 0; - - struct Comparator { - bool operator()(const rec_element p_a, const rec_element p_b) const { return p_a.usage < p_b.usage; } - }; -}; - -struct joypad { - IOHIDDeviceRef device_ref = nullptr; - - Vector<rec_element> axis_elements; - Vector<rec_element> button_elements; - Vector<rec_element> hat_elements; - - int id = 0; - bool offset_hat = false; - - io_service_t ffservice = 0; // Interface for force feedback, 0 = no ff. - FFCONSTANTFORCE ff_constant_force; - FFDeviceObjectReference ff_device = nullptr; - FFEffectObjectReference ff_object = nullptr; - uint64_t ff_timestamp = 0; - LONG *ff_directions = nullptr; - FFEFFECT ff_effect; - DWORD *ff_axes = nullptr; - - void add_hid_elements(CFArrayRef p_array); - void add_hid_element(IOHIDElementRef p_element); - - bool has_element(IOHIDElementCookie p_cookie, Vector<rec_element> *p_list) const; - bool config_force_feedback(io_service_t p_service); - bool check_ff_features(); - - int get_hid_element_state(rec_element *p_element) const; - - void free(); - joypad(); -}; - -class JoypadOSX { - enum { - JOYPADS_MAX = 16, - }; - -private: - Input *input = nullptr; - IOHIDManagerRef hid_manager; - - Vector<joypad> device_list; - - bool have_device(IOHIDDeviceRef p_device) const; - bool configure_joypad(IOHIDDeviceRef p_device_ref, joypad *p_joy); - - int get_joy_index(int p_id) const; - int get_joy_ref(IOHIDDeviceRef p_device) const; - - void poll_joypads() const; - void config_hid_manager(CFArrayRef p_matching_array) const; - - void joypad_vibration_start(int p_id, float p_magnitude, float p_duration, uint64_t p_timestamp); - void joypad_vibration_stop(int p_id, uint64_t p_timestamp); - -public: - void process_joypads(); - - void _device_added(IOReturn p_res, IOHIDDeviceRef p_device); - void _device_removed(IOReturn p_res, IOHIDDeviceRef p_device); - - JoypadOSX(Input *in); - ~JoypadOSX(); -}; - -#endif // JOYPADOSX_H diff --git a/platform/osx/key_mapping_osx.h b/platform/osx/key_mapping_osx.h deleted file mode 100644 index 252cc907bb..0000000000 --- a/platform/osx/key_mapping_osx.h +++ /dev/null @@ -1,52 +0,0 @@ -/*************************************************************************/ -/* key_mapping_osx.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* 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. */ -/*************************************************************************/ - -#ifndef KEY_MAPPING_OSX_H -#define KEY_MAPPING_OSX_H - -#include "core/os/keyboard.h" - -class KeyMappingOSX { - KeyMappingOSX() {} - - static bool is_numpad_key(unsigned int key); - -public: - // Mappings input. - static Key translate_key(unsigned int key); - static unsigned int unmap_key(Key key); - static Key remap_key(unsigned int key, unsigned int state); - - // Mapping for menu shortcuts. - static String keycode_get_native_string(Key p_keycode); - static unsigned int keycode_get_native_mask(Key p_keycode); -}; - -#endif // KEY_MAPPING_OSX_H diff --git a/platform/osx/key_mapping_osx.mm b/platform/osx/key_mapping_osx.mm deleted file mode 100644 index 0bf6bc7d1c..0000000000 --- a/platform/osx/key_mapping_osx.mm +++ /dev/null @@ -1,496 +0,0 @@ -/*************************************************************************/ -/* key_mapping_osx.mm */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "key_mapping_osx.h" - -#import <Carbon/Carbon.h> -#import <Cocoa/Cocoa.h> - -bool KeyMappingOSX::is_numpad_key(unsigned int key) { - static const unsigned int table[] = { - 0x41, /* kVK_ANSI_KeypadDecimal */ - 0x43, /* kVK_ANSI_KeypadMultiply */ - 0x45, /* kVK_ANSI_KeypadPlus */ - 0x47, /* kVK_ANSI_KeypadClear */ - 0x4b, /* kVK_ANSI_KeypadDivide */ - 0x4c, /* kVK_ANSI_KeypadEnter */ - 0x4e, /* kVK_ANSI_KeypadMinus */ - 0x51, /* kVK_ANSI_KeypadEquals */ - 0x52, /* kVK_ANSI_Keypad0 */ - 0x53, /* kVK_ANSI_Keypad1 */ - 0x54, /* kVK_ANSI_Keypad2 */ - 0x55, /* kVK_ANSI_Keypad3 */ - 0x56, /* kVK_ANSI_Keypad4 */ - 0x57, /* kVK_ANSI_Keypad5 */ - 0x58, /* kVK_ANSI_Keypad6 */ - 0x59, /* kVK_ANSI_Keypad7 */ - 0x5b, /* kVK_ANSI_Keypad8 */ - 0x5c, /* kVK_ANSI_Keypad9 */ - 0x5f, /* kVK_JIS_KeypadComma */ - 0x00 - }; - for (int i = 0; table[i] != 0; i++) { - if (key == table[i]) { - return true; - } - } - return false; -} - -// Keyboard symbol translation table. -static const Key _osx_to_godot_table[128] = { - /* 00 */ Key::A, - /* 01 */ Key::S, - /* 02 */ Key::D, - /* 03 */ Key::F, - /* 04 */ Key::H, - /* 05 */ Key::G, - /* 06 */ Key::Z, - /* 07 */ Key::X, - /* 08 */ Key::C, - /* 09 */ Key::V, - /* 0a */ Key::SECTION, /* ISO Section */ - /* 0b */ Key::B, - /* 0c */ Key::Q, - /* 0d */ Key::W, - /* 0e */ Key::E, - /* 0f */ Key::R, - /* 10 */ Key::Y, - /* 11 */ Key::T, - /* 12 */ Key::KEY_1, - /* 13 */ Key::KEY_2, - /* 14 */ Key::KEY_3, - /* 15 */ Key::KEY_4, - /* 16 */ Key::KEY_6, - /* 17 */ Key::KEY_5, - /* 18 */ Key::EQUAL, - /* 19 */ Key::KEY_9, - /* 1a */ Key::KEY_7, - /* 1b */ Key::MINUS, - /* 1c */ Key::KEY_8, - /* 1d */ Key::KEY_0, - /* 1e */ Key::BRACERIGHT, - /* 1f */ Key::O, - /* 20 */ Key::U, - /* 21 */ Key::BRACELEFT, - /* 22 */ Key::I, - /* 23 */ Key::P, - /* 24 */ Key::ENTER, - /* 25 */ Key::L, - /* 26 */ Key::J, - /* 27 */ Key::APOSTROPHE, - /* 28 */ Key::K, - /* 29 */ Key::SEMICOLON, - /* 2a */ Key::BACKSLASH, - /* 2b */ Key::COMMA, - /* 2c */ Key::SLASH, - /* 2d */ Key::N, - /* 2e */ Key::M, - /* 2f */ Key::PERIOD, - /* 30 */ Key::TAB, - /* 31 */ Key::SPACE, - /* 32 */ Key::QUOTELEFT, - /* 33 */ Key::BACKSPACE, - /* 34 */ Key::UNKNOWN, - /* 35 */ Key::ESCAPE, - /* 36 */ Key::META, - /* 37 */ Key::META, - /* 38 */ Key::SHIFT, - /* 39 */ Key::CAPSLOCK, - /* 3a */ Key::ALT, - /* 3b */ Key::CTRL, - /* 3c */ Key::SHIFT, - /* 3d */ Key::ALT, - /* 3e */ Key::CTRL, - /* 3f */ Key::UNKNOWN, /* Function */ - /* 40 */ Key::F17, - /* 41 */ Key::KP_PERIOD, - /* 42 */ Key::UNKNOWN, - /* 43 */ Key::KP_MULTIPLY, - /* 44 */ Key::UNKNOWN, - /* 45 */ Key::KP_ADD, - /* 46 */ Key::UNKNOWN, - /* 47 */ Key::NUMLOCK, /* Really KeypadClear... */ - /* 48 */ Key::VOLUMEUP, /* VolumeUp */ - /* 49 */ Key::VOLUMEDOWN, /* VolumeDown */ - /* 4a */ Key::VOLUMEMUTE, /* Mute */ - /* 4b */ Key::KP_DIVIDE, - /* 4c */ Key::KP_ENTER, - /* 4d */ Key::UNKNOWN, - /* 4e */ Key::KP_SUBTRACT, - /* 4f */ Key::F18, - /* 50 */ Key::F19, - /* 51 */ Key::EQUAL, /* KeypadEqual */ - /* 52 */ Key::KP_0, - /* 53 */ Key::KP_1, - /* 54 */ Key::KP_2, - /* 55 */ Key::KP_3, - /* 56 */ Key::KP_4, - /* 57 */ Key::KP_5, - /* 58 */ Key::KP_6, - /* 59 */ Key::KP_7, - /* 5a */ Key::F20, - /* 5b */ Key::KP_8, - /* 5c */ Key::KP_9, - /* 5d */ Key::YEN, /* JIS Yen */ - /* 5e */ Key::UNDERSCORE, /* JIS Underscore */ - /* 5f */ Key::COMMA, /* JIS KeypadComma */ - /* 60 */ Key::F5, - /* 61 */ Key::F6, - /* 62 */ Key::F7, - /* 63 */ Key::F3, - /* 64 */ Key::F8, - /* 65 */ Key::F9, - /* 66 */ Key::UNKNOWN, /* JIS Eisu */ - /* 67 */ Key::F11, - /* 68 */ Key::UNKNOWN, /* JIS Kana */ - /* 69 */ Key::F13, - /* 6a */ Key::F16, - /* 6b */ Key::F14, - /* 6c */ Key::UNKNOWN, - /* 6d */ Key::F10, - /* 6e */ Key::MENU, - /* 6f */ Key::F12, - /* 70 */ Key::UNKNOWN, - /* 71 */ Key::F15, - /* 72 */ Key::INSERT, /* Really Help... */ - /* 73 */ Key::HOME, - /* 74 */ Key::PAGEUP, - /* 75 */ Key::KEY_DELETE, - /* 76 */ Key::F4, - /* 77 */ Key::END, - /* 78 */ Key::F2, - /* 79 */ Key::PAGEDOWN, - /* 7a */ Key::F1, - /* 7b */ Key::LEFT, - /* 7c */ Key::RIGHT, - /* 7d */ Key::DOWN, - /* 7e */ Key::UP, - /* 7f */ Key::UNKNOWN, -}; - -// Translates a OS X keycode to a Godot keycode. -Key KeyMappingOSX::translate_key(unsigned int key) { - if (key >= 128) { - return Key::UNKNOWN; - } - - return _osx_to_godot_table[key]; -} - -// Translates a Godot keycode back to a OSX keycode. -unsigned int KeyMappingOSX::unmap_key(Key key) { - for (int i = 0; i <= 126; i++) { - if (_osx_to_godot_table[i] == key) { - return i; - } - } - return 127; -} - -struct _KeyCodeMap { - UniChar kchar; - Key kcode; -}; - -static const _KeyCodeMap _keycodes[55] = { - { '`', Key::QUOTELEFT }, - { '~', Key::ASCIITILDE }, - { '0', Key::KEY_0 }, - { '1', Key::KEY_1 }, - { '2', Key::KEY_2 }, - { '3', Key::KEY_3 }, - { '4', Key::KEY_4 }, - { '5', Key::KEY_5 }, - { '6', Key::KEY_6 }, - { '7', Key::KEY_7 }, - { '8', Key::KEY_8 }, - { '9', Key::KEY_9 }, - { '-', Key::MINUS }, - { '_', Key::UNDERSCORE }, - { '=', Key::EQUAL }, - { '+', Key::PLUS }, - { 'q', Key::Q }, - { 'w', Key::W }, - { 'e', Key::E }, - { 'r', Key::R }, - { 't', Key::T }, - { 'y', Key::Y }, - { 'u', Key::U }, - { 'i', Key::I }, - { 'o', Key::O }, - { 'p', Key::P }, - { '[', Key::BRACELEFT }, - { ']', Key::BRACERIGHT }, - { '{', Key::BRACELEFT }, - { '}', Key::BRACERIGHT }, - { 'a', Key::A }, - { 's', Key::S }, - { 'd', Key::D }, - { 'f', Key::F }, - { 'g', Key::G }, - { 'h', Key::H }, - { 'j', Key::J }, - { 'k', Key::K }, - { 'l', Key::L }, - { ';', Key::SEMICOLON }, - { ':', Key::COLON }, - { '\'', Key::APOSTROPHE }, - { '\"', Key::QUOTEDBL }, - { '\\', Key::BACKSLASH }, - { '#', Key::NUMBERSIGN }, - { 'z', Key::Z }, - { 'x', Key::X }, - { 'c', Key::C }, - { 'v', Key::V }, - { 'b', Key::B }, - { 'n', Key::N }, - { 'm', Key::M }, - { ',', Key::COMMA }, - { '.', Key::PERIOD }, - { '/', Key::SLASH } -}; - -// Remap key according to current keyboard layout. -Key KeyMappingOSX::remap_key(unsigned int key, unsigned int state) { - if (is_numpad_key(key)) { - return translate_key(key); - } - - TISInputSourceRef current_keyboard = TISCopyCurrentKeyboardInputSource(); - if (!current_keyboard) { - return translate_key(key); - } - - CFDataRef layout_data = (CFDataRef)TISGetInputSourceProperty(current_keyboard, kTISPropertyUnicodeKeyLayoutData); - if (!layout_data) { - return translate_key(key); - } - - const UCKeyboardLayout *keyboard_layout = (const UCKeyboardLayout *)CFDataGetBytePtr(layout_data); - - UInt32 keys_down = 0; - UniChar chars[4]; - UniCharCount real_length; - - OSStatus err = UCKeyTranslate(keyboard_layout, - key, - kUCKeyActionDisplay, - (state >> 8) & 0xFF, - LMGetKbdType(), - kUCKeyTranslateNoDeadKeysBit, - &keys_down, - sizeof(chars) / sizeof(chars[0]), - &real_length, - chars); - - if (err != noErr) { - return translate_key(key); - } - - for (unsigned int i = 0; i < 55; i++) { - if (_keycodes[i].kchar == chars[0]) { - return _keycodes[i].kcode; - } - } - return translate_key(key); -} - -struct _KeyCodeText { - Key code; - char32_t text; -}; - -static const _KeyCodeText _native_keycodes[] = { - /* clang-format off */ - {Key::ESCAPE ,0x001B}, - {Key::TAB ,0x0009}, - {Key::BACKTAB ,0x007F}, - {Key::BACKSPACE ,0x0008}, - {Key::ENTER ,0x000D}, - {Key::INSERT ,NSInsertFunctionKey}, - {Key::KEY_DELETE ,0x007F}, - {Key::PAUSE ,NSPauseFunctionKey}, - {Key::PRINT ,NSPrintScreenFunctionKey}, - {Key::SYSREQ ,NSSysReqFunctionKey}, - {Key::CLEAR ,NSClearLineFunctionKey}, - {Key::HOME ,0x2196}, - {Key::END ,0x2198}, - {Key::LEFT ,0x001C}, - {Key::UP ,0x001E}, - {Key::RIGHT ,0x001D}, - {Key::DOWN ,0x001F}, - {Key::PAGEUP ,0x21DE}, - {Key::PAGEDOWN ,0x21DF}, - {Key::NUMLOCK ,NSClearLineFunctionKey}, - {Key::SCROLLLOCK ,NSScrollLockFunctionKey}, - {Key::F1 ,NSF1FunctionKey}, - {Key::F2 ,NSF2FunctionKey}, - {Key::F3 ,NSF3FunctionKey}, - {Key::F4 ,NSF4FunctionKey}, - {Key::F5 ,NSF5FunctionKey}, - {Key::F6 ,NSF6FunctionKey}, - {Key::F7 ,NSF7FunctionKey}, - {Key::F8 ,NSF8FunctionKey}, - {Key::F9 ,NSF9FunctionKey}, - {Key::F10 ,NSF10FunctionKey}, - {Key::F11 ,NSF11FunctionKey}, - {Key::F12 ,NSF12FunctionKey}, - {Key::F13 ,NSF13FunctionKey}, - {Key::F14 ,NSF14FunctionKey}, - {Key::F15 ,NSF15FunctionKey}, - {Key::F16 ,NSF16FunctionKey}, - {Key::F17 ,NSF17FunctionKey}, - {Key::F18 ,NSF18FunctionKey}, - {Key::F19 ,NSF19FunctionKey}, - {Key::F20 ,NSF20FunctionKey}, - {Key::F21 ,NSF21FunctionKey}, - {Key::F22 ,NSF22FunctionKey}, - {Key::F23 ,NSF23FunctionKey}, - {Key::F24 ,NSF24FunctionKey}, - {Key::F25 ,NSF25FunctionKey}, - {Key::F26 ,NSF26FunctionKey}, - {Key::F27 ,NSF27FunctionKey}, - {Key::F28 ,NSF28FunctionKey}, - {Key::F29 ,NSF29FunctionKey}, - {Key::F30 ,NSF30FunctionKey}, - {Key::F31 ,NSF31FunctionKey}, - {Key::F32 ,NSF32FunctionKey}, - {Key::F33 ,NSF33FunctionKey}, - {Key::F34 ,NSF34FunctionKey}, - {Key::F35 ,NSF35FunctionKey}, - {Key::MENU ,NSMenuFunctionKey}, - {Key::HELP ,NSHelpFunctionKey}, - {Key::STOP ,NSStopFunctionKey}, - {Key::LAUNCH0 ,NSUserFunctionKey}, - {Key::SPACE ,0x0020}, - {Key::EXCLAM ,'!'}, - {Key::QUOTEDBL ,'\"'}, - {Key::NUMBERSIGN ,'#'}, - {Key::DOLLAR ,'$'}, - {Key::PERCENT ,'\%'}, - {Key::AMPERSAND ,'&'}, - {Key::APOSTROPHE ,'\''}, - {Key::PARENLEFT ,'('}, - {Key::PARENRIGHT ,')'}, - {Key::ASTERISK ,'*'}, - {Key::PLUS ,'+'}, - {Key::COMMA ,','}, - {Key::MINUS ,'-'}, - {Key::PERIOD ,'.'}, - {Key::SLASH ,'/'}, - {Key::KEY_0 ,'0'}, - {Key::KEY_1 ,'1'}, - {Key::KEY_2 ,'2'}, - {Key::KEY_3 ,'3'}, - {Key::KEY_4 ,'4'}, - {Key::KEY_5 ,'5'}, - {Key::KEY_6 ,'6'}, - {Key::KEY_7 ,'7'}, - {Key::KEY_8 ,'8'}, - {Key::KEY_9 ,'9'}, - {Key::COLON ,':'}, - {Key::SEMICOLON ,';'}, - {Key::LESS ,'<'}, - {Key::EQUAL ,'='}, - {Key::GREATER ,'>'}, - {Key::QUESTION ,'?'}, - {Key::AT ,'@'}, - {Key::A ,'a'}, - {Key::B ,'b'}, - {Key::C ,'c'}, - {Key::D ,'d'}, - {Key::E ,'e'}, - {Key::F ,'f'}, - {Key::G ,'g'}, - {Key::H ,'h'}, - {Key::I ,'i'}, - {Key::J ,'j'}, - {Key::K ,'k'}, - {Key::L ,'l'}, - {Key::M ,'m'}, - {Key::N ,'n'}, - {Key::O ,'o'}, - {Key::P ,'p'}, - {Key::Q ,'q'}, - {Key::R ,'r'}, - {Key::S ,'s'}, - {Key::T ,'t'}, - {Key::U ,'u'}, - {Key::V ,'v'}, - {Key::W ,'w'}, - {Key::X ,'x'}, - {Key::Y ,'y'}, - {Key::Z ,'z'}, - {Key::BRACKETLEFT ,'['}, - {Key::BACKSLASH ,'\\'}, - {Key::BRACKETRIGHT ,']'}, - {Key::ASCIICIRCUM ,'^'}, - {Key::UNDERSCORE ,'_'}, - {Key::QUOTELEFT ,'`'}, - {Key::BRACELEFT ,'{'}, - {Key::BAR ,'|'}, - {Key::BRACERIGHT ,'}'}, - {Key::ASCIITILDE ,'~'}, - {Key::NONE ,0x0000} - /* clang-format on */ -}; - -String KeyMappingOSX::keycode_get_native_string(Key p_keycode) { - const _KeyCodeText *kct = &_native_keycodes[0]; - - while (kct->text) { - if (kct->code == p_keycode) { - return String::chr(kct->text); - } - kct++; - } - return String(); -} - -unsigned int KeyMappingOSX::keycode_get_native_mask(Key p_keycode) { - unsigned int mask = 0; - if ((p_keycode & KeyModifierMask::CTRL) != Key::NONE) { - mask |= NSEventModifierFlagControl; - } - if ((p_keycode & KeyModifierMask::ALT) != Key::NONE) { - mask |= NSEventModifierFlagOption; - } - if ((p_keycode & KeyModifierMask::SHIFT) != Key::NONE) { - mask |= NSEventModifierFlagShift; - } - if ((p_keycode & KeyModifierMask::META) != Key::NONE) { - mask |= NSEventModifierFlagCommand; - } - if ((p_keycode & KeyModifierMask::KPAD) != Key::NONE) { - mask |= NSEventModifierFlagNumericPad; - } - return mask; -} diff --git a/platform/osx/logo.png b/platform/osx/logo.png Binary files differdeleted file mode 100644 index b5a660b165..0000000000 --- a/platform/osx/logo.png +++ /dev/null diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h deleted file mode 100644 index b105be4a06..0000000000 --- a/platform/osx/os_osx.h +++ /dev/null @@ -1,120 +0,0 @@ -/*************************************************************************/ -/* os_osx.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* 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. */ -/*************************************************************************/ - -#ifndef OS_OSX_H -#define OS_OSX_H - -#include "core/input/input.h" -#include "crash_handler_osx.h" -#include "drivers/coreaudio/audio_driver_coreaudio.h" -#include "drivers/coremidi/midi_driver_coremidi.h" -#include "drivers/unix/os_unix.h" -#include "joypad_osx.h" -#include "servers/audio_server.h" - -class OS_OSX : public OS_Unix { - bool force_quit = false; - - JoypadOSX *joypad_osx = nullptr; - -#ifdef COREAUDIO_ENABLED - AudioDriverCoreAudio audio_driver; -#endif -#ifdef COREMIDI_ENABLED - MIDIDriverCoreMidi midi_driver; -#endif - - CrashHandler crash_handler; - - CFRunLoopObserverRef pre_wait_observer; - - MainLoop *main_loop = nullptr; - - List<String> launch_service_args; - - static _FORCE_INLINE_ String get_framework_executable(const String &p_path); - static void pre_wait_observer_cb(CFRunLoopObserverRef p_observer, CFRunLoopActivity p_activiy, void *p_context); - -protected: - virtual void initialize_core() override; - virtual void initialize() override; - virtual void finalize() override; - - virtual void initialize_joypads() override; - - virtual void set_main_loop(MainLoop *p_main_loop) override; - virtual void delete_main_loop() override; - -public: - virtual void set_cmdline_platform_args(const List<String> &p_args); - virtual List<String> get_cmdline_platform_args() const override; - - virtual String get_name() const override; - - virtual void alert(const String &p_alert, const String &p_title = "ALERT!") override; - - virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false, String *r_resolved_path = nullptr) override; - - virtual MainLoop *get_main_loop() const override; - - virtual String get_config_path() const override; - virtual String get_data_path() const override; - virtual String get_cache_path() const override; - virtual String get_bundle_resource_dir() const override; - virtual String get_bundle_icon_path() const override; - virtual String get_godot_dir_name() const override; - - virtual String get_system_dir(SystemDir p_dir, bool p_shared_storage = true) const override; - - virtual Error shell_open(String p_uri) override; - - virtual String get_locale() const override; - - virtual String get_executable_path() const override; - virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override; - virtual Error create_instance(const List<String> &p_arguments, ProcessID *r_child_id = nullptr) override; - - virtual String get_unique_id() const override; - virtual String get_processor_name() const override; - - virtual bool _check_internal_feature_support(const String &p_feature) override; - - virtual void disable_crash_handler() override; - virtual bool is_disable_crash_handler() const override; - - virtual Error move_to_trash(const String &p_path) override; - - void run(); - - OS_OSX(); - ~OS_OSX(); -}; - -#endif diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm deleted file mode 100644 index 5230ed4155..0000000000 --- a/platform/osx/os_osx.mm +++ /dev/null @@ -1,524 +0,0 @@ -/*************************************************************************/ -/* os_osx.mm */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "os_osx.h" - -#include "core/version_generated.gen.h" -#include "main/main.h" - -#include "dir_access_osx.h" -#include "display_server_osx.h" -#include "godot_application.h" -#include "godot_application_delegate.h" -#include "osx_terminal_logger.h" - -#include <dlfcn.h> -#include <libproc.h> -#include <mach-o/dyld.h> -#include <os/log.h> -#include <sys/sysctl.h> - -_FORCE_INLINE_ String OS_OSX::get_framework_executable(const String &p_path) { - // Append framework executable name, or return as is if p_path is not a framework. - Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - if (da->dir_exists(p_path) && da->file_exists(p_path.plus_file(p_path.get_file().get_basename()))) { - return p_path.plus_file(p_path.get_file().get_basename()); - } else { - return p_path; - } -} - -void OS_OSX::pre_wait_observer_cb(CFRunLoopObserverRef p_observer, CFRunLoopActivity p_activiy, void *p_context) { - // Prevent main loop from sleeping and redraw window during resize / modal popups. - - DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton(); - if (get_singleton()->get_main_loop() && ds && (get_singleton()->get_render_thread_mode() != RENDER_SEPARATE_THREAD || !ds->get_is_resizing())) { - Main::force_redraw(); - if (!Main::is_iterating()) { // Avoid cyclic loop. - Main::iteration(); - } - } - - CFRunLoopWakeUp(CFRunLoopGetCurrent()); // Prevent main loop from sleeping. -} - -void OS_OSX::initialize() { - crash_handler.initialize(); - - initialize_core(); -} - -String OS_OSX::get_processor_name() const { - char buffer[256]; - size_t buffer_len = 256; - if (sysctlbyname("machdep.cpu.brand_string", &buffer, &buffer_len, NULL, 0) == 0) { - return String::utf8(buffer, buffer_len); - } - ERR_FAIL_V_MSG("", String("Couldn't get the CPU model name. Returning an empty string.")); -} - -void OS_OSX::initialize_core() { - OS_Unix::initialize_core(); - - DirAccess::make_default<DirAccessOSX>(DirAccess::ACCESS_RESOURCES); - DirAccess::make_default<DirAccessOSX>(DirAccess::ACCESS_USERDATA); - DirAccess::make_default<DirAccessOSX>(DirAccess::ACCESS_FILESYSTEM); -} - -void OS_OSX::finalize() { -#ifdef COREMIDI_ENABLED - midi_driver.close(); -#endif - - delete_main_loop(); - - if (joypad_osx) { - memdelete(joypad_osx); - } -} - -void OS_OSX::initialize_joypads() { - joypad_osx = memnew(JoypadOSX(Input::get_singleton())); -} - -void OS_OSX::set_main_loop(MainLoop *p_main_loop) { - main_loop = p_main_loop; -} - -void OS_OSX::delete_main_loop() { - if (!main_loop) { - return; - } - - memdelete(main_loop); - main_loop = nullptr; -} - -void OS_OSX::set_cmdline_platform_args(const List<String> &p_args) { - launch_service_args = p_args; -} - -List<String> OS_OSX::get_cmdline_platform_args() const { - return launch_service_args; -} - -String OS_OSX::get_name() const { - return "macOS"; -} - -void OS_OSX::alert(const String &p_alert, const String &p_title) { - NSAlert *window = [[NSAlert alloc] init]; - NSString *ns_title = [NSString stringWithUTF8String:p_title.utf8().get_data()]; - NSString *ns_alert = [NSString stringWithUTF8String:p_alert.utf8().get_data()]; - - [window addButtonWithTitle:@"OK"]; - [window setMessageText:ns_title]; - [window setInformativeText:ns_alert]; - [window setAlertStyle:NSAlertStyleWarning]; - - id key_window = [[NSApplication sharedApplication] keyWindow]; - [window runModal]; - if (key_window) { - [key_window makeKeyAndOrderFront:nil]; - } -} - -Error OS_OSX::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path, String *r_resolved_path) { - 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().plus_file(p_path.get_file())); - } - - if (!FileAccess::exists(path)) { - // Load .dylib or framework from a standard macOS location. - path = get_framework_executable(get_executable_path().get_base_dir().plus_file("../Frameworks").plus_file(p_path.get_file())); - } - - p_library_handle = dlopen(path.utf8().get_data(), RTLD_NOW); - ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, "Can't open dynamic library: " + p_path + ", error: " + dlerror() + "."); - - if (r_resolved_path != nullptr) { - *r_resolved_path = path; - } - - return OK; -} - -MainLoop *OS_OSX::get_main_loop() const { - return main_loop; -} - -String OS_OSX::get_config_path() const { - // The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on macOS as well. - if (has_environment("XDG_CONFIG_HOME")) { - if (get_environment("XDG_CONFIG_HOME").is_absolute_path()) { - return get_environment("XDG_CONFIG_HOME"); - } else { - WARN_PRINT_ONCE("`XDG_CONFIG_HOME` is a relative path. Ignoring its value and falling back to `$HOME/Library/Application Support` or `.` per the XDG Base Directory specification."); - } - } - if (has_environment("HOME")) { - return get_environment("HOME").plus_file("Library/Application Support"); - } - return "."; -} - -String OS_OSX::get_data_path() const { - // The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on macOS as well. - if (has_environment("XDG_DATA_HOME")) { - if (get_environment("XDG_DATA_HOME").is_absolute_path()) { - return get_environment("XDG_DATA_HOME"); - } else { - WARN_PRINT_ONCE("`XDG_DATA_HOME` is a relative path. Ignoring its value and falling back to `get_config_path()` per the XDG Base Directory specification."); - } - } - return get_config_path(); -} - -String OS_OSX::get_cache_path() const { - // The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on macOS as well. - if (has_environment("XDG_CACHE_HOME")) { - if (get_environment("XDG_CACHE_HOME").is_absolute_path()) { - return get_environment("XDG_CACHE_HOME"); - } else { - WARN_PRINT_ONCE("`XDG_CACHE_HOME` is a relative path. Ignoring its value and falling back to `$HOME/Library/Caches` or `get_config_path()` per the XDG Base Directory specification."); - } - } - if (has_environment("HOME")) { - return get_environment("HOME").plus_file("Library/Caches"); - } - return get_config_path(); -} - -String OS_OSX::get_bundle_resource_dir() const { - String ret; - - NSBundle *main = [NSBundle mainBundle]; - if (main) { - NSString *resource_path = [main resourcePath]; - ret.parse_utf8([resource_path UTF8String]); - } - return ret; -} - -String OS_OSX::get_bundle_icon_path() const { - String ret; - - NSBundle *main = [NSBundle mainBundle]; - if (main) { - NSString *icon_path = [[main infoDictionary] objectForKey:@"CFBundleIconFile"]; - if (icon_path) { - ret.parse_utf8([icon_path UTF8String]); - } - } - return ret; -} - -// Get properly capitalized engine name for system paths -String OS_OSX::get_godot_dir_name() const { - return String(VERSION_SHORT_NAME).capitalize(); -} - -String OS_OSX::get_system_dir(SystemDir p_dir, bool p_shared_storage) const { - NSSearchPathDirectory id; - bool found = true; - - switch (p_dir) { - case SYSTEM_DIR_DESKTOP: { - id = NSDesktopDirectory; - } break; - case SYSTEM_DIR_DOCUMENTS: { - id = NSDocumentDirectory; - } break; - case SYSTEM_DIR_DOWNLOADS: { - id = NSDownloadsDirectory; - } break; - case SYSTEM_DIR_MOVIES: { - id = NSMoviesDirectory; - } break; - case SYSTEM_DIR_MUSIC: { - id = NSMusicDirectory; - } break; - case SYSTEM_DIR_PICTURES: { - id = NSPicturesDirectory; - } break; - default: { - found = false; - } - } - - String ret; - if (found) { - NSArray *paths = NSSearchPathForDirectoriesInDomains(id, NSUserDomainMask, YES); - if (paths && [paths count] >= 1) { - ret.parse_utf8([[paths firstObject] UTF8String]); - } - } - - return ret; -} - -Error OS_OSX::shell_open(String p_uri) { - NSString *string = [NSString stringWithUTF8String:p_uri.utf8().get_data()]; - NSURL *uri = [[NSURL alloc] initWithString:string]; - // Escape special characters in filenames - if (!uri || !uri.scheme || [uri.scheme isEqual:@"file"]) { - uri = [[NSURL alloc] initWithString:[string stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]]]; - } - [[NSWorkspace sharedWorkspace] openURL:uri]; - return OK; -} - -String OS_OSX::get_locale() const { - NSString *locale_code = [[NSLocale preferredLanguages] objectAtIndex:0]; - return String([locale_code UTF8String]).replace("-", "_"); -} - -String OS_OSX::get_executable_path() const { - char pathbuf[PROC_PIDPATHINFO_MAXSIZE]; - int pid = getpid(); - pid_t ret = proc_pidpath(pid, pathbuf, sizeof(pathbuf)); - if (ret <= 0) { - return OS::get_executable_path(); - } else { - String path; - path.parse_utf8(pathbuf); - - return path; - } -} - -Error OS_OSX::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id, bool p_open_console) { - // Use NSWorkspace if path is an .app bundle. - NSURL *url = [NSURL fileURLWithPath:@(p_path.utf8().get_data())]; - NSBundle *bundle = [NSBundle bundleWithURL:url]; - if (bundle) { - NSMutableArray *arguments = [[NSMutableArray alloc] init]; - for (const String &arg : p_arguments) { - [arguments addObject:[NSString stringWithUTF8String:arg.utf8().get_data()]]; - } - if (@available(macOS 10.15, *)) { - NSWorkspaceOpenConfiguration *configuration = [[NSWorkspaceOpenConfiguration alloc] init]; - [configuration setArguments:arguments]; - [configuration setCreatesNewApplicationInstance:YES]; - __block dispatch_semaphore_t lock = dispatch_semaphore_create(0); - __block Error err = ERR_TIMEOUT; - __block pid_t pid = 0; - - [[NSWorkspace sharedWorkspace] openApplicationAtURL:url - configuration:configuration - completionHandler:^(NSRunningApplication *app, NSError *error) { - if (error) { - err = ERR_CANT_FORK; - NSLog(@"Failed to execute: %@", error.localizedDescription); - } else { - pid = [app processIdentifier]; - err = OK; - } - dispatch_semaphore_signal(lock); - }]; - dispatch_semaphore_wait(lock, dispatch_time(DISPATCH_TIME_NOW, 20000000000)); // 20 sec timeout, wait for app to launch. - - if (err == OK) { - if (r_child_id) { - *r_child_id = (ProcessID)pid; - } - } - - return err; - } else { - Error err = ERR_TIMEOUT; - NSError *error = nullptr; - NSRunningApplication *app = [[NSWorkspace sharedWorkspace] launchApplicationAtURL:url options:NSWorkspaceLaunchNewInstance configuration:[NSDictionary dictionaryWithObject:arguments forKey:NSWorkspaceLaunchConfigurationArguments] error:&error]; - if (error) { - err = ERR_CANT_FORK; - NSLog(@"Failed to execute: %@", error.localizedDescription); - } else { - if (r_child_id) { - *r_child_id = (ProcessID)[app processIdentifier]; - } - err = OK; - } - return err; - } - } else { - return OS_Unix::create_process(p_path, p_arguments, r_child_id, p_open_console); - } -} - -Error OS_OSX::create_instance(const List<String> &p_arguments, ProcessID *r_child_id) { - // If executable is bundled, always execute editor instances as an app bundle to ensure app window is registered and activated correctly. - NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"]; - if (nsappname != nil) { - String path; - path.parse_utf8([[[NSBundle mainBundle] bundlePath] UTF8String]); - return create_process(path, p_arguments, r_child_id, false); - } else { - return create_process(get_executable_path(), p_arguments, r_child_id, false); - } -} - -String OS_OSX::get_unique_id() const { - static String serial_number; - - if (serial_number.is_empty()) { - io_service_t platform_expert = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice")); - CFStringRef serial_number_cf_string = nullptr; - if (platform_expert) { - serial_number_cf_string = (CFStringRef)IORegistryEntryCreateCFProperty(platform_expert, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0); - IOObjectRelease(platform_expert); - } - - NSString *serial_number_ns_string = nil; - if (serial_number_cf_string) { - serial_number_ns_string = [NSString stringWithString:(__bridge NSString *)serial_number_cf_string]; - CFRelease(serial_number_cf_string); - } - - if (serial_number_ns_string) { - serial_number.parse_utf8([serial_number_ns_string UTF8String]); - } - } - - return serial_number; -} - -bool OS_OSX::_check_internal_feature_support(const String &p_feature) { - return p_feature == "pc"; -} - -void OS_OSX::disable_crash_handler() { - crash_handler.disable(); -} - -bool OS_OSX::is_disable_crash_handler() const { - return crash_handler.is_disabled(); -} - -Error OS_OSX::move_to_trash(const String &p_path) { - NSFileManager *fm = [NSFileManager defaultManager]; - NSURL *url = [NSURL fileURLWithPath:@(p_path.utf8().get_data())]; - NSError *err; - - if (![fm trashItemAtURL:url resultingItemURL:nil error:&err]) { - ERR_PRINT("trashItemAtURL error: " + String::utf8(err.localizedDescription.UTF8String)); - return FAILED; - } - - return OK; -} - -void OS_OSX::run() { - force_quit = false; - - if (!main_loop) { - return; - } - - main_loop->initialize(); - - bool quit = false; - while (!force_quit && !quit) { - @try { - if (DisplayServer::get_singleton()) { - DisplayServer::get_singleton()->process_events(); // Get rid of pending events. - } - joypad_osx->process_joypads(); - - if (Main::iteration()) { - quit = true; - } - } @catch (NSException *exception) { - ERR_PRINT("NSException: " + String::utf8([exception reason].UTF8String)); - } - } - - main_loop->finalize(); -} - -OS_OSX::OS_OSX() { - main_loop = nullptr; - force_quit = false; - - Vector<Logger *> loggers; - loggers.push_back(memnew(OSXTerminalLogger)); - _set_logger(memnew(CompositeLogger(loggers))); - -#ifdef COREAUDIO_ENABLED - AudioDriverManager::add_driver(&audio_driver); -#endif - - DisplayServerOSX::register_osx_driver(); - - // Implicitly create shared NSApplication instance. - [GodotApplication sharedApplication]; - - // In case we are unbundled, make us a proper UI application. - [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; - - // Menu bar setup must go between sharedApplication above and - // finishLaunching below, in order to properly emulate the behavior - // of NSApplicationMain. - - NSMenu *main_menu = [[NSMenu alloc] initWithTitle:@""]; - [NSApp setMainMenu:main_menu]; - [NSApp finishLaunching]; - - id delegate = [[GodotApplicationDelegate alloc] init]; - ERR_FAIL_COND(!delegate); - [NSApp setDelegate:delegate]; - - pre_wait_observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopBeforeWaiting, true, 0, &pre_wait_observer_cb, nullptr); - CFRunLoopAddObserver(CFRunLoopGetCurrent(), pre_wait_observer, kCFRunLoopCommonModes); - - // Process application:openFile: event. - while (true) { - NSEvent *event = [NSApp - nextEventMatchingMask:NSEventMaskAny - untilDate:[NSDate distantPast] - inMode:NSDefaultRunLoopMode - dequeue:YES]; - - if (event == nil) { - break; - } - - [NSApp sendEvent:event]; - } - - [NSApp activateIgnoringOtherApps:YES]; -} - -OS_OSX::~OS_OSX() { - CFRunLoopRemoveObserver(CFRunLoopGetCurrent(), pre_wait_observer, kCFRunLoopCommonModes); - CFRelease(pre_wait_observer); -} diff --git a/platform/osx/osx_terminal_logger.h b/platform/osx/osx_terminal_logger.h deleted file mode 100644 index 8413509c4b..0000000000 --- a/platform/osx/osx_terminal_logger.h +++ /dev/null @@ -1,44 +0,0 @@ -/*************************************************************************/ -/* osx_terminal_logger.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* 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. */ -/*************************************************************************/ - -#ifndef OSX_TERMINAL_LOGGER_H -#define OSX_TERMINAL_LOGGER_H - -#ifdef OSX_ENABLED - -#include "core/io/logger.h" - -class OSXTerminalLogger : 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) override; -}; - -#endif // OSX_ENABLED -#endif // OSX_TERMINAL_LOGGER_H diff --git a/platform/osx/osx_terminal_logger.mm b/platform/osx/osx_terminal_logger.mm deleted file mode 100644 index 48e26f42bf..0000000000 --- a/platform/osx/osx_terminal_logger.mm +++ /dev/null @@ -1,82 +0,0 @@ -/*************************************************************************/ -/* osx_terminal_logger.mm */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "osx_terminal_logger.h" - -#ifdef OSX_ENABLED - -#include <os/log.h> - -void OSXTerminalLogger::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) { - if (!should_log(true)) { - return; - } - - const char *err_details; - if (p_rationale && p_rationale[0]) { - err_details = p_rationale; - } else { - err_details = p_code; - } - - switch (p_type) { - case ERR_WARNING: - os_log_info(OS_LOG_DEFAULT, - "WARNING: %{public}s\nat: %{public}s (%{public}s:%i)", - err_details, p_function, p_file, p_line); - logf_error("\E[1;33mWARNING:\E[0;93m %s\n", err_details); - logf_error("\E[0;90m at: %s (%s:%i)\E[0m\n", p_function, p_file, p_line); - break; - case ERR_SCRIPT: - os_log_error(OS_LOG_DEFAULT, - "SCRIPT ERROR: %{public}s\nat: %{public}s (%{public}s:%i)", - err_details, p_function, p_file, p_line); - logf_error("\E[1;35mSCRIPT ERROR:\E[0;95m %s\n", err_details); - logf_error("\E[0;90m at: %s (%s:%i)\E[0m\n", p_function, p_file, p_line); - break; - case ERR_SHADER: - os_log_error(OS_LOG_DEFAULT, - "SHADER ERROR: %{public}s\nat: %{public}s (%{public}s:%i)", - err_details, p_function, p_file, p_line); - logf_error("\E[1;36mSHADER ERROR:\E[0;96m %s\n", err_details); - logf_error("\E[0;90m at: %s (%s:%i)\E[0m\n", p_function, p_file, p_line); - break; - case ERR_ERROR: - default: - os_log_error(OS_LOG_DEFAULT, - "ERROR: %{public}s\nat: %{public}s (%{public}s:%i)", - err_details, p_function, p_file, p_line); - logf_error("\E[1;31mERROR:\E[0;91m %s\n", err_details); - logf_error("\E[0;90m at: %s (%s:%i)\E[0m\n", p_function, p_file, p_line); - break; - } -} - -#endif // OSX_ENABLED diff --git a/platform/osx/platform_config.h b/platform/osx/platform_config.h deleted file mode 100644 index e114606b82..0000000000 --- a/platform/osx/platform_config.h +++ /dev/null @@ -1,34 +0,0 @@ -/*************************************************************************/ -/* platform_config.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include <alloca.h> - -#define OPENGL_INCLUDE_H "thirdparty/glad/glad/glad.h" -#define PTHREAD_RENAME_SELF diff --git a/platform/osx/platform_osx_builders.py b/platform/osx/platform_osx_builders.py deleted file mode 100644 index 953ed479db..0000000000 --- a/platform/osx/platform_osx_builders.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Functions used to generate source files during build time - -All such functions are invoked in a subprocess on Windows to prevent build flakiness. - -""" -import os -from platform_methods import subprocess_main - - -def make_debug_osx(target, source, env): - if env["macports_clang"] != "no": - mpprefix = os.environ.get("MACPORTS_PREFIX", "/opt/local") - mpclangver = env["macports_clang"] - os.system(mpprefix + "/libexec/llvm-" + mpclangver + "/bin/llvm-dsymutil {0} -o {0}.dSYM".format(target[0])) - else: - os.system("dsymutil {0} -o {0}.dSYM".format(target[0])) - os.system("strip -u -r {0}".format(target[0])) - - -if __name__ == "__main__": - subprocess_main(globals()) diff --git a/platform/osx/tts_osx.h b/platform/osx/tts_osx.h deleted file mode 100644 index 449418e48f..0000000000 --- a/platform/osx/tts_osx.h +++ /dev/null @@ -1,71 +0,0 @@ -/*************************************************************************/ -/* tts_osx.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* 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. */ -/*************************************************************************/ - -#ifndef TTS_OSX_H -#define TTS_OSX_H - -#include "core/string/ustring.h" -#include "core/templates/list.h" -#include "core/templates/rb_map.h" -#include "core/variant/array.h" -#include "servers/display_server.h" - -#import <AppKit/AppKit.h> - -#if __has_include(<AVFAudio/AVSpeechSynthesis.h>) -#import <AVFAudio/AVSpeechSynthesis.h> -#else -#import <AVFoundation/AVFoundation.h> -#endif - -@interface TTS_OSX : NSObject <AVSpeechSynthesizerDelegate> { - // AVSpeechSynthesizer - bool speaking; - HashMap<id, int> ids; - - // NSSpeechSynthesizer - bool paused; - bool have_utterance; - int last_utterance; - - id synth; // NSSpeechSynthesizer or AVSpeechSynthesizer - List<DisplayServer::TTSUtterance> queue; -} - -- (void)pauseSpeaking; -- (void)resumeSpeaking; -- (void)stopSpeaking; -- (bool)isSpeaking; -- (bool)isPaused; -- (void)speak:(const String &)text voice:(const String &)voice volume:(int)volume pitch:(float)pitch rate:(float)rate utterance_id:(int)utterance_id interrupt:(bool)interrupt; -- (Array)getVoices; -@end - -#endif // TTS_OSX_H diff --git a/platform/osx/tts_osx.mm b/platform/osx/tts_osx.mm deleted file mode 100644 index e6a5236cd9..0000000000 --- a/platform/osx/tts_osx.mm +++ /dev/null @@ -1,266 +0,0 @@ -/*************************************************************************/ -/* tts_osx.mm */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "tts_osx.h" - -@implementation TTS_OSX - -- (id)init { - self = [super init]; - self->speaking = false; - self->have_utterance = false; - self->last_utterance = -1; - self->paused = false; - if (@available(macOS 10.14, *)) { - self->synth = [[AVSpeechSynthesizer alloc] init]; - [self->synth setDelegate:self]; - print_verbose("Text-to-Speech: AVSpeechSynthesizer initialized."); - } else { - self->synth = [[NSSpeechSynthesizer alloc] init]; - [self->synth setDelegate:self]; - print_verbose("Text-to-Speech: NSSpeechSynthesizer initialized."); - } - return self; -} - -// AVSpeechSynthesizer callback (macOS 10.14+) - -- (void)speechSynthesizer:(AVSpeechSynthesizer *)av_synth willSpeakRangeOfSpeechString:(NSRange)characterRange utterance:(AVSpeechUtterance *)utterance API_AVAILABLE(macosx(10.14)) { - NSString *string = [utterance speechString]; - - // Convert from UTF-16 to UTF-32 position. - int pos = 0; - for (NSUInteger i = 0; i < MIN(characterRange.location, string.length); i++) { - unichar c = [string characterAtIndex:i]; - if ((c & 0xfffffc00) == 0xd800) { - i++; - } - pos++; - } - - DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServer::TTS_UTTERANCE_BOUNDARY, ids[utterance], pos); -} - -// AVSpeechSynthesizer callback (macOS 10.14+) - -- (void)speechSynthesizer:(AVSpeechSynthesizer *)av_synth didCancelSpeechUtterance:(AVSpeechUtterance *)utterance API_AVAILABLE(macosx(10.14)) { - DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServer::TTS_UTTERANCE_CANCELED, ids[utterance]); - ids.erase(utterance); - speaking = false; - [self update]; -} - -// AVSpeechSynthesizer callback (macOS 10.14+) - -- (void)speechSynthesizer:(AVSpeechSynthesizer *)av_synth didFinishSpeechUtterance:(AVSpeechUtterance *)utterance API_AVAILABLE(macosx(10.14)) { - DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServer::TTS_UTTERANCE_ENDED, ids[utterance]); - ids.erase(utterance); - speaking = false; - [self update]; -} - -// NSSpeechSynthesizer callback (macOS 10.4+) - -- (void)speechSynthesizer:(NSSpeechSynthesizer *)ns_synth willSpeakWord:(NSRange)characterRange ofString:(NSString *)string { - if (!paused && have_utterance) { - // Convert from UTF-16 to UTF-32 position. - int pos = 0; - for (NSUInteger i = 0; i < MIN(characterRange.location, string.length); i++) { - unichar c = [string characterAtIndex:i]; - if ((c & 0xfffffc00) == 0xd800) { - i++; - } - pos++; - } - - DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServer::TTS_UTTERANCE_BOUNDARY, last_utterance, pos); - } -} - -- (void)speechSynthesizer:(NSSpeechSynthesizer *)ns_synth didFinishSpeaking:(BOOL)success { - if (!paused && have_utterance) { - if (success) { - DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServer::TTS_UTTERANCE_ENDED, last_utterance); - } else { - DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServer::TTS_UTTERANCE_CANCELED, last_utterance); - } - have_utterance = false; - } - speaking = false; - [self update]; -} - -- (void)update { - if (!speaking && queue.size() > 0) { - DisplayServer::TTSUtterance &message = queue.front()->get(); - - if (@available(macOS 10.14, *)) { - AVSpeechSynthesizer *av_synth = synth; - AVSpeechUtterance *new_utterance = [[AVSpeechUtterance alloc] initWithString:[NSString stringWithUTF8String:message.text.utf8().get_data()]]; - [new_utterance setVoice:[AVSpeechSynthesisVoice voiceWithIdentifier:[NSString stringWithUTF8String:message.voice.utf8().get_data()]]]; - if (message.rate > 1.f) { - [new_utterance setRate:Math::range_lerp(message.rate, 1.f, 10.f, AVSpeechUtteranceDefaultSpeechRate, AVSpeechUtteranceMaximumSpeechRate)]; - } else if (message.rate < 1.f) { - [new_utterance setRate:Math::range_lerp(message.rate, 0.1f, 1.f, AVSpeechUtteranceMinimumSpeechRate, AVSpeechUtteranceDefaultSpeechRate)]; - } - [new_utterance setPitchMultiplier:message.pitch]; - [new_utterance setVolume:(Math::range_lerp(message.volume, 0.f, 100.f, 0.f, 1.f))]; - - ids[new_utterance] = message.id; - [av_synth speakUtterance:new_utterance]; - } else { - NSSpeechSynthesizer *ns_synth = synth; - [ns_synth setObject:nil forProperty:NSSpeechResetProperty error:nil]; - [ns_synth setVoice:[NSString stringWithUTF8String:message.voice.utf8().get_data()]]; - int base_pitch = [[ns_synth objectForProperty:NSSpeechPitchBaseProperty error:nil] intValue]; - [ns_synth setObject:[NSNumber numberWithInt:(base_pitch * (message.pitch / 2.f + 0.5f))] forProperty:NSSpeechPitchBaseProperty error:nullptr]; - [ns_synth setVolume:(Math::range_lerp(message.volume, 0.f, 100.f, 0.f, 1.f))]; - [ns_synth setRate:(message.rate * 200)]; - - last_utterance = message.id; - have_utterance = true; - [ns_synth startSpeakingString:[NSString stringWithUTF8String:message.text.utf8().get_data()]]; - } - queue.pop_front(); - - DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServer::TTS_UTTERANCE_STARTED, message.id); - speaking = true; - } -} - -- (void)pauseSpeaking { - if (@available(macOS 10.14, *)) { - AVSpeechSynthesizer *av_synth = synth; - [av_synth pauseSpeakingAtBoundary:AVSpeechBoundaryImmediate]; - } else { - NSSpeechSynthesizer *ns_synth = synth; - [ns_synth pauseSpeakingAtBoundary:NSSpeechImmediateBoundary]; - } - paused = true; -} - -- (void)resumeSpeaking { - if (@available(macOS 10.14, *)) { - AVSpeechSynthesizer *av_synth = synth; - [av_synth continueSpeaking]; - } else { - NSSpeechSynthesizer *ns_synth = synth; - [ns_synth continueSpeaking]; - } - paused = false; -} - -- (void)stopSpeaking { - for (DisplayServer::TTSUtterance &message : queue) { - DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServer::TTS_UTTERANCE_CANCELED, message.id); - } - queue.clear(); - if (@available(macOS 10.14, *)) { - AVSpeechSynthesizer *av_synth = synth; - [av_synth stopSpeakingAtBoundary:AVSpeechBoundaryImmediate]; - } else { - NSSpeechSynthesizer *ns_synth = synth; - if (have_utterance) { - DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServer::TTS_UTTERANCE_CANCELED, last_utterance); - } - [ns_synth stopSpeaking]; - } - have_utterance = false; - speaking = false; - paused = false; -} - -- (bool)isSpeaking { - return speaking || (queue.size() > 0); -} - -- (bool)isPaused { - if (@available(macOS 10.14, *)) { - AVSpeechSynthesizer *av_synth = synth; - return [av_synth isPaused]; - } else { - return paused; - } -} - -- (void)speak:(const String &)text voice:(const String &)voice volume:(int)volume pitch:(float)pitch rate:(float)rate utterance_id:(int)utterance_id interrupt:(bool)interrupt { - if (interrupt) { - [self stopSpeaking]; - } - - if (text.is_empty()) { - DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServer::TTS_UTTERANCE_CANCELED, utterance_id); - return; - } - - DisplayServer::TTSUtterance message; - message.text = text; - message.voice = voice; - message.volume = CLAMP(volume, 0, 100); - message.pitch = CLAMP(pitch, 0.f, 2.f); - message.rate = CLAMP(rate, 0.1f, 10.f); - message.id = utterance_id; - queue.push_back(message); - - if ([self isPaused]) { - [self resumeSpeaking]; - } else { - [self update]; - } -} - -- (Array)getVoices { - Array list; - if (@available(macOS 10.14, *)) { - for (AVSpeechSynthesisVoice *voice in [AVSpeechSynthesisVoice speechVoices]) { - NSString *voiceIdentifierString = [voice identifier]; - NSString *voiceLocaleIdentifier = [voice language]; - NSString *voiceName = [voice name]; - Dictionary voice_d; - voice_d["name"] = String::utf8([voiceName UTF8String]); - voice_d["id"] = String::utf8([voiceIdentifierString UTF8String]); - voice_d["language"] = String::utf8([voiceLocaleIdentifier UTF8String]); - list.push_back(voice_d); - } - } else { - for (NSString *voiceIdentifierString in [NSSpeechSynthesizer availableVoices]) { - NSString *voiceLocaleIdentifier = [[NSSpeechSynthesizer attributesForVoice:voiceIdentifierString] objectForKey:NSVoiceLocaleIdentifier]; - NSString *voiceName = [[NSSpeechSynthesizer attributesForVoice:voiceIdentifierString] objectForKey:NSVoiceName]; - Dictionary voice_d; - voice_d["name"] = String([voiceName UTF8String]); - voice_d["id"] = String([voiceIdentifierString UTF8String]); - voice_d["language"] = String([voiceLocaleIdentifier UTF8String]); - list.push_back(voice_d); - } - } - return list; -} - -@end diff --git a/platform/osx/vulkan_context_osx.h b/platform/osx/vulkan_context_osx.h deleted file mode 100644 index ade0f4a4c9..0000000000 --- a/platform/osx/vulkan_context_osx.h +++ /dev/null @@ -1,47 +0,0 @@ -/*************************************************************************/ -/* vulkan_context_osx.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* 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. */ -/*************************************************************************/ - -#ifndef VULKAN_DEVICE_OSX_H -#define VULKAN_DEVICE_OSX_H - -#include "drivers/vulkan/vulkan_context.h" -#import <AppKit/AppKit.h> - -class VulkanContextOSX : public VulkanContext { - virtual const char *_get_platform_surface_extension() const; - -public: - Error window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, id p_window, int p_width, int p_height); - - VulkanContextOSX(); - ~VulkanContextOSX(); -}; - -#endif // VULKAN_DEVICE_OSX_H diff --git a/platform/osx/vulkan_context_osx.mm b/platform/osx/vulkan_context_osx.mm deleted file mode 100644 index bdabc24c28..0000000000 --- a/platform/osx/vulkan_context_osx.mm +++ /dev/null @@ -1,59 +0,0 @@ -/*************************************************************************/ -/* vulkan_context_osx.mm */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "vulkan_context_osx.h" -#ifdef USE_VOLK -#include <volk.h> -#else -#include <vulkan/vulkan.h> -#endif - -const char *VulkanContextOSX::_get_platform_surface_extension() const { - return VK_MVK_MACOS_SURFACE_EXTENSION_NAME; -} - -Error VulkanContextOSX::window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, id p_window, int p_width, int p_height) { - VkMacOSSurfaceCreateInfoMVK createInfo; - createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK; - createInfo.pNext = nullptr; - createInfo.flags = 0; - createInfo.pView = (__bridge const void *)p_window; - - VkSurfaceKHR surface; - VkResult err = vkCreateMacOSSurfaceMVK(get_instance(), &createInfo, nullptr, &surface); - ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - return _window_create(p_window_id, p_vsync_mode, surface, p_width, p_height); -} - -VulkanContextOSX::VulkanContextOSX() { -} - -VulkanContextOSX::~VulkanContextOSX() { -} |