initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
This commit is contained in:
21
platform/macos/README.md
Normal file
21
platform/macos/README.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# macOS platform port
|
||||
|
||||
This folder contains the C++, Objective-C and Objective-C++ code for the macOS
|
||||
platform port.
|
||||
|
||||
This platform uses shared Apple code ([`drivers/apple`](/drivers/apple)).
|
||||
|
||||
See also [`misc/dist/macos`](/misc/dist/macos) folder for additional files used
|
||||
by this platform. [`misc/dist/macos_tools.app`](/misc/dist/macos_tools.app) is
|
||||
an `.app` bundle template used for packaging the macOS editor, while
|
||||
[`misc/dist/macos_template.app`](/misc/dist/macos_template.app) is used for
|
||||
packaging macOS export templates.
|
||||
|
||||
## Documentation
|
||||
|
||||
- [Compiling for macOS](https://docs.godotengine.org/en/latest/engine_details/development/compiling/compiling_for_macos.html)
|
||||
- Instructions on building this platform port from source.
|
||||
- [Exporting for macOS](https://docs.godotengine.org/en/latest/tutorials/export/exporting_for_macos.html)
|
||||
- Instructions on using the compiled export templates to export a project.
|
||||
- [Running Godot apps on macOS](https://docs.godotengine.org/en/latest/tutorials/export/running_on_macos.html)
|
||||
- Instructions on running Godot projects on macOS.
|
48
platform/macos/SCsub
Normal file
48
platform/macos/SCsub
Normal file
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
import platform_macos_builders
|
||||
|
||||
Import("env")
|
||||
|
||||
files = [
|
||||
"os_macos.mm",
|
||||
"godot_application.mm",
|
||||
"godot_application_delegate.mm",
|
||||
"crash_handler_macos.mm",
|
||||
"display_server_macos_base.mm",
|
||||
"display_server_embedded.mm",
|
||||
"display_server_macos.mm",
|
||||
"embedded_debugger.mm",
|
||||
"embedded_gl_manager.mm",
|
||||
"godot_button_view.mm",
|
||||
"godot_content_view.mm",
|
||||
"godot_status_item.mm",
|
||||
"godot_window_delegate.mm",
|
||||
"godot_window.mm",
|
||||
"key_mapping_macos.mm",
|
||||
"godot_main_macos.mm",
|
||||
"godot_menu_delegate.mm",
|
||||
"godot_menu_item.mm",
|
||||
"godot_open_save_delegate.mm",
|
||||
"native_menu_macos.mm",
|
||||
"dir_access_macos.mm",
|
||||
"tts_macos.mm",
|
||||
"rendering_context_driver_vulkan_macos.mm",
|
||||
"gl_manager_macos_angle.mm",
|
||||
"gl_manager_macos_legacy.mm",
|
||||
]
|
||||
|
||||
if env.editor_build:
|
||||
files += [
|
||||
"editor/embedded_game_view_plugin.mm",
|
||||
"editor/embedded_process_macos.mm",
|
||||
]
|
||||
|
||||
prog = env.add_program("#bin/godot", files)
|
||||
|
||||
if env["debug_symbols"] and env["separate_debug_symbols"]:
|
||||
env.AddPostAction(prog, env.Run(platform_macos_builders.make_debug_macos))
|
||||
|
||||
if env["generate_bundle"]:
|
||||
env.AlwaysBuild(env.CommandNoCache("generate_bundle", prog, env.Run(platform_macos_builders.generate_bundle)))
|
44
platform/macos/crash_handler_macos.h
Normal file
44
platform/macos/crash_handler_macos.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/**************************************************************************/
|
||||
/* crash_handler_macos.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
class CrashHandler {
|
||||
bool disabled;
|
||||
|
||||
public:
|
||||
void initialize();
|
||||
|
||||
void disable();
|
||||
bool is_disabled() const { return disabled; }
|
||||
|
||||
CrashHandler();
|
||||
~CrashHandler();
|
||||
};
|
222
platform/macos/crash_handler_macos.mm
Normal file
222
platform/macos/crash_handler_macos.mm
Normal file
@@ -0,0 +1,222 @@
|
||||
/**************************************************************************/
|
||||
/* crash_handler_macos.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "crash_handler_macos.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/object/script_language.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/string/print_string.h"
|
||||
#include "core/version.h"
|
||||
#include "main/main.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 <csignal>
|
||||
#include <cstdlib>
|
||||
|
||||
#import <mach-o/dyld.h>
|
||||
#import <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) {
|
||||
signal(SIGSEGV, SIG_DFL);
|
||||
signal(SIGFPE, SIG_DFL);
|
||||
signal(SIGILL, SIG_DFL);
|
||||
signal(SIGTRAP, SIG_DFL);
|
||||
|
||||
if (OS::get_singleton() == nullptr) {
|
||||
abort();
|
||||
}
|
||||
|
||||
if (OS::get_singleton()->is_crash_handler_silent()) {
|
||||
std::_Exit(0);
|
||||
}
|
||||
|
||||
void *bt_buffer[256];
|
||||
size_t size = backtrace(bt_buffer, 256);
|
||||
String _execpath = OS::get_singleton()->get_executable_path();
|
||||
|
||||
String msg;
|
||||
if (ProjectSettings::get_singleton()) {
|
||||
msg = GLOBAL_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(GODOT_VERSION_HASH).is_empty()) {
|
||||
print_error(vformat("Engine version: %s", GODOT_VERSION_FULL_NAME));
|
||||
} else {
|
||||
print_error(vformat("Engine version: %s (%s)", GODOT_VERSION_FULL_NAME, GODOT_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("--fullPath");
|
||||
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 C++ BACKTRACE --");
|
||||
print_error("================================================================");
|
||||
|
||||
for (const Ref<ScriptBacktrace> &backtrace : ScriptServer::capture_script_backtraces(false)) {
|
||||
if (!backtrace->is_empty()) {
|
||||
print_error(backtrace->format());
|
||||
print_error(vformat("-- END OF %s BACKTRACE --", backtrace->get_language_name().to_upper()));
|
||||
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, SIG_DFL);
|
||||
signal(SIGFPE, SIG_DFL);
|
||||
signal(SIGILL, SIG_DFL);
|
||||
signal(SIGTRAP, SIG_DFL);
|
||||
#endif
|
||||
|
||||
disabled = true;
|
||||
}
|
||||
|
||||
void CrashHandler::initialize() {
|
||||
#ifdef CRASH_HANDLER_ENABLED
|
||||
signal(SIGSEGV, handle_crash);
|
||||
signal(SIGFPE, handle_crash);
|
||||
signal(SIGILL, handle_crash);
|
||||
signal(SIGTRAP, handle_crash);
|
||||
#endif
|
||||
}
|
293
platform/macos/detect.py
Normal file
293
platform/macos/detect.py
Normal file
@@ -0,0 +1,293 @@
|
||||
import os
|
||||
import sys
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from methods import detect_darwin_sdk_path, get_compiler_version, is_apple_clang, print_error, print_warning
|
||||
from platform_methods import detect_arch, detect_mvk, validate_arch
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from SCons.Script.SConscript import SConsEnvironment
|
||||
|
||||
# To match other platforms
|
||||
STACK_SIZE = 8388608
|
||||
STACK_SIZE_SANITIZERS = 30 * 1024 * 1024
|
||||
|
||||
|
||||
def get_name():
|
||||
return "macOS"
|
||||
|
||||
|
||||
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"], ignorecase=2),
|
||||
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),
|
||||
("angle_libs", "Path to the ANGLE static libraries", ""),
|
||||
(
|
||||
"bundle_sign_identity",
|
||||
"The 'Full Name', 'Common Name' or SHA-1 hash of the signing identity used to sign editor .app bundle.",
|
||||
"-",
|
||||
),
|
||||
BoolVariable("generate_bundle", "Generate an APP bundle after building iOS/macOS binaries", False),
|
||||
]
|
||||
|
||||
|
||||
def get_doc_classes():
|
||||
return [
|
||||
"EditorExportPlatformMacOS",
|
||||
]
|
||||
|
||||
|
||||
def get_doc_path():
|
||||
return "doc_classes"
|
||||
|
||||
|
||||
def get_flags():
|
||||
return {
|
||||
"arch": detect_arch(),
|
||||
"use_volk": False,
|
||||
"metal": True,
|
||||
"supported": ["metal", "mono"],
|
||||
}
|
||||
|
||||
|
||||
def configure(env: "SConsEnvironment"):
|
||||
# Validate arch.
|
||||
supported_arches = ["x86_64", "arm64"]
|
||||
validate_arch(env["arch"], get_name(), supported_arches)
|
||||
|
||||
## Compiler configuration
|
||||
|
||||
# Save this in environment for use by other modules
|
||||
if "OSXCROSS_ROOT" in os.environ:
|
||||
env["osxcross"] = True
|
||||
|
||||
# CPU architecture.
|
||||
if env["arch"] == "arm64":
|
||||
print("Building for macOS 11.0+.")
|
||||
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"])
|
||||
elif env["arch"] == "x86_64":
|
||||
print("Building for macOS 10.13+.")
|
||||
env.Append(ASFLAGS=["-arch", "x86_64", "-mmacosx-version-min=10.13"])
|
||||
env.Append(CCFLAGS=["-arch", "x86_64", "-mmacosx-version-min=10.13"])
|
||||
env.Append(LINKFLAGS=["-arch", "x86_64", "-mmacosx-version-min=10.13"])
|
||||
|
||||
env.Append(CCFLAGS=["-ffp-contract=off"])
|
||||
env.Append(CCFLAGS=["-fobjc-arc"])
|
||||
|
||||
cc_version = get_compiler_version(env)
|
||||
cc_version_major = cc_version["apple_major"]
|
||||
cc_version_minor = cc_version["apple_minor"]
|
||||
|
||||
# Workaround for Xcode 15 linker bug.
|
||||
if is_apple_clang(env) and cc_version_major == 1500 and cc_version_minor == 0:
|
||||
env.Prepend(LINKFLAGS=["-ld_classic"])
|
||||
|
||||
if env.dev_build:
|
||||
env.Prepend(LINKFLAGS=["-Xlinker", "-no_deduplicate"])
|
||||
|
||||
ccache_path = os.environ.get("CCACHE", "")
|
||||
if ccache_path != "":
|
||||
ccache_path = ccache_path + " "
|
||||
|
||||
if "osxcross" not 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"] = ccache_path + "clang"
|
||||
env["CXX"] = ccache_path + "clang++"
|
||||
|
||||
detect_darwin_sdk_path("macos", env)
|
||||
env.Append(CCFLAGS=["-isysroot", "$MACOS_SDK_PATH"])
|
||||
env.Append(LINKFLAGS=["-isysroot", "$MACOS_SDK_PATH"])
|
||||
|
||||
else: # osxcross build
|
||||
root = os.environ.get("OSXCROSS_ROOT", "")
|
||||
if env["arch"] == "arm64":
|
||||
basecmd = root + "/target/bin/arm64-apple-" + env["osxcross_sdk"] + "-"
|
||||
else:
|
||||
basecmd = root + "/target/bin/x86_64-apple-" + env["osxcross_sdk"] + "-"
|
||||
|
||||
env["CC"] = ccache_path + basecmd + "cc"
|
||||
env["CXX"] = ccache_path + basecmd + "c++"
|
||||
env["AR"] = basecmd + "ar"
|
||||
env["RANLIB"] = basecmd + "ranlib"
|
||||
env["AS"] = basecmd + "as"
|
||||
|
||||
# LTO
|
||||
|
||||
if env["lto"] == "auto": # LTO benefits for macOS (size, performance) haven't been clearly established yet.
|
||||
env["lto"] = "none"
|
||||
|
||||
if env["lto"] != "none":
|
||||
if env["lto"] == "thin":
|
||||
env.Append(CCFLAGS=["-flto=thin"])
|
||||
env.Append(LINKFLAGS=["-flto=thin"])
|
||||
else:
|
||||
env.Append(CCFLAGS=["-flto"])
|
||||
env.Append(LINKFLAGS=["-flto"])
|
||||
|
||||
# Sanitizers
|
||||
|
||||
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"])
|
||||
|
||||
env.Append(LINKFLAGS=["-Wl,-stack_size," + hex(STACK_SIZE_SANITIZERS)])
|
||||
else:
|
||||
env.Append(LINKFLAGS=["-Wl,-stack_size," + hex(STACK_SIZE)])
|
||||
|
||||
if env["use_coverage"]:
|
||||
env.Append(CCFLAGS=["-ftest-coverage", "-fprofile-arcs"])
|
||||
env.Append(LINKFLAGS=["-ftest-coverage", "-fprofile-arcs"])
|
||||
|
||||
## Dependencies
|
||||
|
||||
if env["accesskit"]:
|
||||
if env["accesskit_sdk_path"] != "":
|
||||
env.Prepend(CPPPATH=[env["accesskit_sdk_path"] + "/include"])
|
||||
if env["arch"] == "arm64" or env["arch"] == "universal":
|
||||
env.Append(LINKFLAGS=["-L" + env["accesskit_sdk_path"] + "/lib/macos/arm64/static/"])
|
||||
if env["arch"] == "x86_64" or env["arch"] == "universal":
|
||||
env.Append(LINKFLAGS=["-L" + env["accesskit_sdk_path"] + "/lib/macos/x86_64/static/"])
|
||||
env.Append(LINKFLAGS=["-laccesskit"])
|
||||
else:
|
||||
env.Append(CPPDEFINES=["ACCESSKIT_DYNAMIC"])
|
||||
env.Append(CPPDEFINES=["ACCESSKIT_ENABLED"])
|
||||
|
||||
if env["builtin_libtheora"] and env["arch"] == "x86_64":
|
||||
env["x86_libtheora_opt_gcc"] = True
|
||||
|
||||
if env["sdl"]:
|
||||
env.Append(CPPDEFINES=["SDL_ENABLED"])
|
||||
env.Append(LINKFLAGS=["-framework", "ForceFeedback"])
|
||||
|
||||
## Flags
|
||||
|
||||
env.Prepend(CPPPATH=["#platform/macos"])
|
||||
env.Append(CPPDEFINES=["MACOS_ENABLED", "UNIX_ENABLED", "COREAUDIO_ENABLED", "COREMIDI_ENABLED"])
|
||||
env.Append(
|
||||
LINKFLAGS=[
|
||||
"-framework",
|
||||
"Cocoa",
|
||||
"-framework",
|
||||
"Carbon",
|
||||
"-framework",
|
||||
"AudioUnit",
|
||||
"-framework",
|
||||
"CoreAudio",
|
||||
"-framework",
|
||||
"CoreMIDI",
|
||||
"-framework",
|
||||
"IOKit",
|
||||
"-framework",
|
||||
"GameController",
|
||||
"-framework",
|
||||
"CoreHaptics",
|
||||
"-framework",
|
||||
"CoreVideo",
|
||||
"-framework",
|
||||
"AVFoundation",
|
||||
"-framework",
|
||||
"CoreMedia",
|
||||
"-framework",
|
||||
"QuartzCore",
|
||||
"-framework",
|
||||
"Security",
|
||||
"-framework",
|
||||
"UniformTypeIdentifiers",
|
||||
"-framework",
|
||||
"IOSurface",
|
||||
]
|
||||
)
|
||||
env.Append(LIBS=["pthread", "z"])
|
||||
|
||||
extra_frameworks = set()
|
||||
|
||||
if env["opengl3"]:
|
||||
env.Append(CPPDEFINES=["GLES3_ENABLED"])
|
||||
if env["angle_libs"] != "":
|
||||
env.AppendUnique(CPPDEFINES=["EGL_STATIC"])
|
||||
env.Append(LINKFLAGS=["-L" + env["angle_libs"]])
|
||||
env.Append(LINKFLAGS=["-lANGLE.macos." + env["arch"]])
|
||||
env.Append(LINKFLAGS=["-lEGL.macos." + env["arch"]])
|
||||
env.Append(LINKFLAGS=["-lGLES.macos." + env["arch"]])
|
||||
env.Prepend(CPPEXTPATH=["#thirdparty/angle/include"])
|
||||
|
||||
env.Append(LINKFLAGS=["-rpath", "@executable_path/../Frameworks", "-rpath", "@executable_path"])
|
||||
|
||||
if env["metal"] and env["arch"] != "arm64":
|
||||
print_warning("Target architecture '{}' does not support the Metal rendering driver".format(env["arch"]))
|
||||
env["metal"] = False
|
||||
|
||||
if env["metal"]:
|
||||
env.AppendUnique(CPPDEFINES=["METAL_ENABLED", "RD_ENABLED"])
|
||||
extra_frameworks.add("Metal")
|
||||
extra_frameworks.add("MetalKit")
|
||||
extra_frameworks.add("MetalFX")
|
||||
env.Prepend(CPPEXTPATH=["#thirdparty/spirv-cross"])
|
||||
|
||||
if env["vulkan"]:
|
||||
env.AppendUnique(CPPDEFINES=["VULKAN_ENABLED", "RD_ENABLED"])
|
||||
extra_frameworks.add("Metal")
|
||||
if not env["use_volk"]:
|
||||
env.Append(LINKFLAGS=["-lMoltenVK"])
|
||||
|
||||
mvk_path = ""
|
||||
arch_variants = ["macos-arm64_x86_64", "macos-" + env["arch"]]
|
||||
for arch in arch_variants:
|
||||
mvk_path = detect_mvk(env, arch)
|
||||
if mvk_path != "":
|
||||
mvk_path = os.path.join(mvk_path, arch)
|
||||
break
|
||||
|
||||
if mvk_path != "":
|
||||
env.Append(LINKFLAGS=["-L" + mvk_path])
|
||||
else:
|
||||
print_error(
|
||||
"MoltenVK SDK installation directory not found, use 'vulkan_sdk_path' SCons parameter to specify SDK path."
|
||||
)
|
||||
sys.exit(255)
|
||||
|
||||
if len(extra_frameworks) > 0:
|
||||
frameworks = [item for key in extra_frameworks for item in ["-framework", key]]
|
||||
env.Append(LINKFLAGS=frameworks)
|
60
platform/macos/dir_access_macos.h
Normal file
60
platform/macos/dir_access_macos.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/**************************************************************************/
|
||||
/* dir_access_macos.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(UNIX_ENABLED)
|
||||
|
||||
#include "core/io/dir_access.h"
|
||||
#include "drivers/unix/dir_access_unix.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
class DirAccessMacOS : public DirAccessUnix {
|
||||
GDSOFTCLASS(DirAccessMacOS, DirAccessUnix);
|
||||
|
||||
protected:
|
||||
virtual String fix_unicode_name(const char *p_name) const override;
|
||||
|
||||
virtual int get_drive_count() override;
|
||||
virtual String get_drive(int p_drive) override;
|
||||
|
||||
virtual bool is_hidden(const String &p_name) override;
|
||||
virtual bool is_case_sensitive(const String &p_path) const override;
|
||||
|
||||
virtual String get_filesystem_type() const override;
|
||||
|
||||
virtual bool is_bundle(const String &p_file) const override;
|
||||
};
|
||||
|
||||
#endif // UNIX ENABLED
|
118
platform/macos/dir_access_macos.mm
Normal file
118
platform/macos/dir_access_macos.mm
Normal file
@@ -0,0 +1,118 @@
|
||||
/**************************************************************************/
|
||||
/* dir_access_macos.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "dir_access_macos.h"
|
||||
|
||||
#if defined(UNIX_ENABLED)
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
|
||||
#include <sys/mount.h>
|
||||
#include <cerrno>
|
||||
|
||||
#import <AppKit/NSWorkspace.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
String DirAccessMacOS::get_filesystem_type() const {
|
||||
struct statfs fs;
|
||||
if (statfs(current_dir.utf8().get_data(), &fs) != 0) {
|
||||
return "";
|
||||
}
|
||||
return String::utf8(fs.f_fstypename).to_upper();
|
||||
}
|
||||
|
||||
String DirAccessMacOS::fix_unicode_name(const char *p_name) const {
|
||||
String fname;
|
||||
if (p_name != nullptr) {
|
||||
NSString *nsstr = [[NSString stringWithUTF8String:p_name] precomposedStringWithCanonicalMapping];
|
||||
fname.append_utf8([nsstr UTF8String]);
|
||||
}
|
||||
|
||||
return fname;
|
||||
}
|
||||
|
||||
int DirAccessMacOS::get_drive_count() {
|
||||
NSArray *res_keys = [NSArray arrayWithObjects:NSURLVolumeURLKey, NSURLIsSystemImmutableKey, nil];
|
||||
NSArray *vols = [[NSFileManager defaultManager] mountedVolumeURLsIncludingResourceValuesForKeys:res_keys options:NSVolumeEnumerationSkipHiddenVolumes];
|
||||
|
||||
return [vols count];
|
||||
}
|
||||
|
||||
String DirAccessMacOS::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.append_utf8([path UTF8String]);
|
||||
|
||||
return volname;
|
||||
}
|
||||
|
||||
bool DirAccessMacOS::is_hidden(const String &p_name) {
|
||||
String f = get_current_dir().path_join(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];
|
||||
}
|
||||
|
||||
bool DirAccessMacOS::is_case_sensitive(const String &p_path) const {
|
||||
String f = p_path;
|
||||
if (!f.is_absolute_path()) {
|
||||
f = get_current_dir().path_join(f);
|
||||
}
|
||||
f = fix_path(f);
|
||||
|
||||
NSURL *url = [NSURL fileURLWithPath:@(f.utf8().get_data())];
|
||||
NSNumber *cs = nil;
|
||||
if (![url getResourceValue:&cs forKey:NSURLVolumeSupportsCaseSensitiveNamesKey error:nil]) {
|
||||
return false;
|
||||
}
|
||||
return [cs boolValue];
|
||||
}
|
||||
|
||||
bool DirAccessMacOS::is_bundle(const String &p_file) const {
|
||||
String f = p_file;
|
||||
if (!f.is_absolute_path()) {
|
||||
f = get_current_dir().path_join(f);
|
||||
}
|
||||
f = fix_path(f);
|
||||
|
||||
return [[NSWorkspace sharedWorkspace] isFilePackageAtPath:[NSString stringWithUTF8String:f.utf8().get_data()]];
|
||||
}
|
||||
|
||||
#endif // UNIX_ENABLED
|
226
platform/macos/display_server_embedded.h
Normal file
226
platform/macos/display_server_embedded.h
Normal file
@@ -0,0 +1,226 @@
|
||||
/**************************************************************************/
|
||||
/* display_server_embedded.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "display_server_macos_base.h"
|
||||
|
||||
@class CAContext;
|
||||
@class CALayer;
|
||||
class GLManagerEmbedded;
|
||||
class RenderingContextDriver;
|
||||
class RenderingDevice;
|
||||
|
||||
struct DisplayServerEmbeddedState {
|
||||
/*! Default to a scale of 2.0, which is the most common. */
|
||||
float screen_max_scale = 2.0f;
|
||||
float screen_dpi = 96.0f;
|
||||
/*! Scale for window displaying embedded content */
|
||||
float screen_window_scale = 2.0f;
|
||||
/*! The display ID of the window which is displaying the embedded process content. */
|
||||
uint32_t display_id = -1;
|
||||
|
||||
void serialize(PackedByteArray &r_data);
|
||||
Error deserialize(const PackedByteArray &p_data);
|
||||
|
||||
_FORCE_INLINE_ bool operator==(const DisplayServerEmbeddedState &p_other) const {
|
||||
return screen_max_scale == p_other.screen_max_scale && screen_dpi == p_other.screen_dpi && display_id == p_other.display_id;
|
||||
}
|
||||
};
|
||||
|
||||
class DisplayServerEmbedded : public DisplayServerMacOSBase {
|
||||
GDSOFTCLASS(DisplayServerEmbedded, DisplayServerMacOSBase)
|
||||
|
||||
DisplayServerEmbeddedState state;
|
||||
|
||||
NativeMenu *native_menu = nullptr;
|
||||
|
||||
HashMap<WindowID, ObjectID> window_attached_instance_id;
|
||||
|
||||
HashMap<WindowID, Callable> window_event_callbacks;
|
||||
HashMap<WindowID, Callable> window_resize_callbacks;
|
||||
HashMap<WindowID, Callable> input_event_callbacks;
|
||||
HashMap<WindowID, Callable> input_text_callbacks;
|
||||
|
||||
WindowID window_id_counter = MAIN_WINDOW_ID;
|
||||
|
||||
bool transparent = false;
|
||||
|
||||
CAContext *ca_context = nullptr;
|
||||
// Either be a CAMetalLayer or a CALayer depending on the rendering driver.
|
||||
CALayer *layer = nullptr;
|
||||
#ifdef GLES3_ENABLED
|
||||
GLManagerEmbedded *gl_manager = nullptr;
|
||||
#endif
|
||||
|
||||
#if defined(RD_ENABLED)
|
||||
RenderingContextDriver *rendering_context = nullptr;
|
||||
RenderingDevice *rendering_device = nullptr;
|
||||
#endif
|
||||
|
||||
String rendering_driver;
|
||||
|
||||
Point2i ime_last_position;
|
||||
Point2i im_selection;
|
||||
String im_text;
|
||||
|
||||
MouseMode mouse_mode = MOUSE_MODE_VISIBLE;
|
||||
MouseMode mouse_mode_base = MOUSE_MODE_VISIBLE;
|
||||
MouseMode mouse_mode_override = MOUSE_MODE_VISIBLE;
|
||||
bool mouse_mode_override_enabled = false;
|
||||
void _mouse_update_mode();
|
||||
|
||||
CursorShape cursor_shape = CURSOR_ARROW;
|
||||
|
||||
struct Joy {
|
||||
String name;
|
||||
uint64_t timestamp = 0;
|
||||
|
||||
Joy() = default;
|
||||
Joy(const String &p_name) :
|
||||
name(p_name) {}
|
||||
};
|
||||
HashMap<int, Joy> joysticks;
|
||||
|
||||
public:
|
||||
static void register_embedded_driver();
|
||||
static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error);
|
||||
static Vector<String> get_rendering_drivers_func();
|
||||
|
||||
// MARK: - Events
|
||||
|
||||
virtual void process_events() override;
|
||||
|
||||
virtual void window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual void window_set_input_text_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual void window_set_drop_files_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
|
||||
static void _dispatch_input_events(const Ref<InputEvent> &p_event);
|
||||
void send_input_event(const Ref<InputEvent> &p_event, DisplayServer::WindowID p_id = MAIN_WINDOW_ID) const;
|
||||
void send_input_text(const String &p_text, DisplayServer::WindowID p_id = MAIN_WINDOW_ID) const;
|
||||
void send_window_event(DisplayServer::WindowEvent p_event, DisplayServer::WindowID p_id = MAIN_WINDOW_ID) const;
|
||||
void _window_callback(const Callable &p_callable, const Variant &p_arg) const;
|
||||
|
||||
virtual void beep() const override;
|
||||
|
||||
// MARK: - Mouse
|
||||
virtual void mouse_set_mode(MouseMode p_mode) override;
|
||||
virtual MouseMode mouse_get_mode() const override;
|
||||
virtual void mouse_set_mode_override(MouseMode p_mode) override;
|
||||
virtual MouseMode mouse_get_mode_override() const override;
|
||||
virtual void mouse_set_mode_override_enabled(bool p_override_enabled) override;
|
||||
virtual bool mouse_is_mode_override_enabled() const override;
|
||||
|
||||
virtual void warp_mouse(const Point2i &p_position) override;
|
||||
virtual Point2i mouse_get_position() const override;
|
||||
virtual BitField<MouseButtonMask> mouse_get_button_state() const override;
|
||||
|
||||
// MARK: - Window
|
||||
|
||||
virtual bool has_feature(Feature p_feature) const override;
|
||||
virtual String get_name() const override;
|
||||
|
||||
virtual int get_screen_count() const override;
|
||||
virtual int get_primary_screen() const override;
|
||||
virtual Point2i screen_get_position(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
|
||||
virtual Size2i screen_get_size(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
|
||||
virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
|
||||
virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
|
||||
virtual float screen_get_scale(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
|
||||
virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
|
||||
|
||||
virtual Vector<DisplayServer::WindowID> get_window_list() const override;
|
||||
|
||||
virtual WindowID get_window_at_screen_position(const Point2i &p_position) const override;
|
||||
|
||||
virtual void window_attach_instance_id(ObjectID p_instance, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual ObjectID window_get_attached_instance_id(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
|
||||
virtual int window_get_current_screen(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
virtual void window_set_current_screen(int p_screen, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
|
||||
virtual Point2i window_get_position(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
virtual Point2i window_get_position_with_decorations(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
virtual void window_set_position(const Point2i &p_position, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
|
||||
virtual void window_set_transient(WindowID p_window, WindowID p_parent) override;
|
||||
|
||||
virtual void window_set_max_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual Size2i window_get_max_size(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual void window_set_min_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual Size2i window_get_min_size(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual void window_set_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual Size2i window_get_size(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
virtual Size2i window_get_size_with_decorations(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual void window_set_mode(WindowMode p_mode, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual WindowMode window_get_mode(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual bool window_is_maximize_allowed(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual void window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual bool window_get_flag(WindowFlags p_flag, WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual void window_request_attention(WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual void window_move_to_foreground(WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual bool window_is_focused(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual float screen_get_max_scale() const override;
|
||||
|
||||
virtual 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 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;
|
||||
|
||||
void update_im_text(const Point2i &p_selection, const String &p_text);
|
||||
virtual Point2i ime_get_selection() const override;
|
||||
virtual String ime_get_text() const override;
|
||||
|
||||
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;
|
||||
|
||||
void set_state(const DisplayServerEmbeddedState &p_state);
|
||||
virtual void swap_buffers() override;
|
||||
|
||||
DisplayServerEmbedded(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error);
|
||||
~DisplayServerEmbedded();
|
||||
};
|
850
platform/macos/display_server_embedded.mm
Normal file
850
platform/macos/display_server_embedded.mm
Normal file
@@ -0,0 +1,850 @@
|
||||
/**************************************************************************/
|
||||
/* display_server_embedded.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "display_server_embedded.h"
|
||||
|
||||
#if defined(GLES3_ENABLED)
|
||||
#import "embedded_gl_manager.h"
|
||||
#import "platform_gl.h"
|
||||
|
||||
#import "drivers/gles3/rasterizer_gles3.h"
|
||||
#endif
|
||||
|
||||
#if defined(RD_ENABLED)
|
||||
#import "servers/rendering/renderer_rd/renderer_compositor_rd.h"
|
||||
#import "servers/rendering/rendering_device.h"
|
||||
|
||||
#if defined(VULKAN_ENABLED)
|
||||
#import "rendering_context_driver_vulkan_macos.h"
|
||||
#endif // VULKAN_ENABLED
|
||||
#if defined(METAL_ENABLED)
|
||||
#import "drivers/metal/rendering_context_driver_metal.h"
|
||||
#endif
|
||||
#endif // RD_ENABLED
|
||||
|
||||
#import "embedded_debugger.h"
|
||||
#import "macos_quartz_core_spi.h"
|
||||
|
||||
#import "core/config/project_settings.h"
|
||||
#import "core/debugger/engine_debugger.h"
|
||||
#import "core/io/marshalls.h"
|
||||
|
||||
DisplayServerEmbedded::DisplayServerEmbedded(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) {
|
||||
EmbeddedDebugger::initialize(this);
|
||||
|
||||
r_error = OK; // default to OK
|
||||
|
||||
native_menu = memnew(NativeMenu);
|
||||
|
||||
Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events);
|
||||
|
||||
rendering_driver = p_rendering_driver;
|
||||
|
||||
#if defined(RD_ENABLED)
|
||||
#if defined(VULKAN_ENABLED)
|
||||
#if defined(__x86_64__)
|
||||
bool fallback_to_vulkan = GLOBAL_GET("rendering/rendering_device/fallback_to_vulkan");
|
||||
if (!fallback_to_vulkan) {
|
||||
WARN_PRINT("Metal is not supported on Intel Macs, switching to Vulkan.");
|
||||
}
|
||||
// Metal rendering driver not available on Intel.
|
||||
if (rendering_driver == "metal") {
|
||||
rendering_driver = "vulkan";
|
||||
OS::get_singleton()->set_current_rendering_driver_name(rendering_driver);
|
||||
}
|
||||
#endif
|
||||
if (rendering_driver == "vulkan") {
|
||||
rendering_context = memnew(RenderingContextDriverVulkanMacOS);
|
||||
}
|
||||
#endif
|
||||
#if defined(METAL_ENABLED)
|
||||
if (rendering_driver == "metal") {
|
||||
rendering_context = memnew(RenderingContextDriverMetal);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (rendering_context) {
|
||||
if (rendering_context->initialize() != OK) {
|
||||
memdelete(rendering_context);
|
||||
rendering_context = nullptr;
|
||||
#if defined(GLES3_ENABLED)
|
||||
bool fallback_to_opengl3 = GLOBAL_GET("rendering/rendering_device/fallback_to_opengl3");
|
||||
if (fallback_to_opengl3 && rendering_driver != "opengl3") {
|
||||
WARN_PRINT("Your device does not seem to support MoltenVK or Metal, switching to OpenGL 3.");
|
||||
rendering_driver = "opengl3";
|
||||
OS::get_singleton()->set_current_rendering_method("gl_compatibility");
|
||||
OS::get_singleton()->set_current_rendering_driver_name(rendering_driver);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
r_error = ERR_CANT_CREATE;
|
||||
ERR_FAIL_MSG("Could not initialize " + rendering_driver);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(GLES3_ENABLED)
|
||||
if (rendering_driver == "opengl3_angle") {
|
||||
WARN_PRINT("ANGLE not supported for embedded display, switching to native OpenGL.");
|
||||
rendering_driver = "opengl3";
|
||||
OS::get_singleton()->set_current_rendering_driver_name(rendering_driver);
|
||||
}
|
||||
|
||||
if (rendering_driver == "opengl3") {
|
||||
gl_manager = memnew(GLManagerEmbedded);
|
||||
if (gl_manager->initialize() != OK) {
|
||||
memdelete(gl_manager);
|
||||
gl_manager = nullptr;
|
||||
r_error = ERR_UNAVAILABLE;
|
||||
ERR_FAIL_MSG("Could not initialize native OpenGL.");
|
||||
}
|
||||
layer = [CALayer new];
|
||||
// OpenGL content is flipped, so it must be transformed.
|
||||
layer.anchorPoint = CGPointMake(0, 0);
|
||||
layer.transform = CATransform3DMakeScale(1.0, -1.0, 1.0);
|
||||
|
||||
Error err = gl_manager->window_create(window_id_counter, layer, p_resolution.width, p_resolution.height);
|
||||
if (err != OK) {
|
||||
ERR_FAIL_MSG("Could not create OpenGL context.");
|
||||
}
|
||||
gl_manager->set_vsync_enabled(p_vsync_mode != DisplayServer::VSYNC_DISABLED);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(RD_ENABLED)
|
||||
if (rendering_context) {
|
||||
layer = [CAMetalLayer new];
|
||||
layer.anchorPoint = CGPointMake(0, 1);
|
||||
|
||||
union {
|
||||
#ifdef VULKAN_ENABLED
|
||||
RenderingContextDriverVulkanMacOS::WindowPlatformData vulkan;
|
||||
#endif
|
||||
#ifdef METAL_ENABLED
|
||||
RenderingContextDriverMetal::WindowPlatformData metal;
|
||||
#endif
|
||||
} wpd;
|
||||
#ifdef VULKAN_ENABLED
|
||||
if (rendering_driver == "vulkan") {
|
||||
wpd.vulkan.layer_ptr = (CAMetalLayer *const *)&layer;
|
||||
}
|
||||
#endif
|
||||
#ifdef METAL_ENABLED
|
||||
if (rendering_driver == "metal") {
|
||||
wpd.metal.layer = (CAMetalLayer *)layer;
|
||||
}
|
||||
#endif
|
||||
Error err = rendering_context->window_create(window_id_counter, &wpd);
|
||||
ERR_FAIL_COND_MSG(err != OK, vformat("Can't create a %s context", rendering_driver));
|
||||
|
||||
// The rendering context is always in pixels
|
||||
rendering_context->window_set_size(window_id_counter, p_resolution.width, p_resolution.height);
|
||||
rendering_context->window_set_vsync_mode(window_id_counter, p_vsync_mode);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(GLES3_ENABLED)
|
||||
if (rendering_driver == "opengl3") {
|
||||
RasterizerGLES3::make_current(true);
|
||||
}
|
||||
if (rendering_driver == "opengl3_angle") {
|
||||
RasterizerGLES3::make_current(false);
|
||||
}
|
||||
#endif
|
||||
#if defined(RD_ENABLED)
|
||||
if (rendering_context) {
|
||||
rendering_device = memnew(RenderingDevice);
|
||||
rendering_device->initialize(rendering_context, MAIN_WINDOW_ID);
|
||||
rendering_device->screen_create(MAIN_WINDOW_ID);
|
||||
|
||||
RendererCompositorRD::make_current();
|
||||
}
|
||||
#endif
|
||||
|
||||
CGFloat scale = screen_get_max_scale();
|
||||
layer.contentsScale = scale;
|
||||
layer.magnificationFilter = kCAFilterNearest;
|
||||
layer.minificationFilter = kCAFilterNearest;
|
||||
transparent = ((p_flags & WINDOW_FLAG_TRANSPARENT_BIT) == WINDOW_FLAG_TRANSPARENT_BIT);
|
||||
layer.opaque = !(OS::get_singleton()->is_layered_allowed() && transparent);
|
||||
layer.actions = @{ @"contents" : [NSNull null] }; // Disable implicit animations for contents.
|
||||
// AppKit frames, bounds and positions are always in points.
|
||||
CGRect bounds = CGRectMake(0, 0, p_resolution.width, p_resolution.height);
|
||||
bounds = CGRectApplyAffineTransform(bounds, CGAffineTransformInvert(CGAffineTransformMakeScale(scale, scale)));
|
||||
layer.bounds = bounds;
|
||||
|
||||
CGSConnectionID connection_id = CGSMainConnectionID();
|
||||
ca_context = [CAContext contextWithCGSConnection:connection_id options:@{ kCAContextCIFilterBehavior : @"ignore" }];
|
||||
ca_context.layer = layer;
|
||||
|
||||
{
|
||||
Array arr = { ca_context.contextId };
|
||||
EngineDebugger::get_singleton()->send_message("game_view:set_context_id", arr);
|
||||
}
|
||||
}
|
||||
|
||||
DisplayServerEmbedded::~DisplayServerEmbedded() {
|
||||
if (native_menu) {
|
||||
memdelete(native_menu);
|
||||
native_menu = nullptr;
|
||||
}
|
||||
|
||||
EmbeddedDebugger::deinitialize();
|
||||
|
||||
#if defined(GLES3_ENABLED)
|
||||
if (gl_manager) {
|
||||
memdelete(gl_manager);
|
||||
gl_manager = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(RD_ENABLED)
|
||||
if (rendering_device) {
|
||||
memdelete(rendering_device);
|
||||
rendering_device = nullptr;
|
||||
}
|
||||
|
||||
if (rendering_context) {
|
||||
memdelete(rendering_context);
|
||||
rendering_context = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
DisplayServer *DisplayServerEmbedded::create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t /* p_parent_window */, Error &r_error) {
|
||||
return memnew(DisplayServerEmbedded(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, r_error));
|
||||
}
|
||||
|
||||
Vector<String> DisplayServerEmbedded::get_rendering_drivers_func() {
|
||||
Vector<String> drivers;
|
||||
|
||||
#if defined(VULKAN_ENABLED)
|
||||
drivers.push_back("vulkan");
|
||||
#endif
|
||||
#if defined(METAL_ENABLED)
|
||||
drivers.push_back("metal");
|
||||
#endif
|
||||
#if defined(GLES3_ENABLED)
|
||||
drivers.push_back("opengl3");
|
||||
#endif
|
||||
|
||||
return drivers;
|
||||
}
|
||||
|
||||
void DisplayServerEmbedded::register_embedded_driver() {
|
||||
register_create_function("embedded", create_func, get_rendering_drivers_func);
|
||||
}
|
||||
|
||||
void DisplayServerEmbedded::beep() const {
|
||||
NSBeep();
|
||||
}
|
||||
|
||||
// MARK: - Mouse
|
||||
|
||||
void DisplayServerEmbedded::_mouse_update_mode() {
|
||||
MouseMode wanted_mouse_mode = mouse_mode_override_enabled
|
||||
? mouse_mode_override
|
||||
: mouse_mode_base;
|
||||
|
||||
if (wanted_mouse_mode == mouse_mode) {
|
||||
return;
|
||||
}
|
||||
|
||||
EngineDebugger::get_singleton()->send_message("game_view:mouse_set_mode", { wanted_mouse_mode });
|
||||
|
||||
mouse_mode = wanted_mouse_mode;
|
||||
}
|
||||
|
||||
void DisplayServerEmbedded::mouse_set_mode(MouseMode p_mode) {
|
||||
if (p_mode == mouse_mode_base) {
|
||||
return;
|
||||
}
|
||||
mouse_mode_base = p_mode;
|
||||
_mouse_update_mode();
|
||||
}
|
||||
|
||||
DisplayServerEmbedded::MouseMode DisplayServerEmbedded::mouse_get_mode() const {
|
||||
return mouse_mode;
|
||||
}
|
||||
|
||||
void DisplayServerEmbedded::mouse_set_mode_override(MouseMode p_mode) {
|
||||
ERR_FAIL_INDEX(p_mode, MouseMode::MOUSE_MODE_MAX);
|
||||
if (p_mode == mouse_mode_override) {
|
||||
return;
|
||||
}
|
||||
mouse_mode_override = p_mode;
|
||||
_mouse_update_mode();
|
||||
}
|
||||
|
||||
DisplayServer::MouseMode DisplayServerEmbedded::mouse_get_mode_override() const {
|
||||
return mouse_mode_override;
|
||||
}
|
||||
|
||||
void DisplayServerEmbedded::mouse_set_mode_override_enabled(bool p_override_enabled) {
|
||||
if (p_override_enabled == mouse_mode_override_enabled) {
|
||||
return;
|
||||
}
|
||||
mouse_mode_override_enabled = p_override_enabled;
|
||||
_mouse_update_mode();
|
||||
}
|
||||
|
||||
bool DisplayServerEmbedded::mouse_is_mode_override_enabled() const {
|
||||
return mouse_mode_override_enabled;
|
||||
}
|
||||
|
||||
void DisplayServerEmbedded::warp_mouse(const Point2i &p_position) {
|
||||
_THREAD_SAFE_METHOD_
|
||||
Input::get_singleton()->set_mouse_position(p_position);
|
||||
EngineDebugger::get_singleton()->send_message("game_view:warp_mouse", { p_position });
|
||||
}
|
||||
|
||||
Point2i DisplayServerEmbedded::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;
|
||||
// TODO(sgc): fix this
|
||||
// pos -= _get_screens_origin();
|
||||
pos.y *= -1;
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
return Vector2i();
|
||||
}
|
||||
|
||||
BitField<MouseButtonMask> DisplayServerEmbedded::mouse_get_button_state() const {
|
||||
BitField<MouseButtonMask> last_button_state = MouseButtonMask::NONE;
|
||||
|
||||
NSUInteger buttons = [NSEvent pressedMouseButtons];
|
||||
if (buttons & (1 << 0)) {
|
||||
last_button_state.set_flag(MouseButtonMask::LEFT);
|
||||
}
|
||||
if (buttons & (1 << 1)) {
|
||||
last_button_state.set_flag(MouseButtonMask::RIGHT);
|
||||
}
|
||||
if (buttons & (1 << 2)) {
|
||||
last_button_state.set_flag(MouseButtonMask::MIDDLE);
|
||||
}
|
||||
if (buttons & (1 << 3)) {
|
||||
last_button_state.set_flag(MouseButtonMask::MB_XBUTTON1);
|
||||
}
|
||||
if (buttons & (1 << 4)) {
|
||||
last_button_state.set_flag(MouseButtonMask::MB_XBUTTON2);
|
||||
}
|
||||
return last_button_state;
|
||||
}
|
||||
|
||||
// MARK: Events
|
||||
|
||||
void DisplayServerEmbedded::window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window) {
|
||||
window_resize_callbacks[p_window] = p_callable;
|
||||
}
|
||||
|
||||
void DisplayServerEmbedded::window_set_window_event_callback(const Callable &p_callable, WindowID p_window) {
|
||||
window_event_callbacks[p_window] = p_callable;
|
||||
}
|
||||
void DisplayServerEmbedded::window_set_input_event_callback(const Callable &p_callable, WindowID p_window) {
|
||||
input_event_callbacks[p_window] = p_callable;
|
||||
}
|
||||
|
||||
void DisplayServerEmbedded::window_set_input_text_callback(const Callable &p_callable, WindowID p_window) {
|
||||
input_text_callbacks[p_window] = p_callable;
|
||||
}
|
||||
|
||||
void DisplayServerEmbedded::window_set_drop_files_callback(const Callable &p_callable, WindowID p_window) {
|
||||
// Not supported
|
||||
}
|
||||
|
||||
void DisplayServerEmbedded::process_events() {
|
||||
Input *input = Input::get_singleton();
|
||||
input->flush_buffered_events();
|
||||
}
|
||||
|
||||
void DisplayServerEmbedded::_dispatch_input_events(const Ref<InputEvent> &p_event) {
|
||||
Ref<InputEventFromWindow> event_from_window = p_event;
|
||||
WindowID window_id = INVALID_WINDOW_ID;
|
||||
if (event_from_window.is_valid()) {
|
||||
window_id = event_from_window->get_window_id();
|
||||
}
|
||||
DisplayServerEmbedded *ds = (DisplayServerEmbedded *)DisplayServer::get_singleton();
|
||||
ds->send_input_event(p_event, window_id);
|
||||
}
|
||||
|
||||
void DisplayServerEmbedded::send_input_event(const Ref<InputEvent> &p_event, WindowID p_id) const {
|
||||
if (p_id != INVALID_WINDOW_ID) {
|
||||
const Callable *cb = input_event_callbacks.getptr(p_id);
|
||||
if (cb) {
|
||||
_window_callback(*cb, p_event);
|
||||
}
|
||||
} else {
|
||||
for (const KeyValue<WindowID, Callable> &E : input_event_callbacks) {
|
||||
_window_callback(E.value, p_event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayServerEmbedded::send_input_text(const String &p_text, WindowID p_id) const {
|
||||
const Callable *cb = input_text_callbacks.getptr(p_id);
|
||||
if (cb) {
|
||||
_window_callback(*cb, p_text);
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayServerEmbedded::send_window_event(DisplayServer::WindowEvent p_event, WindowID p_id) const {
|
||||
const Callable *cb = window_event_callbacks.getptr(p_id);
|
||||
if (cb) {
|
||||
_window_callback(*cb, int(p_event));
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayServerEmbedded::_window_callback(const Callable &p_callable, const Variant &p_arg) const {
|
||||
if (p_callable.is_valid()) {
|
||||
p_callable.call(p_arg);
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
bool DisplayServerEmbedded::has_feature(Feature p_feature) const {
|
||||
switch (p_feature) {
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
case FEATURE_GLOBAL_MENU: {
|
||||
return (native_menu && native_menu->has_feature(NativeMenu::FEATURE_GLOBAL_MENU));
|
||||
} break;
|
||||
#endif
|
||||
case FEATURE_CURSOR_SHAPE:
|
||||
case FEATURE_IME:
|
||||
case FEATURE_CUSTOM_CURSOR_SHAPE:
|
||||
// case FEATURE_HIDPI:
|
||||
// case FEATURE_ICON:
|
||||
// case FEATURE_MOUSE:
|
||||
case FEATURE_MOUSE_WARP:
|
||||
// case FEATURE_NATIVE_DIALOG:
|
||||
// case FEATURE_NATIVE_ICON:
|
||||
// case FEATURE_WINDOW_TRANSPARENCY:
|
||||
case FEATURE_CLIPBOARD:
|
||||
// case FEATURE_KEEP_SCREEN_ON:
|
||||
// case FEATURE_ORIENTATION:
|
||||
// case FEATURE_VIRTUAL_KEYBOARD:
|
||||
case FEATURE_TEXT_TO_SPEECH:
|
||||
// case FEATURE_TOUCHSCREEN:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
String DisplayServerEmbedded::get_name() const {
|
||||
return "embedded";
|
||||
}
|
||||
|
||||
int DisplayServerEmbedded::get_screen_count() const {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int DisplayServerEmbedded::get_primary_screen() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Point2i DisplayServerEmbedded::screen_get_position(int p_screen) const {
|
||||
_THREAD_SAFE_METHOD_
|
||||
|
||||
p_screen = _get_screen_index(p_screen);
|
||||
int screen_count = get_screen_count();
|
||||
ERR_FAIL_INDEX_V(p_screen, screen_count, Point2i());
|
||||
|
||||
return Point2i(0, 0);
|
||||
}
|
||||
|
||||
Size2i DisplayServerEmbedded::screen_get_size(int p_screen) const {
|
||||
_THREAD_SAFE_METHOD_
|
||||
|
||||
p_screen = _get_screen_index(p_screen);
|
||||
int screen_count = get_screen_count();
|
||||
ERR_FAIL_INDEX_V(p_screen, screen_count, Size2i());
|
||||
|
||||
return window_get_size(MAIN_WINDOW_ID);
|
||||
}
|
||||
|
||||
Rect2i DisplayServerEmbedded::screen_get_usable_rect(int p_screen) const {
|
||||
_THREAD_SAFE_METHOD_
|
||||
|
||||
p_screen = _get_screen_index(p_screen);
|
||||
int screen_count = get_screen_count();
|
||||
ERR_FAIL_INDEX_V(p_screen, screen_count, Rect2i());
|
||||
|
||||
return Rect2i(screen_get_position(p_screen), screen_get_size(p_screen));
|
||||
}
|
||||
|
||||
int DisplayServerEmbedded::screen_get_dpi(int p_screen) const {
|
||||
_THREAD_SAFE_METHOD_
|
||||
|
||||
p_screen = _get_screen_index(p_screen);
|
||||
int screen_count = get_screen_count();
|
||||
ERR_FAIL_INDEX_V(p_screen, screen_count, 72);
|
||||
|
||||
return 96;
|
||||
}
|
||||
|
||||
float DisplayServerEmbedded::screen_get_scale(int p_screen) const {
|
||||
_THREAD_SAFE_METHOD_
|
||||
|
||||
switch (p_screen) {
|
||||
case SCREEN_WITH_MOUSE_FOCUS:
|
||||
case SCREEN_WITH_KEYBOARD_FOCUS:
|
||||
case SCREEN_PRIMARY:
|
||||
case SCREEN_OF_MAIN_WINDOW:
|
||||
case 0:
|
||||
return state.screen_window_scale;
|
||||
default:
|
||||
return 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
float DisplayServerEmbedded::screen_get_refresh_rate(int p_screen) const {
|
||||
_THREAD_SAFE_METHOD_
|
||||
|
||||
p_screen = _get_screen_index(p_screen);
|
||||
int screen_count = get_screen_count();
|
||||
ERR_FAIL_INDEX_V(p_screen, screen_count, SCREEN_REFRESH_RATE_FALLBACK);
|
||||
|
||||
p_screen = _get_screen_index(p_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> DisplayServerEmbedded::get_window_list() const {
|
||||
Vector<DisplayServer::WindowID> list;
|
||||
list.push_back(MAIN_WINDOW_ID);
|
||||
return list;
|
||||
}
|
||||
|
||||
DisplayServer::WindowID DisplayServerEmbedded::get_window_at_screen_position(const Point2i &p_position) const {
|
||||
return MAIN_WINDOW_ID;
|
||||
}
|
||||
|
||||
void DisplayServerEmbedded::window_attach_instance_id(ObjectID p_instance, WindowID p_window) {
|
||||
window_attached_instance_id[p_window] = p_instance;
|
||||
}
|
||||
|
||||
ObjectID DisplayServerEmbedded::window_get_attached_instance_id(WindowID p_window) const {
|
||||
return window_attached_instance_id[p_window];
|
||||
}
|
||||
|
||||
void DisplayServerEmbedded::window_set_title(const String &p_title, WindowID p_window) {
|
||||
// Not supported
|
||||
}
|
||||
|
||||
int DisplayServerEmbedded::window_get_current_screen(WindowID p_window) const {
|
||||
_THREAD_SAFE_METHOD_
|
||||
ERR_FAIL_COND_V(p_window != MAIN_WINDOW_ID, INVALID_SCREEN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DisplayServerEmbedded::window_set_current_screen(int p_screen, WindowID p_window) {
|
||||
// Not supported
|
||||
}
|
||||
|
||||
Point2i DisplayServerEmbedded::window_get_position(WindowID p_window) const {
|
||||
return Point2i();
|
||||
}
|
||||
|
||||
Point2i DisplayServerEmbedded::window_get_position_with_decorations(WindowID p_window) const {
|
||||
return Point2i();
|
||||
}
|
||||
|
||||
void DisplayServerEmbedded::window_set_position(const Point2i &p_position, WindowID p_window) {
|
||||
// Probably not supported for single window iOS app
|
||||
}
|
||||
|
||||
void DisplayServerEmbedded::window_set_transient(WindowID p_window, WindowID p_parent) {
|
||||
// Not supported
|
||||
}
|
||||
|
||||
void DisplayServerEmbedded::window_set_max_size(const Size2i p_size, WindowID p_window) {
|
||||
// Not supported
|
||||
}
|
||||
|
||||
Size2i DisplayServerEmbedded::window_get_max_size(WindowID p_window) const {
|
||||
return Size2i();
|
||||
}
|
||||
|
||||
void DisplayServerEmbedded::window_set_min_size(const Size2i p_size, WindowID p_window) {
|
||||
// Not supported
|
||||
}
|
||||
|
||||
Size2i DisplayServerEmbedded::window_get_min_size(WindowID p_window) const {
|
||||
return Size2i();
|
||||
}
|
||||
|
||||
void DisplayServerEmbedded::window_set_size(const Size2i p_size, WindowID p_window) {
|
||||
[CATransaction begin];
|
||||
[CATransaction setDisableActions:YES];
|
||||
|
||||
CGFloat scale = screen_get_max_scale();
|
||||
CGRect bounds = CGRectMake(0, 0, p_size.width, p_size.height);
|
||||
bounds = CGRectApplyAffineTransform(bounds, CGAffineTransformInvert(CGAffineTransformMakeScale(scale, scale)));
|
||||
layer.bounds = bounds;
|
||||
layer.contentsScale = scale;
|
||||
|
||||
#if defined(RD_ENABLED)
|
||||
if (rendering_context) {
|
||||
rendering_context->window_set_size(p_window, p_size.width, p_size.height);
|
||||
}
|
||||
#endif
|
||||
#if defined(GLES3_ENABLED)
|
||||
if (gl_manager) {
|
||||
gl_manager->window_resize(p_window, p_size.width, p_size.height);
|
||||
}
|
||||
#endif
|
||||
[CATransaction commit];
|
||||
|
||||
Callable *cb = window_resize_callbacks.getptr(p_window);
|
||||
if (cb) {
|
||||
Variant resize_rect = Rect2i(Point2i(), p_size);
|
||||
_window_callback(window_resize_callbacks[p_window], resize_rect);
|
||||
}
|
||||
}
|
||||
|
||||
Size2i DisplayServerEmbedded::window_get_size(WindowID p_window) const {
|
||||
#if defined(RD_ENABLED)
|
||||
if (rendering_context) {
|
||||
RenderingContextDriver::SurfaceID surface = rendering_context->surface_get_from_window(p_window);
|
||||
ERR_FAIL_COND_V_MSG(surface == 0, Size2i(), "Invalid window ID");
|
||||
uint32_t width = rendering_context->surface_get_width(surface);
|
||||
uint32_t height = rendering_context->surface_get_height(surface);
|
||||
return Size2i(width, height);
|
||||
}
|
||||
#endif
|
||||
#ifdef GLES3_ENABLED
|
||||
if (gl_manager) {
|
||||
return gl_manager->window_get_size(p_window);
|
||||
}
|
||||
#endif
|
||||
return Size2i();
|
||||
}
|
||||
|
||||
Size2i DisplayServerEmbedded::window_get_size_with_decorations(WindowID p_window) const {
|
||||
return window_get_size(p_window);
|
||||
}
|
||||
|
||||
void DisplayServerEmbedded::window_set_mode(WindowMode p_mode, WindowID p_window) {
|
||||
// Not supported
|
||||
}
|
||||
|
||||
DisplayServer::WindowMode DisplayServerEmbedded::window_get_mode(WindowID p_window) const {
|
||||
return WindowMode::WINDOW_MODE_WINDOWED;
|
||||
}
|
||||
|
||||
bool DisplayServerEmbedded::window_is_maximize_allowed(WindowID p_window) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void DisplayServerEmbedded::window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window) {
|
||||
if (p_flag == WINDOW_FLAG_TRANSPARENT && p_window == MAIN_WINDOW_ID) {
|
||||
transparent = p_enabled;
|
||||
layer.opaque = !(OS::get_singleton()->is_layered_allowed() && transparent);
|
||||
}
|
||||
}
|
||||
|
||||
bool DisplayServerEmbedded::window_get_flag(WindowFlags p_flag, WindowID p_window) const {
|
||||
if (p_flag == WINDOW_FLAG_TRANSPARENT && p_window == MAIN_WINDOW_ID) {
|
||||
return transparent;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void DisplayServerEmbedded::window_request_attention(WindowID p_window) {
|
||||
// Not supported
|
||||
}
|
||||
|
||||
void DisplayServerEmbedded::window_move_to_foreground(WindowID p_window) {
|
||||
// Not supported
|
||||
}
|
||||
|
||||
bool DisplayServerEmbedded::window_is_focused(WindowID p_window) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
float DisplayServerEmbedded::screen_get_max_scale() const {
|
||||
return state.screen_max_scale;
|
||||
}
|
||||
|
||||
bool DisplayServerEmbedded::window_can_draw(WindowID p_window) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DisplayServerEmbedded::can_any_window_draw() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
void DisplayServerEmbedded::window_set_ime_active(const bool p_active, WindowID p_window) {
|
||||
EngineDebugger::get_singleton()->send_message("game_view:window_set_ime_active", { p_active });
|
||||
}
|
||||
|
||||
void DisplayServerEmbedded::window_set_ime_position(const Point2i &p_pos, WindowID p_window) {
|
||||
if (p_pos == ime_last_position) {
|
||||
return;
|
||||
}
|
||||
EngineDebugger::get_singleton()->send_message("game_view:window_set_ime_position", { p_pos });
|
||||
ime_last_position = p_pos;
|
||||
}
|
||||
|
||||
void DisplayServerEmbedded::set_state(const DisplayServerEmbeddedState &p_state) {
|
||||
if (state == p_state) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t old_display_id = state.display_id;
|
||||
|
||||
state = p_state;
|
||||
|
||||
if (state.display_id != old_display_id) {
|
||||
#if defined(GLES3_ENABLED)
|
||||
if (gl_manager) {
|
||||
gl_manager->set_display_id(state.display_id);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayServerEmbedded::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) {
|
||||
#if defined(GLES3_ENABLED)
|
||||
if (gl_manager) {
|
||||
gl_manager->set_vsync_enabled(p_vsync_mode != DisplayServer::VSYNC_DISABLED);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(RD_ENABLED)
|
||||
if (rendering_context) {
|
||||
rendering_context->window_set_vsync_mode(p_window, p_vsync_mode);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
DisplayServer::VSyncMode DisplayServerEmbedded::window_get_vsync_mode(WindowID p_window) const {
|
||||
_THREAD_SAFE_METHOD_
|
||||
#if defined(GLES3_ENABLED)
|
||||
if (gl_manager) {
|
||||
return (gl_manager->is_vsync_enabled() ? DisplayServer::VSyncMode::VSYNC_ENABLED : DisplayServer::VSyncMode::VSYNC_DISABLED);
|
||||
}
|
||||
#endif
|
||||
#if defined(RD_ENABLED)
|
||||
if (rendering_context) {
|
||||
return rendering_context->window_get_vsync_mode(p_window);
|
||||
}
|
||||
#endif
|
||||
return DisplayServer::VSYNC_ENABLED;
|
||||
}
|
||||
|
||||
void DisplayServerEmbedded::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);
|
||||
}
|
||||
|
||||
Point2i DisplayServerEmbedded::ime_get_selection() const {
|
||||
return im_selection;
|
||||
}
|
||||
|
||||
String DisplayServerEmbedded::ime_get_text() const {
|
||||
return im_text;
|
||||
}
|
||||
|
||||
void DisplayServerEmbedded::cursor_set_shape(CursorShape p_shape) {
|
||||
cursor_shape = p_shape;
|
||||
EngineDebugger::get_singleton()->send_message("game_view:cursor_set_shape", { p_shape });
|
||||
}
|
||||
|
||||
DisplayServer::CursorShape DisplayServerEmbedded::cursor_get_shape() const {
|
||||
return cursor_shape;
|
||||
}
|
||||
|
||||
void DisplayServerEmbedded::cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
|
||||
PackedByteArray data;
|
||||
if (p_cursor.is_valid()) {
|
||||
Ref<Image> image = _get_cursor_image_from_resource(p_cursor, p_hotspot);
|
||||
if (image.is_valid()) {
|
||||
data = image->save_png_to_buffer();
|
||||
}
|
||||
}
|
||||
EngineDebugger::get_singleton()->send_message("game_view:cursor_set_custom_image", { data, p_shape, p_hotspot });
|
||||
}
|
||||
|
||||
void DisplayServerEmbedded::swap_buffers() {
|
||||
#ifdef GLES3_ENABLED
|
||||
if (gl_manager) {
|
||||
gl_manager->swap_buffers();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void DisplayServerEmbeddedState::serialize(PackedByteArray &r_data) {
|
||||
r_data.resize(16);
|
||||
|
||||
uint8_t *data = r_data.ptrw();
|
||||
data += encode_float(screen_max_scale, data);
|
||||
data += encode_float(screen_dpi, data);
|
||||
data += encode_float(screen_window_scale, data);
|
||||
data += encode_uint32(display_id, data);
|
||||
|
||||
// Assert we had enough space.
|
||||
DEV_ASSERT(r_data.size() >= (data - r_data.ptrw()));
|
||||
}
|
||||
|
||||
Error DisplayServerEmbeddedState::deserialize(const PackedByteArray &p_data) {
|
||||
const uint8_t *data = p_data.ptr();
|
||||
|
||||
screen_max_scale = decode_float(data);
|
||||
data += sizeof(float);
|
||||
screen_dpi = decode_float(data);
|
||||
data += sizeof(float);
|
||||
screen_window_scale = decode_float(data);
|
||||
data += sizeof(float);
|
||||
display_id = decode_uint32(data);
|
||||
|
||||
return OK;
|
||||
}
|
474
platform/macos/display_server_macos.h
Normal file
474
platform/macos/display_server_macos.h
Normal file
@@ -0,0 +1,474 @@
|
||||
/**************************************************************************/
|
||||
/* display_server_macos.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "display_server_macos_base.h"
|
||||
|
||||
#if defined(GLES3_ENABLED)
|
||||
#include "gl_manager_macos_angle.h"
|
||||
#include "gl_manager_macos_legacy.h"
|
||||
#endif // GLES3_ENABLED
|
||||
|
||||
#import "native_menu_macos.h"
|
||||
|
||||
#if defined(RD_ENABLED)
|
||||
#include "servers/rendering/rendering_device.h"
|
||||
|
||||
#if defined(VULKAN_ENABLED)
|
||||
#import "rendering_context_driver_vulkan_macos.h"
|
||||
#endif // VULKAN_ENABLED
|
||||
#if defined(METAL_ENABLED)
|
||||
#import "drivers/metal/rendering_context_driver_metal.h"
|
||||
#endif
|
||||
#endif // RD_ENABLED
|
||||
|
||||
#define FontVariation __FontVariation
|
||||
#define BitMap _QDBitMap // Suppress deprecated QuickDraw definition.
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <AppKit/NSCursor.h>
|
||||
#import <ApplicationServices/ApplicationServices.h>
|
||||
#import <CoreVideo/CoreVideo.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <IOKit/pwr_mgt/IOPMLib.h>
|
||||
|
||||
@class GodotWindow;
|
||||
@class GodotContentView;
|
||||
@class GodotWindowDelegate;
|
||||
@class GodotButtonView;
|
||||
@class GodotEmbeddedView;
|
||||
@class CALayerHost;
|
||||
|
||||
#undef BitMap
|
||||
#undef CursorShape
|
||||
#undef FontVariation
|
||||
|
||||
class EmbeddedProcessMacOS;
|
||||
|
||||
class DisplayServerMacOS : public DisplayServerMacOSBase {
|
||||
GDSOFTCLASS(DisplayServerMacOS, DisplayServerMacOSBase);
|
||||
|
||||
public:
|
||||
struct KeyEvent {
|
||||
WindowID window_id = INVALID_WINDOW_ID;
|
||||
unsigned int macos_state = false;
|
||||
bool pressed = false;
|
||||
bool echo = false;
|
||||
bool raw = false;
|
||||
Key keycode = Key::NONE;
|
||||
Key physical_keycode = Key::NONE;
|
||||
Key key_label = Key::NONE;
|
||||
uint32_t unicode = 0;
|
||||
KeyLocation location = KeyLocation::UNSPECIFIED;
|
||||
};
|
||||
|
||||
struct WindowData {
|
||||
GodotWindowDelegate *window_delegate;
|
||||
GodotWindow *window_object;
|
||||
GodotContentView *window_view;
|
||||
GodotButtonView *window_button_view;
|
||||
|
||||
Vector<Vector2> mpath;
|
||||
|
||||
CGDirectDisplayID display_id = -1;
|
||||
|
||||
Point2i mouse_pos;
|
||||
WindowResizeEdge edge = WINDOW_EDGE_MAX;
|
||||
|
||||
Size2i min_size;
|
||||
Size2i max_size;
|
||||
Size2i size;
|
||||
Vector2i wb_offset = Vector2i(14, 14);
|
||||
|
||||
NSRect last_frame_rect = NSMakeRect(0, 0, 0, 0);
|
||||
NSRect pre_zoom_rect = NSMakeRect(0, 0, 0, 0);
|
||||
|
||||
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;
|
||||
bool fs_transition = false;
|
||||
bool initial_size = true;
|
||||
|
||||
WindowID transient_parent = INVALID_WINDOW_ID;
|
||||
bool exclusive = false;
|
||||
HashSet<WindowID> transient_children;
|
||||
|
||||
bool layered_window = false;
|
||||
bool fullscreen = false;
|
||||
bool exclusive_fullscreen = false;
|
||||
bool on_top = false;
|
||||
bool borderless = false;
|
||||
bool resize_disabled = false;
|
||||
bool no_min_btn = false;
|
||||
bool no_max_btn = false;
|
||||
bool no_focus = false;
|
||||
bool is_popup = false;
|
||||
bool mpass = false;
|
||||
bool focused = false;
|
||||
bool is_visible = true;
|
||||
bool extend_to_title = false;
|
||||
bool hide_from_capture = false;
|
||||
|
||||
Rect2i parent_safe_rect;
|
||||
};
|
||||
|
||||
List<WindowID> popup_list;
|
||||
uint64_t time_since_popup = 0;
|
||||
|
||||
private:
|
||||
#if defined(GLES3_ENABLED)
|
||||
GLManagerLegacy_MacOS *gl_manager_legacy = nullptr;
|
||||
GLManagerANGLE_MacOS *gl_manager_angle = nullptr;
|
||||
#endif
|
||||
#if defined(RD_ENABLED)
|
||||
RenderingContextDriver *rendering_context = nullptr;
|
||||
RenderingDevice *rendering_device = nullptr;
|
||||
#endif
|
||||
String rendering_driver;
|
||||
|
||||
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 menu_delegate = nullptr;
|
||||
NativeMenuMacOS *native_menu = nullptr;
|
||||
|
||||
Point2i im_selection;
|
||||
String im_text;
|
||||
|
||||
CGEventSourceRef event_source;
|
||||
MouseMode mouse_mode = MOUSE_MODE_VISIBLE;
|
||||
MouseMode mouse_mode_base = MOUSE_MODE_VISIBLE;
|
||||
MouseMode mouse_mode_override = MOUSE_MODE_VISIBLE;
|
||||
bool mouse_mode_override_enabled = false;
|
||||
void _mouse_update_mode();
|
||||
|
||||
bool drop_events = false;
|
||||
bool in_dispatch_input_event = false;
|
||||
|
||||
WindowID window_mouseover_id = INVALID_WINDOW_ID;
|
||||
WindowID last_focused_window = INVALID_WINDOW_ID;
|
||||
WindowID window_id_counter = MAIN_WINDOW_ID;
|
||||
float display_max_scale = 1.f;
|
||||
mutable Point2i origin;
|
||||
mutable 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;
|
||||
|
||||
struct IndicatorData {
|
||||
id delegate;
|
||||
id item;
|
||||
};
|
||||
|
||||
IndicatorID indicator_id_counter = 0;
|
||||
HashMap<IndicatorID, IndicatorData> indicators;
|
||||
|
||||
IOPMAssertionID screen_keep_on_assertion = kIOPMNullAssertionID;
|
||||
|
||||
Callable help_search_callback;
|
||||
Callable help_action_callback;
|
||||
|
||||
struct MenuCall {
|
||||
Variant tag;
|
||||
Callable callback;
|
||||
};
|
||||
List<MenuCall> deferred_menu_calls;
|
||||
|
||||
Callable system_theme_changed;
|
||||
|
||||
WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, const Rect2i &p_rect);
|
||||
void _update_window_style(WindowData p_wd, WindowID p_window);
|
||||
|
||||
void _update_displays_arrangement() 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();
|
||||
|
||||
static NSCursor *_cursor_from_selector(SEL p_selector, SEL p_fallback = nil);
|
||||
|
||||
Error _file_dialog_with_options_show(const String &p_title, const String &p_current_directory, const String &p_root, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const TypedArray<Dictionary> &p_options, const Callable &p_callback, bool p_options_in_cb, WindowID p_window_id);
|
||||
|
||||
struct EmbeddedProcessData {
|
||||
EmbeddedProcessMacOS *process;
|
||||
WindowData *wd = nullptr;
|
||||
CALayer *layer_host = nil;
|
||||
};
|
||||
HashMap<OS::ProcessID, EmbeddedProcessData> embedded_processes;
|
||||
void _window_update_display_id(WindowData *p_wd);
|
||||
|
||||
public:
|
||||
void menu_callback(id p_sender);
|
||||
|
||||
void emit_system_theme_changed();
|
||||
|
||||
bool has_window(WindowID p_window) const;
|
||||
WindowData &get_window(WindowID p_window);
|
||||
|
||||
NSImage *_convert_to_nsimg(Ref<Image> &p_image) const;
|
||||
Point2i _get_screens_origin() const;
|
||||
|
||||
void set_menu_delegate(NSMenu *p_menu);
|
||||
|
||||
void send_event(NSEvent *p_event);
|
||||
void send_window_event(const WindowData &p_wd, WindowEvent p_event);
|
||||
void release_pressed_events();
|
||||
void sync_mouse_state();
|
||||
void get_key_modifier_state(unsigned int p_macos_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 pop_last_key_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 reparent_check(WindowID p_window);
|
||||
WindowID _get_focused_window_or_popup() const;
|
||||
void mouse_enter_window(WindowID p_window);
|
||||
void mouse_exit_window(WindowID p_window);
|
||||
void update_presentation_mode();
|
||||
|
||||
bool is_always_on_top_recursive(WindowID p_window) const;
|
||||
|
||||
/**
|
||||
* Get the display ID of a window.
|
||||
*/
|
||||
uint32_t window_get_display_id(WindowID p_window) const;
|
||||
void window_destroy(WindowID p_window);
|
||||
void window_resize(WindowID p_window, int p_width, int p_height);
|
||||
void window_set_custom_window_buttons(WindowData &p_wd, bool p_enabled);
|
||||
void set_window_per_pixel_transparency_enabled(bool p_enabled, WindowID p_window);
|
||||
|
||||
virtual bool has_feature(Feature p_feature) const override;
|
||||
virtual String get_name() const override;
|
||||
|
||||
virtual void help_set_search_callbacks(const Callable &p_search_callback = Callable(), const Callable &p_action_callback = Callable()) override;
|
||||
Callable _help_get_search_callback() const;
|
||||
Callable _help_get_action_callback() const;
|
||||
|
||||
virtual bool is_dark_mode_supported() const override;
|
||||
virtual bool is_dark_mode() const override;
|
||||
virtual Color get_accent_color() const override;
|
||||
virtual Color get_base_color() const override;
|
||||
virtual void set_system_theme_change_callback(const Callable &p_callable) 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 Error file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback, WindowID p_window_id) override;
|
||||
virtual Error file_dialog_with_options_show(const String &p_title, const String &p_current_directory, const String &p_root, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const TypedArray<Dictionary> &p_options, const Callable &p_callback, WindowID p_window_id) override;
|
||||
|
||||
virtual void beep() const override;
|
||||
|
||||
virtual void mouse_set_mode(MouseMode p_mode) override;
|
||||
virtual MouseMode mouse_get_mode() const override;
|
||||
virtual void mouse_set_mode_override(MouseMode p_mode) override;
|
||||
virtual MouseMode mouse_get_mode_override() const override;
|
||||
virtual void mouse_set_mode_override_enabled(bool p_override_enabled) override;
|
||||
virtual bool mouse_is_mode_override_enabled() 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;
|
||||
virtual BitField<MouseButtonMask> mouse_get_button_state() const override;
|
||||
|
||||
virtual int get_screen_count() const override;
|
||||
virtual int get_primary_screen() const override;
|
||||
virtual int get_keyboard_focus_screen() const override;
|
||||
virtual Point2i screen_get_position(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
|
||||
virtual Size2i screen_get_size(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
|
||||
virtual 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 Color screen_get_pixel(const Point2i &p_position) const override;
|
||||
virtual Ref<Image> screen_get_image(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
|
||||
virtual Ref<Image> screen_get_image_rect(const Rect2i &p_rect) const override;
|
||||
virtual void screen_set_keep_on(bool p_enable) override;
|
||||
virtual bool screen_is_kept_on() 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(), bool p_exclusive = false, WindowID p_transient_parent = INVALID_WINDOW_ID) 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 Size2i window_get_title_size(const String &p_title, WindowID p_window) const 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 Point2i window_get_position_with_decorations(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
virtual void window_set_position(const Point2i &p_position, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
|
||||
virtual void window_set_transient(WindowID p_window, WindowID p_parent) override;
|
||||
virtual void window_set_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_size_with_decorations(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual void window_set_mode(WindowMode p_mode, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual WindowMode window_get_mode(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual bool window_is_maximize_allowed(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual void window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual bool window_get_flag(WindowFlags p_flag, WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual void window_request_attention(WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual void window_move_to_foreground(WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual bool window_is_focused(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual WindowID get_focused_window() const override;
|
||||
|
||||
virtual bool window_can_draw(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual bool can_any_window_draw() const override;
|
||||
|
||||
virtual void window_set_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 bool window_maximize_on_title_dbl_click() const override;
|
||||
virtual bool window_minimize_on_title_dbl_click() const override;
|
||||
|
||||
virtual void window_start_drag(WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual void window_start_resize(WindowResizeEdge p_edge, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
|
||||
virtual void window_set_window_buttons_offset(const Vector2i &p_offset, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual Vector3i window_get_safe_title_margins(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual int accessibility_should_increase_contrast() const override;
|
||||
virtual int accessibility_should_reduce_animation() const override;
|
||||
virtual int accessibility_should_reduce_transparency() const override;
|
||||
virtual int accessibility_screen_reader_active() 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 void enable_for_stealing_focus(OS::ProcessID pid) override;
|
||||
#ifdef TOOLS_ENABLED
|
||||
Error embed_process_update(WindowID p_window, EmbeddedProcessMacOS *p_process);
|
||||
#endif
|
||||
virtual Error request_close_embedded_process(OS::ProcessID p_pid) override;
|
||||
virtual Error remove_embedded_process(OS::ProcessID p_pid) override;
|
||||
|
||||
void _process_events(bool p_pump);
|
||||
virtual void process_events() override;
|
||||
virtual void force_process_and_drop_events() override;
|
||||
|
||||
virtual void release_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;
|
||||
|
||||
virtual IndicatorID create_status_indicator(const Ref<Texture2D> &p_icon, const String &p_tooltip, const Callable &p_callback) override;
|
||||
virtual void status_indicator_set_icon(IndicatorID p_id, const Ref<Texture2D> &p_icon) override;
|
||||
virtual void status_indicator_set_tooltip(IndicatorID p_id, const String &p_tooltip) override;
|
||||
virtual void status_indicator_set_menu(IndicatorID p_id, const RID &p_menu_rid) override;
|
||||
virtual void status_indicator_set_callback(IndicatorID p_id, const Callable &p_callback) override;
|
||||
virtual Rect2 status_indicator_get_rect(IndicatorID p_id) const override;
|
||||
virtual void delete_status_indicator(IndicatorID p_id) override;
|
||||
|
||||
virtual bool is_window_transparency_available() const override;
|
||||
|
||||
static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error);
|
||||
static Vector<String> get_rendering_drivers_func();
|
||||
|
||||
static void register_macos_driver();
|
||||
|
||||
DisplayServerMacOS(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error);
|
||||
~DisplayServerMacOS();
|
||||
};
|
4028
platform/macos/display_server_macos.mm
Normal file
4028
platform/macos/display_server_macos.mm
Normal file
File diff suppressed because it is too large
Load Diff
90
platform/macos/display_server_macos_base.h
Normal file
90
platform/macos/display_server_macos_base.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/**************************************************************************/
|
||||
/* display_server_macos_base.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/input/input.h"
|
||||
#include "servers/display_server.h"
|
||||
|
||||
#define FontVariation __FontVariation
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
#undef FontVariation
|
||||
|
||||
class DisplayServerMacOSBase : public DisplayServer {
|
||||
GDSOFTCLASS(DisplayServerMacOSBase, DisplayServer)
|
||||
|
||||
id tts = nullptr;
|
||||
|
||||
struct LayoutInfo {
|
||||
String name;
|
||||
String code;
|
||||
};
|
||||
mutable Vector<LayoutInfo> kbd_layouts;
|
||||
mutable int current_layout = 0;
|
||||
mutable bool keyboard_layout_dirty = true;
|
||||
|
||||
protected:
|
||||
_THREAD_SAFE_CLASS_
|
||||
|
||||
void initialize_tts() const;
|
||||
|
||||
void _update_keyboard_layouts() const;
|
||||
static void _keyboard_layout_changed(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef user_info);
|
||||
|
||||
public:
|
||||
virtual void clipboard_set(const String &p_text) override;
|
||||
virtual String clipboard_get() const override;
|
||||
virtual Ref<Image> clipboard_get_image() const override;
|
||||
virtual bool clipboard_has() const override;
|
||||
virtual bool clipboard_has_image() const 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 Key keyboard_get_label_from_physical(Key p_keycode) const override;
|
||||
virtual void show_emoji_and_symbol_picker() const override;
|
||||
|
||||
virtual bool tts_is_speaking() const override;
|
||||
virtual bool tts_is_paused() const override;
|
||||
virtual TypedArray<Dictionary> tts_get_voices() const override;
|
||||
|
||||
virtual void tts_speak(const String &p_text, const String &p_voice, int p_volume = 50, float p_pitch = 1.f, float p_rate = 1.f, int p_utterance_id = 0, bool p_interrupt = false) override;
|
||||
virtual void tts_pause() override;
|
||||
virtual void tts_resume() override;
|
||||
virtual void tts_stop() override;
|
||||
|
||||
DisplayServerMacOSBase();
|
||||
~DisplayServerMacOSBase();
|
||||
};
|
320
platform/macos/display_server_macos_base.mm
Normal file
320
platform/macos/display_server_macos_base.mm
Normal file
@@ -0,0 +1,320 @@
|
||||
/**************************************************************************/
|
||||
/* display_server_macos_base.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "display_server_macos_base.h"
|
||||
#import "key_mapping_macos.h"
|
||||
#import "tts_macos.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "drivers/png/png_driver_common.h"
|
||||
|
||||
#import <Carbon/Carbon.h>
|
||||
|
||||
void DisplayServerMacOSBase::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 DisplayServerMacOSBase::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.append_utf8([string UTF8String]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Ref<Image> DisplayServerMacOSBase::clipboard_get_image() const {
|
||||
Ref<Image> image;
|
||||
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
|
||||
NSString *result = [pasteboard availableTypeFromArray:[NSArray arrayWithObjects:NSPasteboardTypeTIFF, NSPasteboardTypePNG, nil]];
|
||||
if (!result) {
|
||||
return image;
|
||||
}
|
||||
NSData *data = [pasteboard dataForType:result];
|
||||
if (!data) {
|
||||
return image;
|
||||
}
|
||||
NSBitmapImageRep *bitmap = [NSBitmapImageRep imageRepWithData:data];
|
||||
NSData *pngData = [bitmap representationUsingType:NSBitmapImageFileTypePNG properties:@{}];
|
||||
image.instantiate();
|
||||
PNGDriverCommon::png_to_image((const uint8_t *)pngData.bytes, pngData.length, false, image);
|
||||
return image;
|
||||
}
|
||||
|
||||
bool DisplayServerMacOSBase::clipboard_has() const {
|
||||
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
|
||||
NSArray *classArray = [NSArray arrayWithObject:[NSString class]];
|
||||
NSDictionary *options = [NSDictionary dictionary];
|
||||
return [pasteboard canReadObjectForClasses:classArray options:options];
|
||||
}
|
||||
|
||||
bool DisplayServerMacOSBase::clipboard_has_image() const {
|
||||
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
|
||||
NSString *result = [pasteboard availableTypeFromArray:[NSArray arrayWithObjects:NSPasteboardTypeTIFF, NSPasteboardTypePNG, nil]];
|
||||
return result;
|
||||
}
|
||||
|
||||
void DisplayServerMacOSBase::initialize_tts() const {
|
||||
const_cast<DisplayServerMacOSBase *>(this)->tts = [[TTS_MacOS alloc] init];
|
||||
}
|
||||
|
||||
bool DisplayServerMacOSBase::tts_is_speaking() const {
|
||||
if (unlikely(!tts)) {
|
||||
initialize_tts();
|
||||
}
|
||||
ERR_FAIL_NULL_V(tts, false);
|
||||
return [tts isSpeaking];
|
||||
}
|
||||
|
||||
bool DisplayServerMacOSBase::tts_is_paused() const {
|
||||
if (unlikely(!tts)) {
|
||||
initialize_tts();
|
||||
}
|
||||
ERR_FAIL_NULL_V(tts, false);
|
||||
return [tts isPaused];
|
||||
}
|
||||
|
||||
TypedArray<Dictionary> DisplayServerMacOSBase::tts_get_voices() const {
|
||||
if (unlikely(!tts)) {
|
||||
initialize_tts();
|
||||
}
|
||||
ERR_FAIL_NULL_V(tts, TypedArray<Dictionary>());
|
||||
return [tts getVoices];
|
||||
}
|
||||
|
||||
void DisplayServerMacOSBase::tts_speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int p_utterance_id, bool p_interrupt) {
|
||||
if (unlikely(!tts)) {
|
||||
initialize_tts();
|
||||
}
|
||||
ERR_FAIL_NULL(tts);
|
||||
[tts speak:p_text voice:p_voice volume:p_volume pitch:p_pitch rate:p_rate utterance_id:p_utterance_id interrupt:p_interrupt];
|
||||
}
|
||||
|
||||
void DisplayServerMacOSBase::tts_pause() {
|
||||
if (unlikely(!tts)) {
|
||||
initialize_tts();
|
||||
}
|
||||
ERR_FAIL_NULL(tts);
|
||||
[tts pauseSpeaking];
|
||||
}
|
||||
|
||||
void DisplayServerMacOSBase::tts_resume() {
|
||||
if (unlikely(!tts)) {
|
||||
initialize_tts();
|
||||
}
|
||||
ERR_FAIL_NULL(tts);
|
||||
[tts resumeSpeaking];
|
||||
}
|
||||
|
||||
void DisplayServerMacOSBase::tts_stop() {
|
||||
if (unlikely(!tts)) {
|
||||
initialize_tts();
|
||||
}
|
||||
ERR_FAIL_NULL(tts);
|
||||
[tts stopSpeaking];
|
||||
}
|
||||
|
||||
void DisplayServerMacOSBase::_update_keyboard_layouts() const {
|
||||
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.append_utf8([name UTF8String]);
|
||||
|
||||
NSArray *langs = (__bridge NSArray *)TISGetInputSourceProperty((__bridge TISInputSourceRef)[list_ime objectAtIndex:i], kTISPropertyInputSourceLanguages);
|
||||
ly.code.append_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.append_utf8([name UTF8String]);
|
||||
|
||||
NSArray *langs = (__bridge NSArray *)TISGetInputSourceProperty((__bridge TISInputSourceRef)[list_kbd objectAtIndex:i], kTISPropertyInputSourceLanguages);
|
||||
ly.code.append_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 DisplayServerMacOSBase::_keyboard_layout_changed(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef user_info) {
|
||||
DisplayServerMacOSBase *ds = (DisplayServerMacOSBase *)DisplayServer::get_singleton();
|
||||
if (ds) {
|
||||
ds->keyboard_layout_dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
int DisplayServerMacOSBase::keyboard_get_layout_count() const {
|
||||
if (keyboard_layout_dirty) {
|
||||
_update_keyboard_layouts();
|
||||
}
|
||||
return kbd_layouts.size();
|
||||
}
|
||||
|
||||
void DisplayServerMacOSBase::keyboard_set_current_layout(int p_index) {
|
||||
if (keyboard_layout_dirty) {
|
||||
_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 DisplayServerMacOSBase::keyboard_get_current_layout() const {
|
||||
if (keyboard_layout_dirty) {
|
||||
_update_keyboard_layouts();
|
||||
}
|
||||
|
||||
return current_layout;
|
||||
}
|
||||
|
||||
String DisplayServerMacOSBase::keyboard_get_layout_language(int p_index) const {
|
||||
if (keyboard_layout_dirty) {
|
||||
_update_keyboard_layouts();
|
||||
}
|
||||
|
||||
ERR_FAIL_INDEX_V(p_index, kbd_layouts.size(), "");
|
||||
return kbd_layouts[p_index].code;
|
||||
}
|
||||
|
||||
String DisplayServerMacOSBase::keyboard_get_layout_name(int p_index) const {
|
||||
if (keyboard_layout_dirty) {
|
||||
_update_keyboard_layouts();
|
||||
}
|
||||
|
||||
ERR_FAIL_INDEX_V(p_index, kbd_layouts.size(), "");
|
||||
return kbd_layouts[p_index].name;
|
||||
}
|
||||
|
||||
Key DisplayServerMacOSBase::keyboard_get_keycode_from_physical(Key p_keycode) const {
|
||||
if (p_keycode == Key::PAUSE || p_keycode == Key::NONE) {
|
||||
return p_keycode;
|
||||
}
|
||||
|
||||
Key modifiers = p_keycode & KeyModifierMask::MODIFIER_MASK;
|
||||
Key keycode_no_mod = p_keycode & KeyModifierMask::CODE_MASK;
|
||||
unsigned int macos_keycode = KeyMappingMacOS::unmap_key(keycode_no_mod);
|
||||
return (Key)(KeyMappingMacOS::remap_key(macos_keycode, 0, false) | modifiers);
|
||||
}
|
||||
|
||||
Key DisplayServerMacOSBase::keyboard_get_label_from_physical(Key p_keycode) const {
|
||||
if (p_keycode == Key::PAUSE || p_keycode == Key::NONE) {
|
||||
return p_keycode;
|
||||
}
|
||||
|
||||
Key modifiers = p_keycode & KeyModifierMask::MODIFIER_MASK;
|
||||
Key keycode_no_mod = p_keycode & KeyModifierMask::CODE_MASK;
|
||||
unsigned int macos_keycode = KeyMappingMacOS::unmap_key(keycode_no_mod);
|
||||
return (Key)(KeyMappingMacOS::remap_key(macos_keycode, 0, true) | modifiers);
|
||||
}
|
||||
|
||||
void DisplayServerMacOSBase::show_emoji_and_symbol_picker() const {
|
||||
[[NSApplication sharedApplication] orderFrontCharacterPalette:nil];
|
||||
}
|
||||
|
||||
DisplayServerMacOSBase::DisplayServerMacOSBase() {
|
||||
// Init TTS
|
||||
bool tts_enabled = GLOBAL_GET("audio/general/text_to_speech");
|
||||
if (tts_enabled) {
|
||||
initialize_tts();
|
||||
}
|
||||
|
||||
// Register to be notified on keyboard layout changes.
|
||||
CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(),
|
||||
nullptr, _keyboard_layout_changed,
|
||||
kTISNotifySelectedKeyboardInputSourceChanged, nullptr,
|
||||
CFNotificationSuspensionBehaviorDeliverImmediately);
|
||||
}
|
||||
|
||||
DisplayServerMacOSBase::~DisplayServerMacOSBase() {
|
||||
CFNotificationCenterRemoveObserver(CFNotificationCenterGetDistributedCenter(), nullptr, kTISNotifySelectedKeyboardInputSourceChanged, nullptr);
|
||||
}
|
753
platform/macos/doc_classes/EditorExportPlatformMacOS.xml
Normal file
753
platform/macos/doc_classes/EditorExportPlatformMacOS.xml
Normal file
@@ -0,0 +1,753 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="EditorExportPlatformMacOS" inherits="EditorExportPlatform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
|
||||
<brief_description>
|
||||
Exporter for macOS.
|
||||
</brief_description>
|
||||
<description>
|
||||
</description>
|
||||
<tutorials>
|
||||
<link title="Exporting for macOS">$DOCS_URL/tutorials/export/exporting_for_macos.html</link>
|
||||
<link title="Running Godot apps on macOS">$DOCS_URL/tutorials//export/running_on_macos.html</link>
|
||||
</tutorials>
|
||||
<members>
|
||||
<member name="application/additional_plist_content" type="String" setter="" getter="">
|
||||
Additional data added to the root [code]<dict>[/code] section of the [url=https://developer.apple.com/documentation/bundleresources/information_property_list]Info.plist[/url] file. The value should be an XML section with pairs of key-value elements, e.g.:
|
||||
[codeblock lang=text]
|
||||
<key>key_name</key>
|
||||
<string>value</string>
|
||||
[/codeblock]
|
||||
</member>
|
||||
<member name="application/app_category" type="String" setter="" getter="">
|
||||
Application category for the App Store.
|
||||
</member>
|
||||
<member name="application/bundle_identifier" type="String" setter="" getter="">
|
||||
Unique application identifier in a reverse-DNS format, can only contain alphanumeric characters ([code]A-Z[/code], [code]a-z[/code], and [code]0-9[/code]), hyphens ([code]-[/code]), and periods ([code].[/code]).
|
||||
</member>
|
||||
<member name="application/copyright" type="String" setter="" getter="">
|
||||
Copyright notice for the bundle visible to the user (in English).
|
||||
</member>
|
||||
<member name="application/copyright_localized" type="Dictionary" setter="" getter="">
|
||||
Copyright notice for the bundle visible to the user (localized).
|
||||
</member>
|
||||
<member name="application/export_angle" type="int" setter="" getter="">
|
||||
If set to [code]1[/code], ANGLE libraries are exported with the exported application. If set to [code]0[/code], ANGLE libraries are exported only if [member ProjectSettings.rendering/gl_compatibility/driver] is set to [code]"opengl3_angle"[/code].
|
||||
</member>
|
||||
<member name="application/icon" type="String" setter="" getter="">
|
||||
Application icon file. If left empty, it will fallback to [member ProjectSettings.application/config/macos_native_icon], and then to [member ProjectSettings.application/config/icon].
|
||||
</member>
|
||||
<member name="application/icon_interpolation" type="int" setter="" getter="">
|
||||
Interpolation method used to resize application icon.
|
||||
</member>
|
||||
<member name="application/min_macos_version_arm64" type="String" setter="" getter="">
|
||||
Minimum version of macOS required for this application to run on Apple Silicon Macs, in the [code]major.minor.patch[/code] or [code]major.minor[/code] format, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]).
|
||||
</member>
|
||||
<member name="application/min_macos_version_x86_64" type="String" setter="" getter="">
|
||||
Minimum version of macOS required for this application to run on Intel Macs, in the [code]major.minor.patch[/code] or [code]major.minor[/code] format, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]).
|
||||
</member>
|
||||
<member name="application/short_version" type="String" setter="" getter="">
|
||||
Application version visible to the user. Can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]). Falls back to [member ProjectSettings.application/config/version] if left empty.
|
||||
[b]Note:[/b] This value is used for the [i]Identity > Version[/i] value in the generated Xcode project.
|
||||
</member>
|
||||
<member name="application/signature" type="String" setter="" getter="">
|
||||
A four-character creator code that is specific to the bundle. Optional.
|
||||
</member>
|
||||
<member name="application/version" type="String" setter="" getter="">
|
||||
Machine-readable application version in the [code]major.minor.patch[/code] format. Can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]). This must be incremented with every new release pushed to the App Store. Falls back to [member ProjectSettings.application/config/version] if left empty.
|
||||
[b]Note:[/b] This value is used for the [i]Identity > Build[/i] value in the generated Xcode project.
|
||||
</member>
|
||||
<member name="binary_format/architecture" type="String" setter="" getter="">
|
||||
Application executable architecture.
|
||||
Supported architectures: [code]x86_64[/code], [code]arm64[/code], and [code]universal[/code] ([code]x86_64 + arm64[/code]).
|
||||
Official export templates include [code]universal[/code] binaries only.
|
||||
</member>
|
||||
<member name="codesign/apple_team_id" type="String" setter="" getter="">
|
||||
Apple Team ID, unique 10-character string. To locate your Team ID check "Membership details" section in your Apple developer account dashboard, or "Organizational Unit" of your code signing certificate. See [url=https://developer.apple.com/help/account/manage-your-team/locate-your-team-id]Locate your Team ID[/url].
|
||||
</member>
|
||||
<member name="codesign/certificate_file" type="String" setter="" getter="">
|
||||
PKCS #12 certificate file used to sign [code].app[/code] bundle.
|
||||
Can be overridden with the environment variable [code]GODOT_MACOS_CODESIGN_CERTIFICATE_FILE[/code].
|
||||
</member>
|
||||
<member name="codesign/certificate_password" type="String" setter="" getter="">
|
||||
Password for the certificate file used to sign [code].app[/code] bundle.
|
||||
Can be overridden with the environment variable [code]GODOT_MACOS_CODESIGN_CERTIFICATE_PASSWORD[/code].
|
||||
</member>
|
||||
<member name="codesign/codesign" type="int" setter="" getter="">
|
||||
Tool to use for code signing.
|
||||
</member>
|
||||
<member name="codesign/custom_options" type="PackedStringArray" setter="" getter="">
|
||||
Array of the additional command line arguments passed to the code signing tool.
|
||||
</member>
|
||||
<member name="codesign/entitlements/additional" type="String" setter="" getter="">
|
||||
Additional data added to the root [code]<dict>[/code] section of the [url=https://developer.apple.com/documentation/bundleresources/entitlements].entitlements[/url] file. The value should be an XML section with pairs of key-value elements, for example:
|
||||
[codeblock lang=text]
|
||||
<key>key_name</key>
|
||||
<string>value</string>
|
||||
[/codeblock]
|
||||
</member>
|
||||
<member name="codesign/entitlements/address_book" type="bool" setter="" getter="">
|
||||
Enable to allow access to contacts in the user's address book, if it's enabled you should also provide usage message in the [member privacy/address_book_usage_description] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_personal-information_addressbook]com.apple.security.personal-information.addressbook[/url].
|
||||
</member>
|
||||
<member name="codesign/entitlements/allow_dyld_environment_variables" type="bool" setter="" getter="">
|
||||
Allows app to use dynamic linker environment variables to inject code. If you are using add-ons with dynamic or self-modifying native code, enable them according to the add-on documentation. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_cs_allow-dyld-environment-variables]com.apple.security.cs.allow-dyld-environment-variables[/url].
|
||||
</member>
|
||||
<member name="codesign/entitlements/allow_jit_code_execution" type="bool" setter="" getter="">
|
||||
Allows creating writable and executable memory for JIT code. If you are using add-ons with dynamic or self-modifying native code, enable them according to the add-on documentation. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_cs_allow-jit]com.apple.security.cs.allow-jit[/url].
|
||||
</member>
|
||||
<member name="codesign/entitlements/allow_unsigned_executable_memory" type="bool" setter="" getter="">
|
||||
Allows creating writable and executable memory without JIT restrictions. If you are using add-ons with dynamic or self-modifying native code, enable them according to the add-on documentation. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_cs_allow-unsigned-executable-memory]com.apple.security.cs.allow-unsigned-executable-memory[/url].
|
||||
</member>
|
||||
<member name="codesign/entitlements/app_sandbox/device_bluetooth" type="bool" setter="" getter="">
|
||||
Enable to allow app to interact with Bluetooth devices. This entitlement is required to use wireless controllers. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_device_bluetooth]com.apple.security.device.bluetooth[/url].
|
||||
</member>
|
||||
<member name="codesign/entitlements/app_sandbox/device_usb" type="bool" setter="" getter="">
|
||||
Enable to allow app to interact with USB devices. This entitlement is required to use wired controllers. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_device_usb]com.apple.security.device.usb[/url].
|
||||
</member>
|
||||
<member name="codesign/entitlements/app_sandbox/enabled" type="bool" setter="" getter="">
|
||||
Enables App Sandbox. The App Sandbox restricts access to user data, networking, and devices. Sandboxed apps can't access most of the file system, can't use custom file dialogs and execute binaries outside the .app bundle. See [url=https://developer.apple.com/documentation/security/app_sandbox]App Sandbox[/url].
|
||||
[b]Note:[/b] To distribute an app through the App Store, you must enable the App Sandbox.
|
||||
</member>
|
||||
<member name="codesign/entitlements/app_sandbox/files_downloads" type="int" setter="" getter="">
|
||||
Allows read or write access to the user's "Downloads" folder. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_files_downloads_read-write]com.apple.security.files.downloads.read-write[/url].
|
||||
</member>
|
||||
<member name="codesign/entitlements/app_sandbox/files_movies" type="int" setter="" getter="">
|
||||
Allows read or write access to the user's "Movies" folder. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_assets_movies_read-write]com.apple.security.files.movies.read-write[/url].
|
||||
</member>
|
||||
<member name="codesign/entitlements/app_sandbox/files_music" type="int" setter="" getter="">
|
||||
Allows read or write access to the user's "Music" folder. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_assets_music_read-write]com.apple.security.files.music.read-write[/url].
|
||||
</member>
|
||||
<member name="codesign/entitlements/app_sandbox/files_pictures" type="int" setter="" getter="">
|
||||
Allows read or write access to the user's "Pictures" folder. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_assets_pictures_read-write]com.apple.security.files.pictures.read-write[/url].
|
||||
</member>
|
||||
<member name="codesign/entitlements/app_sandbox/files_user_selected" type="int" setter="" getter="">
|
||||
Allows read or write access to the locations the user has selected using a native file dialog. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_files_user-selected_read-write]com.apple.security.files.user-selected.read-write[/url].
|
||||
</member>
|
||||
<member name="codesign/entitlements/app_sandbox/helper_executables" type="Array" setter="" getter="">
|
||||
List of helper executables to embedded to the app bundle. Sandboxed app are limited to execute only these executable. See [url=https://developer.apple.com/documentation/xcode/embedding-a-helper-tool-in-a-sandboxed-app]Embedding a command-line tool in a sandboxed app[/url].
|
||||
</member>
|
||||
<member name="codesign/entitlements/app_sandbox/network_client" type="bool" setter="" getter="">
|
||||
Enable to allow app to establish outgoing network connections. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_network_client]com.apple.security.network.client[/url].
|
||||
</member>
|
||||
<member name="codesign/entitlements/app_sandbox/network_server" type="bool" setter="" getter="">
|
||||
Enable to allow app to listen for incoming network connections. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_network_server]com.apple.security.network.server[/url].
|
||||
</member>
|
||||
<member name="codesign/entitlements/apple_events" type="bool" setter="" getter="">
|
||||
Enable to allow app to send Apple events to other apps. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_automation_apple-events]com.apple.security.automation.apple-events[/url].
|
||||
</member>
|
||||
<member name="codesign/entitlements/audio_input" type="bool" setter="" getter="">
|
||||
Enable if you need to use the microphone or other audio input sources, if it's enabled you should also provide usage message in the [member privacy/microphone_usage_description] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_device_audio-input]com.apple.security.device.audio-input[/url].
|
||||
</member>
|
||||
<member name="codesign/entitlements/calendars" type="bool" setter="" getter="">
|
||||
Enable to allow access to the user's calendar, if it's enabled you should also provide usage message in the [member privacy/calendar_usage_description] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_personal-information_calendars]com.apple.security.personal-information.calendars[/url].
|
||||
</member>
|
||||
<member name="codesign/entitlements/camera" type="bool" setter="" getter="">
|
||||
Enable if you need to use the camera, if it's enabled you should also provide usage message in the [member privacy/camera_usage_description] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_device_camera]com.apple.security.device.camera[/url].
|
||||
</member>
|
||||
<member name="codesign/entitlements/custom_file" type="String" setter="" getter="">
|
||||
Custom entitlements [code].plist[/code] file, if specified the rest of entitlements in the export config are ignored.
|
||||
</member>
|
||||
<member name="codesign/entitlements/debugging" type="bool" setter="" getter="">
|
||||
You can temporarily enable this entitlement to use native debugger (GDB, LLDB) with the exported app. This entitlement should be disabled for production export. See [url=https://developer.apple.com/documentation/xcode/embedding-a-helper-tool-in-a-sandboxed-app]Embedding a command-line tool in a sandboxed app[/url].
|
||||
</member>
|
||||
<member name="codesign/entitlements/disable_library_validation" type="bool" setter="" getter="">
|
||||
Allows app to load arbitrary libraries and frameworks (not signed with the same Team ID as the main executable or by Apple). Enable it if you are using GDExtension add-ons or ad-hoc signing, or want to support user-provided external add-ons. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_cs_disable-library-validation]com.apple.security.cs.disable-library-validation[/url].
|
||||
</member>
|
||||
<member name="codesign/entitlements/location" type="bool" setter="" getter="">
|
||||
Enable if you need to use location information from Location Services, if it's enabled you should also provide usage message in the [member privacy/location_usage_description] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_personal-information_location]com.apple.security.personal-information.location[/url].
|
||||
</member>
|
||||
<member name="codesign/entitlements/photos_library" type="bool" setter="" getter="">
|
||||
Enable to allow access to the user's Photos library, if it's enabled you should also provide usage message in the [member privacy/photos_library_usage_description] option. See [url=https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_personal-information_photos-library]com.apple.security.personal-information.photos-library[/url].
|
||||
</member>
|
||||
<member name="codesign/identity" type="String" setter="" getter="">
|
||||
The "Full Name", "Common Name" or SHA-1 hash of the signing identity used to sign [code].app[/code] bundle.
|
||||
</member>
|
||||
<member name="codesign/installer_identity" type="String" setter="" getter="">
|
||||
The "Full Name", "Common Name" or SHA-1 hash of the signing identity used to sign [code].pkg[/code] installer package for App Store distribution, use [code]3rd Party Mac Developer Installer: Name.[/code] identity.
|
||||
</member>
|
||||
<member name="codesign/provisioning_profile" type="String" setter="" getter="">
|
||||
Provisioning profile file downloaded from Apple developer account dashboard. See [url=https://developer.apple.com/help/account/manage-profiles/edit-download-or-delete-profiles]Edit, download, or delete provisioning profiles[/url].
|
||||
Can be overridden with the environment variable [code]GODOT_MACOS_CODESIGN_PROVISIONING_PROFILE[/code].
|
||||
</member>
|
||||
<member name="custom_template/debug" type="String" setter="" getter="">
|
||||
Path to the custom export template. If left empty, default template is used.
|
||||
</member>
|
||||
<member name="custom_template/release" type="String" setter="" getter="">
|
||||
Path to the custom export template. If left empty, default template is used.
|
||||
</member>
|
||||
<member name="debug/export_console_wrapper" type="int" setter="" getter="">
|
||||
If enabled, a wrapper that can be used to run the application with console output is created alongside the exported application.
|
||||
</member>
|
||||
<member name="display/high_res" type="bool" setter="" getter="">
|
||||
If [code]true[/code], the application is rendered at native display resolution, otherwise it is always rendered at loDPI resolution and upscaled by OS when required.
|
||||
</member>
|
||||
<member name="export/distribution_type" type="int" setter="" getter="">
|
||||
Application distribution target.
|
||||
</member>
|
||||
<member name="notarization/api_key" type="String" setter="" getter="">
|
||||
Apple App Store Connect API issuer key file.
|
||||
Can be overridden with the environment variable [code]GODOT_MACOS_NOTARIZATION_API_KEY[/code].
|
||||
</member>
|
||||
<member name="notarization/api_key_id" type="String" setter="" getter="">
|
||||
Apple App Store Connect API issuer key ID.
|
||||
Can be overridden with the environment variable [code]GODOT_MACOS_NOTARIZATION_API_KEY_ID[/code].
|
||||
</member>
|
||||
<member name="notarization/api_uuid" type="String" setter="" getter="">
|
||||
Apple App Store Connect API issuer UUID.
|
||||
Can be overridden with the environment variable [code]GODOT_MACOS_NOTARIZATION_API_UUID[/code].
|
||||
</member>
|
||||
<member name="notarization/apple_id_name" type="String" setter="" getter="">
|
||||
Apple ID account name (email address).
|
||||
Can be overridden with the environment variable [code]GODOT_MACOS_NOTARIZATION_APPLE_ID_NAME[/code].
|
||||
</member>
|
||||
<member name="notarization/apple_id_password" type="String" setter="" getter="">
|
||||
Apple ID app-specific password.
|
||||
Can be overridden with the environment variable [code]GODOT_MACOS_NOTARIZATION_APPLE_ID_PASSWORD[/code].
|
||||
</member>
|
||||
<member name="notarization/notarization" type="int" setter="" getter="">
|
||||
Tool to use for notarization.
|
||||
</member>
|
||||
<member name="privacy/address_book_usage_description" type="String" setter="" getter="">
|
||||
A message displayed when requesting access to the user's contacts (in English).
|
||||
</member>
|
||||
<member name="privacy/address_book_usage_description_localized" type="Dictionary" setter="" getter="">
|
||||
A message displayed when requesting access to the user's contacts (localized).
|
||||
</member>
|
||||
<member name="privacy/calendar_usage_description" type="String" setter="" getter="">
|
||||
A message displayed when requesting access to the user's calendar data (in English).
|
||||
</member>
|
||||
<member name="privacy/calendar_usage_description_localized" type="Dictionary" setter="" getter="">
|
||||
A message displayed when requesting access to the user's calendar data (localized).
|
||||
</member>
|
||||
<member name="privacy/camera_usage_description" type="String" setter="" getter="">
|
||||
A message displayed when requesting access to the device's camera (in English).
|
||||
</member>
|
||||
<member name="privacy/camera_usage_description_localized" type="Dictionary" setter="" getter="">
|
||||
A message displayed when requesting access to the device's camera (localized).
|
||||
</member>
|
||||
<member name="privacy/collected_data/advertising_data/collected" type="bool" setter="" getter="">
|
||||
Indicates whether your app collects advertising data.
|
||||
</member>
|
||||
<member name="privacy/collected_data/advertising_data/collection_purposes" type="int" setter="" getter="">
|
||||
The reasons your app collects advertising data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
|
||||
</member>
|
||||
<member name="privacy/collected_data/advertising_data/linked_to_user" type="bool" setter="" getter="">
|
||||
Indicates whether your app links advertising data to the user's identity.
|
||||
</member>
|
||||
<member name="privacy/collected_data/advertising_data/used_for_tracking" type="bool" setter="" getter="">
|
||||
Indicates whether your app uses advertising data for tracking.
|
||||
</member>
|
||||
<member name="privacy/collected_data/audio_data/collected" type="bool" setter="" getter="">
|
||||
Indicates whether your app collects audio data.
|
||||
</member>
|
||||
<member name="privacy/collected_data/audio_data/collection_purposes" type="int" setter="" getter="">
|
||||
The reasons your app collects audio data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
|
||||
</member>
|
||||
<member name="privacy/collected_data/audio_data/linked_to_user" type="bool" setter="" getter="">
|
||||
Indicates whether your app links audio data to the user's identity.
|
||||
</member>
|
||||
<member name="privacy/collected_data/audio_data/used_for_tracking" type="bool" setter="" getter="">
|
||||
Indicates whether your app uses audio data for tracking.
|
||||
</member>
|
||||
<member name="privacy/collected_data/browsing_history/collected" type="bool" setter="" getter="">
|
||||
Indicates whether your app collects browsing history.
|
||||
</member>
|
||||
<member name="privacy/collected_data/browsing_history/collection_purposes" type="int" setter="" getter="">
|
||||
The reasons your app collects browsing history. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
|
||||
</member>
|
||||
<member name="privacy/collected_data/browsing_history/linked_to_user" type="bool" setter="" getter="">
|
||||
Indicates whether your app links browsing history to the user's identity.
|
||||
</member>
|
||||
<member name="privacy/collected_data/browsing_history/used_for_tracking" type="bool" setter="" getter="">
|
||||
Indicates whether your app uses browsing history for tracking.
|
||||
</member>
|
||||
<member name="privacy/collected_data/coarse_location/collected" type="bool" setter="" getter="">
|
||||
Indicates whether your app collects coarse location data.
|
||||
</member>
|
||||
<member name="privacy/collected_data/coarse_location/collection_purposes" type="int" setter="" getter="">
|
||||
The reasons your app collects coarse location data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
|
||||
</member>
|
||||
<member name="privacy/collected_data/coarse_location/linked_to_user" type="bool" setter="" getter="">
|
||||
Indicates whether your app links coarse location data to the user's identity.
|
||||
</member>
|
||||
<member name="privacy/collected_data/coarse_location/used_for_tracking" type="bool" setter="" getter="">
|
||||
Indicates whether your app uses coarse location data for tracking.
|
||||
</member>
|
||||
<member name="privacy/collected_data/contacts/collected" type="bool" setter="" getter="">
|
||||
Indicates whether your app collects contacts.
|
||||
</member>
|
||||
<member name="privacy/collected_data/contacts/collection_purposes" type="int" setter="" getter="">
|
||||
The reasons your app collects contacts. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
|
||||
</member>
|
||||
<member name="privacy/collected_data/contacts/linked_to_user" type="bool" setter="" getter="">
|
||||
Indicates whether your app links contacts to the user's identity.
|
||||
</member>
|
||||
<member name="privacy/collected_data/contacts/used_for_tracking" type="bool" setter="" getter="">
|
||||
Indicates whether your app uses contacts for tracking.
|
||||
</member>
|
||||
<member name="privacy/collected_data/crash_data/collected" type="bool" setter="" getter="">
|
||||
Indicates whether your app collects crash data.
|
||||
</member>
|
||||
<member name="privacy/collected_data/crash_data/collection_purposes" type="int" setter="" getter="">
|
||||
The reasons your app collects crash data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
|
||||
</member>
|
||||
<member name="privacy/collected_data/crash_data/linked_to_user" type="bool" setter="" getter="">
|
||||
Indicates whether your app links crash data to the user's identity.
|
||||
</member>
|
||||
<member name="privacy/collected_data/crash_data/used_for_tracking" type="bool" setter="" getter="">
|
||||
Indicates whether your app uses crash data for tracking.
|
||||
</member>
|
||||
<member name="privacy/collected_data/credit_info/collected" type="bool" setter="" getter="">
|
||||
Indicates whether your app collects credit information.
|
||||
</member>
|
||||
<member name="privacy/collected_data/credit_info/collection_purposes" type="int" setter="" getter="">
|
||||
The reasons your app collects credit information. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
|
||||
</member>
|
||||
<member name="privacy/collected_data/credit_info/linked_to_user" type="bool" setter="" getter="">
|
||||
Indicates whether your app links credit information to the user's identity.
|
||||
</member>
|
||||
<member name="privacy/collected_data/credit_info/used_for_tracking" type="bool" setter="" getter="">
|
||||
Indicates whether your app uses credit information for tracking.
|
||||
</member>
|
||||
<member name="privacy/collected_data/customer_support/collected" type="bool" setter="" getter="">
|
||||
Indicates whether your app collects customer support data.
|
||||
</member>
|
||||
<member name="privacy/collected_data/customer_support/collection_purposes" type="int" setter="" getter="">
|
||||
The reasons your app collects customer support data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
|
||||
</member>
|
||||
<member name="privacy/collected_data/customer_support/linked_to_user" type="bool" setter="" getter="">
|
||||
Indicates whether your app links customer support data to the user's identity.
|
||||
</member>
|
||||
<member name="privacy/collected_data/customer_support/used_for_tracking" type="bool" setter="" getter="">
|
||||
Indicates whether your app uses customer support data for tracking.
|
||||
</member>
|
||||
<member name="privacy/collected_data/device_id/collected" type="bool" setter="" getter="">
|
||||
Indicates whether your app collects device IDs.
|
||||
</member>
|
||||
<member name="privacy/collected_data/device_id/collection_purposes" type="int" setter="" getter="">
|
||||
The reasons your app collects device IDs. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
|
||||
</member>
|
||||
<member name="privacy/collected_data/device_id/linked_to_user" type="bool" setter="" getter="">
|
||||
Indicates whether your app links device IDs to the user's identity.
|
||||
</member>
|
||||
<member name="privacy/collected_data/device_id/used_for_tracking" type="bool" setter="" getter="">
|
||||
Indicates whether your app uses device IDs for tracking.
|
||||
</member>
|
||||
<member name="privacy/collected_data/email_address/collected" type="bool" setter="" getter="">
|
||||
Indicates whether your app collects email address.
|
||||
</member>
|
||||
<member name="privacy/collected_data/email_address/collection_purposes" type="int" setter="" getter="">
|
||||
The reasons your app collects email address. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
|
||||
</member>
|
||||
<member name="privacy/collected_data/email_address/linked_to_user" type="bool" setter="" getter="">
|
||||
Indicates whether your app links email address to the user's identity.
|
||||
</member>
|
||||
<member name="privacy/collected_data/email_address/used_for_tracking" type="bool" setter="" getter="">
|
||||
Indicates whether your app uses email address for tracking.
|
||||
</member>
|
||||
<member name="privacy/collected_data/emails_or_text_messages/collected" type="bool" setter="" getter="">
|
||||
Indicates whether your app collects emails or text messages.
|
||||
</member>
|
||||
<member name="privacy/collected_data/emails_or_text_messages/collection_purposes" type="int" setter="" getter="">
|
||||
The reasons your app collects emails or text messages. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
|
||||
</member>
|
||||
<member name="privacy/collected_data/emails_or_text_messages/linked_to_user" type="bool" setter="" getter="">
|
||||
Indicates whether your app links emails or text messages to the user's identity.
|
||||
</member>
|
||||
<member name="privacy/collected_data/emails_or_text_messages/used_for_tracking" type="bool" setter="" getter="">
|
||||
Indicates whether your app uses emails or text messages for tracking.
|
||||
</member>
|
||||
<member name="privacy/collected_data/environment_scanning/collected" type="bool" setter="" getter="">
|
||||
Indicates whether your app collects environment scanning data.
|
||||
</member>
|
||||
<member name="privacy/collected_data/environment_scanning/collection_purposes" type="int" setter="" getter="">
|
||||
The reasons your app collects environment scanning data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
|
||||
</member>
|
||||
<member name="privacy/collected_data/environment_scanning/linked_to_user" type="bool" setter="" getter="">
|
||||
Indicates whether your app links environment scanning data to the user's identity.
|
||||
</member>
|
||||
<member name="privacy/collected_data/environment_scanning/used_for_tracking" type="bool" setter="" getter="">
|
||||
Indicates whether your app uses environment scanning data for tracking.
|
||||
</member>
|
||||
<member name="privacy/collected_data/fitness/collected" type="bool" setter="" getter="">
|
||||
Indicates whether your app collects fitness and exercise data.
|
||||
</member>
|
||||
<member name="privacy/collected_data/fitness/collection_purposes" type="int" setter="" getter="">
|
||||
The reasons your app collects fitness and exercise data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
|
||||
</member>
|
||||
<member name="privacy/collected_data/fitness/linked_to_user" type="bool" setter="" getter="">
|
||||
Indicates whether your app links fitness and exercise data to the user's identity.
|
||||
</member>
|
||||
<member name="privacy/collected_data/fitness/used_for_tracking" type="bool" setter="" getter="">
|
||||
Indicates whether your app uses fitness and exercise data for tracking.
|
||||
</member>
|
||||
<member name="privacy/collected_data/gameplay_content/collected" type="bool" setter="" getter="">
|
||||
Indicates whether your app collects gameplay content.
|
||||
</member>
|
||||
<member name="privacy/collected_data/gameplay_content/collection_purposes" type="int" setter="" getter="">
|
||||
The reasons your app collects gameplay content. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
|
||||
</member>
|
||||
<member name="privacy/collected_data/gameplay_content/linked_to_user" type="bool" setter="" getter="">
|
||||
Indicates whether your app links gameplay content to the user's identity.
|
||||
</member>
|
||||
<member name="privacy/collected_data/gameplay_content/used_for_tracking" type="bool" setter="" getter="">
|
||||
Indicates whether your app uses gameplay content for tracking.
|
||||
</member>
|
||||
<member name="privacy/collected_data/hands/collected" type="bool" setter="" getter="">
|
||||
Indicates whether your app collects user's hand structure and hand movements.
|
||||
</member>
|
||||
<member name="privacy/collected_data/hands/collection_purposes" type="int" setter="" getter="">
|
||||
The reasons your app collects user's hand structure and hand movements. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
|
||||
</member>
|
||||
<member name="privacy/collected_data/hands/linked_to_user" type="bool" setter="" getter="">
|
||||
Indicates whether your app links user's hand structure and hand movements to the user's identity.
|
||||
</member>
|
||||
<member name="privacy/collected_data/hands/used_for_tracking" type="bool" setter="" getter="">
|
||||
Indicates whether your app uses user's hand structure and hand movements for tracking.
|
||||
</member>
|
||||
<member name="privacy/collected_data/head/collected" type="bool" setter="" getter="">
|
||||
Indicates whether your app collects user's head movement.
|
||||
</member>
|
||||
<member name="privacy/collected_data/head/collection_purposes" type="int" setter="" getter="">
|
||||
The reasons your app collects user's head movement. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
|
||||
</member>
|
||||
<member name="privacy/collected_data/head/linked_to_user" type="bool" setter="" getter="">
|
||||
Indicates whether your app links user's head movement to the user's identity.
|
||||
</member>
|
||||
<member name="privacy/collected_data/head/used_for_tracking" type="bool" setter="" getter="">
|
||||
Indicates whether your app uses user's head movement for tracking.
|
||||
</member>
|
||||
<member name="privacy/collected_data/health/collected" type="bool" setter="" getter="">
|
||||
Indicates whether your app collects health and medical data.
|
||||
</member>
|
||||
<member name="privacy/collected_data/health/collection_purposes" type="int" setter="" getter="">
|
||||
The reasons your app collects health and medical data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
|
||||
</member>
|
||||
<member name="privacy/collected_data/health/linked_to_user" type="bool" setter="" getter="">
|
||||
Indicates whether your app links health and medical data to the user's identity.
|
||||
</member>
|
||||
<member name="privacy/collected_data/health/used_for_tracking" type="bool" setter="" getter="">
|
||||
Indicates whether your app uses health and medical data for tracking.
|
||||
</member>
|
||||
<member name="privacy/collected_data/name/collected" type="bool" setter="" getter="">
|
||||
Indicates whether your app collects user's name.
|
||||
</member>
|
||||
<member name="privacy/collected_data/name/collection_purposes" type="int" setter="" getter="">
|
||||
The reasons your app collects user's name. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
|
||||
</member>
|
||||
<member name="privacy/collected_data/name/linked_to_user" type="bool" setter="" getter="">
|
||||
Indicates whether your app links user's name to the user's identity.
|
||||
</member>
|
||||
<member name="privacy/collected_data/name/used_for_tracking" type="bool" setter="" getter="">
|
||||
Indicates whether your app uses user's name for tracking.
|
||||
</member>
|
||||
<member name="privacy/collected_data/other_contact_info/collected" type="bool" setter="" getter="">
|
||||
Indicates whether your app collects any other contact information.
|
||||
</member>
|
||||
<member name="privacy/collected_data/other_contact_info/collection_purposes" type="int" setter="" getter="">
|
||||
The reasons your app collects any other contact information. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
|
||||
</member>
|
||||
<member name="privacy/collected_data/other_contact_info/linked_to_user" type="bool" setter="" getter="">
|
||||
Indicates whether your app links any other contact information to the user's identity.
|
||||
</member>
|
||||
<member name="privacy/collected_data/other_contact_info/used_for_tracking" type="bool" setter="" getter="">
|
||||
Indicates whether your app uses any other contact information for tracking.
|
||||
</member>
|
||||
<member name="privacy/collected_data/other_data_types/collected" type="bool" setter="" getter="">
|
||||
Indicates whether your app collects any other data.
|
||||
</member>
|
||||
<member name="privacy/collected_data/other_data_types/collection_purposes" type="int" setter="" getter="">
|
||||
The reasons your app collects any other data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
|
||||
</member>
|
||||
<member name="privacy/collected_data/other_data_types/linked_to_user" type="bool" setter="" getter="">
|
||||
Indicates whether your app links any other data to the user's identity.
|
||||
</member>
|
||||
<member name="privacy/collected_data/other_data_types/used_for_tracking" type="bool" setter="" getter="">
|
||||
Indicates whether your app uses any other data for tracking.
|
||||
</member>
|
||||
<member name="privacy/collected_data/other_diagnostic_data/collected" type="bool" setter="" getter="">
|
||||
Indicates whether your app collects any other diagnostic data.
|
||||
</member>
|
||||
<member name="privacy/collected_data/other_diagnostic_data/collection_purposes" type="int" setter="" getter="">
|
||||
The reasons your app collects any other diagnostic data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
|
||||
</member>
|
||||
<member name="privacy/collected_data/other_diagnostic_data/linked_to_user" type="bool" setter="" getter="">
|
||||
Indicates whether your app links any other diagnostic data to the user's identity.
|
||||
</member>
|
||||
<member name="privacy/collected_data/other_diagnostic_data/used_for_tracking" type="bool" setter="" getter="">
|
||||
Indicates whether your app uses any other diagnostic data for tracking.
|
||||
</member>
|
||||
<member name="privacy/collected_data/other_financial_info/collected" type="bool" setter="" getter="">
|
||||
Indicates whether your app collects any other financial information.
|
||||
</member>
|
||||
<member name="privacy/collected_data/other_financial_info/collection_purposes" type="int" setter="" getter="">
|
||||
The reasons your app collects any other financial information. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
|
||||
</member>
|
||||
<member name="privacy/collected_data/other_financial_info/linked_to_user" type="bool" setter="" getter="">
|
||||
Indicates whether your app links any other financial information to the user's identity.
|
||||
</member>
|
||||
<member name="privacy/collected_data/other_financial_info/used_for_tracking" type="bool" setter="" getter="">
|
||||
Indicates whether your app uses any other financial information for tracking.
|
||||
</member>
|
||||
<member name="privacy/collected_data/other_usage_data/collected" type="bool" setter="" getter="">
|
||||
Indicates whether your app collects any other usage data.
|
||||
</member>
|
||||
<member name="privacy/collected_data/other_usage_data/collection_purposes" type="int" setter="" getter="">
|
||||
The reasons your app collects any other usage data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
|
||||
</member>
|
||||
<member name="privacy/collected_data/other_usage_data/linked_to_user" type="bool" setter="" getter="">
|
||||
Indicates whether your app links any other usage data to the user's identity.
|
||||
</member>
|
||||
<member name="privacy/collected_data/other_usage_data/used_for_tracking" type="bool" setter="" getter="">
|
||||
Indicates whether your app uses any other usage data for tracking.
|
||||
</member>
|
||||
<member name="privacy/collected_data/other_user_content/collected" type="bool" setter="" getter="">
|
||||
Indicates whether your app collects any other user generated content.
|
||||
</member>
|
||||
<member name="privacy/collected_data/other_user_content/collection_purposes" type="int" setter="" getter="">
|
||||
The reasons your app collects any other user generated content. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
|
||||
</member>
|
||||
<member name="privacy/collected_data/other_user_content/linked_to_user" type="bool" setter="" getter="">
|
||||
Indicates whether your app links any other user generated content to the user's identity.
|
||||
</member>
|
||||
<member name="privacy/collected_data/other_user_content/used_for_tracking" type="bool" setter="" getter="">
|
||||
Indicates whether your app uses any other user generated content for tracking.
|
||||
</member>
|
||||
<member name="privacy/collected_data/payment_info/collected" type="bool" setter="" getter="">
|
||||
Indicates whether your app collects payment information.
|
||||
</member>
|
||||
<member name="privacy/collected_data/payment_info/collection_purposes" type="int" setter="" getter="">
|
||||
The reasons your app collects payment information. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
|
||||
</member>
|
||||
<member name="privacy/collected_data/payment_info/linked_to_user" type="bool" setter="" getter="">
|
||||
Indicates whether your app links payment information to the user's identity.
|
||||
</member>
|
||||
<member name="privacy/collected_data/payment_info/used_for_tracking" type="bool" setter="" getter="">
|
||||
Indicates whether your app uses payment information for tracking.
|
||||
</member>
|
||||
<member name="privacy/collected_data/performance_data/collected" type="bool" setter="" getter="">
|
||||
Indicates whether your app collects performance data.
|
||||
</member>
|
||||
<member name="privacy/collected_data/performance_data/collection_purposes" type="int" setter="" getter="">
|
||||
The reasons your app collects performance data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
|
||||
</member>
|
||||
<member name="privacy/collected_data/performance_data/linked_to_user" type="bool" setter="" getter="">
|
||||
Indicates whether your app links performance data to the user's identity.
|
||||
</member>
|
||||
<member name="privacy/collected_data/performance_data/used_for_tracking" type="bool" setter="" getter="">
|
||||
Indicates whether your app uses performance data for tracking.
|
||||
</member>
|
||||
<member name="privacy/collected_data/phone_number/collected" type="bool" setter="" getter="">
|
||||
Indicates whether your app collects phone number.
|
||||
</member>
|
||||
<member name="privacy/collected_data/phone_number/collection_purposes" type="int" setter="" getter="">
|
||||
The reasons your app collects phone number. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
|
||||
</member>
|
||||
<member name="privacy/collected_data/phone_number/linked_to_user" type="bool" setter="" getter="">
|
||||
Indicates whether your app links phone number to the user's identity.
|
||||
</member>
|
||||
<member name="privacy/collected_data/phone_number/used_for_tracking" type="bool" setter="" getter="">
|
||||
Indicates whether your app uses phone number for tracking.
|
||||
</member>
|
||||
<member name="privacy/collected_data/photos_or_videos/collected" type="bool" setter="" getter="">
|
||||
Indicates whether your app collects photos or videos.
|
||||
</member>
|
||||
<member name="privacy/collected_data/photos_or_videos/collection_purposes" type="int" setter="" getter="">
|
||||
The reasons your app collects photos or videos. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
|
||||
</member>
|
||||
<member name="privacy/collected_data/photos_or_videos/linked_to_user" type="bool" setter="" getter="">
|
||||
Indicates whether your app links photos or videos to the user's identity.
|
||||
</member>
|
||||
<member name="privacy/collected_data/photos_or_videos/used_for_tracking" type="bool" setter="" getter="">
|
||||
Indicates whether your app uses photos or videos for tracking.
|
||||
</member>
|
||||
<member name="privacy/collected_data/physical_address/collected" type="bool" setter="" getter="">
|
||||
Indicates whether your app collects physical address.
|
||||
</member>
|
||||
<member name="privacy/collected_data/physical_address/collection_purposes" type="int" setter="" getter="">
|
||||
The reasons your app collects physical address. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
|
||||
</member>
|
||||
<member name="privacy/collected_data/physical_address/linked_to_user" type="bool" setter="" getter="">
|
||||
Indicates whether your app links physical address to the user's identity.
|
||||
</member>
|
||||
<member name="privacy/collected_data/physical_address/used_for_tracking" type="bool" setter="" getter="">
|
||||
Indicates whether your app uses physical address for tracking.
|
||||
</member>
|
||||
<member name="privacy/collected_data/precise_location/collected" type="bool" setter="" getter="">
|
||||
Indicates whether your app collects precise location data.
|
||||
</member>
|
||||
<member name="privacy/collected_data/precise_location/collection_purposes" type="int" setter="" getter="">
|
||||
The reasons your app collects precise location data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
|
||||
</member>
|
||||
<member name="privacy/collected_data/precise_location/linked_to_user" type="bool" setter="" getter="">
|
||||
Indicates whether your app links precise location data to the user's identity.
|
||||
</member>
|
||||
<member name="privacy/collected_data/precise_location/used_for_tracking" type="bool" setter="" getter="">
|
||||
Indicates whether your app uses precise location data for tracking.
|
||||
</member>
|
||||
<member name="privacy/collected_data/product_interaction/collected" type="bool" setter="" getter="">
|
||||
Indicates whether your app collects product interaction data.
|
||||
</member>
|
||||
<member name="privacy/collected_data/product_interaction/collection_purposes" type="int" setter="" getter="">
|
||||
The reasons your app collects product interaction data. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
|
||||
</member>
|
||||
<member name="privacy/collected_data/product_interaction/linked_to_user" type="bool" setter="" getter="">
|
||||
Indicates whether your app links product interaction data to the user's identity.
|
||||
</member>
|
||||
<member name="privacy/collected_data/product_interaction/used_for_tracking" type="bool" setter="" getter="">
|
||||
Indicates whether your app uses product interaction data for tracking.
|
||||
</member>
|
||||
<member name="privacy/collected_data/purchase_history/collected" type="bool" setter="" getter="">
|
||||
Indicates whether your app collects purchase history.
|
||||
</member>
|
||||
<member name="privacy/collected_data/purchase_history/collection_purposes" type="int" setter="" getter="">
|
||||
The reasons your app collects purchase history. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
|
||||
</member>
|
||||
<member name="privacy/collected_data/purchase_history/linked_to_user" type="bool" setter="" getter="">
|
||||
Indicates whether your app links purchase history to the user's identity.
|
||||
</member>
|
||||
<member name="privacy/collected_data/purchase_history/used_for_tracking" type="bool" setter="" getter="">
|
||||
Indicates whether your app uses purchase history for tracking.
|
||||
</member>
|
||||
<member name="privacy/collected_data/search_hhistory/collected" type="bool" setter="" getter="">
|
||||
Indicates whether your app collects search history.
|
||||
</member>
|
||||
<member name="privacy/collected_data/search_hhistory/collection_purposes" type="int" setter="" getter="">
|
||||
The reasons your app collects search history. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
|
||||
</member>
|
||||
<member name="privacy/collected_data/search_hhistory/linked_to_user" type="bool" setter="" getter="">
|
||||
Indicates whether your app links search history to the user's identity.
|
||||
</member>
|
||||
<member name="privacy/collected_data/search_hhistory/used_for_tracking" type="bool" setter="" getter="">
|
||||
Indicates whether your app uses search history for tracking.
|
||||
</member>
|
||||
<member name="privacy/collected_data/sensitive_info/collected" type="bool" setter="" getter="">
|
||||
Indicates whether your app collects sensitive user information.
|
||||
</member>
|
||||
<member name="privacy/collected_data/sensitive_info/collection_purposes" type="int" setter="" getter="">
|
||||
The reasons your app collects sensitive user information. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
|
||||
</member>
|
||||
<member name="privacy/collected_data/sensitive_info/linked_to_user" type="bool" setter="" getter="">
|
||||
Indicates whether your app links sensitive user information to the user's identity.
|
||||
</member>
|
||||
<member name="privacy/collected_data/sensitive_info/used_for_tracking" type="bool" setter="" getter="">
|
||||
Indicates whether your app uses sensitive user information for tracking.
|
||||
</member>
|
||||
<member name="privacy/collected_data/user_id/collected" type="bool" setter="" getter="">
|
||||
Indicates whether your app collects user IDs.
|
||||
</member>
|
||||
<member name="privacy/collected_data/user_id/collection_purposes" type="int" setter="" getter="">
|
||||
The reasons your app collects user IDs. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests]Describing data use in privacy manifests[/url].
|
||||
</member>
|
||||
<member name="privacy/collected_data/user_id/linked_to_user" type="bool" setter="" getter="">
|
||||
Indicates whether your app links user IDs to the user's identity.
|
||||
</member>
|
||||
<member name="privacy/collected_data/user_id/used_for_tracking" type="bool" setter="" getter="">
|
||||
Indicates whether your app uses user IDs for tracking.
|
||||
</member>
|
||||
<member name="privacy/desktop_folder_usage_description" type="String" setter="" getter="">
|
||||
A message displayed when requesting access to the user's "Desktop" folder (in English).
|
||||
</member>
|
||||
<member name="privacy/desktop_folder_usage_description_localized" type="Dictionary" setter="" getter="">
|
||||
A message displayed when requesting access to the user's "Desktop" folder (localized).
|
||||
</member>
|
||||
<member name="privacy/documents_folder_usage_description" type="String" setter="" getter="">
|
||||
A message displayed when requesting access to the user's "Documents" folder (in English).
|
||||
</member>
|
||||
<member name="privacy/documents_folder_usage_description_localized" type="Dictionary" setter="" getter="">
|
||||
A message displayed when requesting access to the user's "Documents" folder (localized).
|
||||
</member>
|
||||
<member name="privacy/downloads_folder_usage_description" type="String" setter="" getter="">
|
||||
A message displayed when requesting access to the user's "Downloads" folder (in English).
|
||||
</member>
|
||||
<member name="privacy/downloads_folder_usage_description_localized" type="Dictionary" setter="" getter="">
|
||||
A message displayed when requesting access to the user's "Downloads" folder (localized).
|
||||
</member>
|
||||
<member name="privacy/location_usage_description" type="String" setter="" getter="">
|
||||
A message displayed when requesting access to the user's location information (in English).
|
||||
</member>
|
||||
<member name="privacy/location_usage_description_localized" type="Dictionary" setter="" getter="">
|
||||
A message displayed when requesting access to the user's location information (localized).
|
||||
</member>
|
||||
<member name="privacy/microphone_usage_description" type="String" setter="" getter="">
|
||||
A message displayed when requesting access to the device's microphone (in English).
|
||||
</member>
|
||||
<member name="privacy/microphone_usage_description_localized" type="Dictionary" setter="" getter="">
|
||||
A message displayed when requesting access to the device's microphone (localized).
|
||||
</member>
|
||||
<member name="privacy/network_volumes_usage_description" type="String" setter="" getter="">
|
||||
A message displayed when requesting access to the user's network drives (in English).
|
||||
</member>
|
||||
<member name="privacy/network_volumes_usage_description_localized" type="Dictionary" setter="" getter="">
|
||||
A message displayed when requesting access to the user's network drives (localized).
|
||||
</member>
|
||||
<member name="privacy/photos_library_usage_description" type="String" setter="" getter="">
|
||||
A message displayed when requesting access to the user's photo library (in English).
|
||||
</member>
|
||||
<member name="privacy/photos_library_usage_description_localized" type="Dictionary" setter="" getter="">
|
||||
A message displayed when requesting access to the user's photo library (localized).
|
||||
</member>
|
||||
<member name="privacy/removable_volumes_usage_description" type="String" setter="" getter="">
|
||||
A message displayed when requesting access to the user's removable drives (in English).
|
||||
</member>
|
||||
<member name="privacy/removable_volumes_usage_description_localized" type="Dictionary" setter="" getter="">
|
||||
A message displayed when requesting access to the user's removable drives (localized).
|
||||
</member>
|
||||
<member name="privacy/tracking_domains" type="PackedStringArray" setter="" getter="">
|
||||
The list of internet domains your app connects to that engage in tracking. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files]Privacy manifest files[/url].
|
||||
</member>
|
||||
<member name="privacy/tracking_enabled" type="bool" setter="" getter="">
|
||||
Indicates whether your app uses data for tracking. See [url=https://developer.apple.com/documentation/bundleresources/privacy_manifest_files]Privacy manifest files[/url].
|
||||
</member>
|
||||
<member name="shader_baker/enabled" type="bool" setter="" getter="">
|
||||
If [code]true[/code], shaders will be compiled and embedded in the application. This option is only supported when using the Forward+ or Mobile renderers.
|
||||
</member>
|
||||
<member name="ssh_remote_deploy/cleanup_script" type="String" setter="" getter="">
|
||||
Script code to execute on the remote host when app is finished.
|
||||
The following variables can be used in the script:
|
||||
- [code]{temp_dir}[/code] - Path of temporary folder on the remote, used to upload app and scripts to.
|
||||
- [code]{archive_name}[/code] - Name of the ZIP containing uploaded application.
|
||||
- [code]{exe_name}[/code] - Name of application executable.
|
||||
- [code]{cmd_args}[/code] - Array of the command line argument for the application.
|
||||
</member>
|
||||
<member name="ssh_remote_deploy/enabled" type="bool" setter="" getter="">
|
||||
Enables remote deploy using SSH/SCP.
|
||||
</member>
|
||||
<member name="ssh_remote_deploy/extra_args_scp" type="String" setter="" getter="">
|
||||
Array of the additional command line arguments passed to the SCP.
|
||||
</member>
|
||||
<member name="ssh_remote_deploy/extra_args_ssh" type="String" setter="" getter="">
|
||||
Array of the additional command line arguments passed to the SSH.
|
||||
</member>
|
||||
<member name="ssh_remote_deploy/host" type="String" setter="" getter="">
|
||||
Remote host SSH user name and address, in [code]user@address[/code] format.
|
||||
</member>
|
||||
<member name="ssh_remote_deploy/port" type="String" setter="" getter="">
|
||||
Remote host SSH port number.
|
||||
</member>
|
||||
<member name="ssh_remote_deploy/run_script" type="String" setter="" getter="">
|
||||
Script code to execute on the remote host when running the app.
|
||||
The following variables can be used in the script:
|
||||
- [code]{temp_dir}[/code] - Path of temporary folder on the remote, used to upload app and scripts to.
|
||||
- [code]{archive_name}[/code] - Name of the ZIP containing uploaded application.
|
||||
- [code]{exe_name}[/code] - Name of application executable.
|
||||
- [code]{cmd_args}[/code] - Array of the command line argument for the application.
|
||||
</member>
|
||||
<member name="xcode/platform_build" type="String" setter="" getter="">
|
||||
macOS build number used to build application executable.
|
||||
</member>
|
||||
<member name="xcode/sdk_build" type="String" setter="" getter="">
|
||||
macOS SDK build number used to build application executable.
|
||||
</member>
|
||||
<member name="xcode/sdk_name" type="String" setter="" getter="">
|
||||
macOS SDK name used to build application executable.
|
||||
</member>
|
||||
<member name="xcode/sdk_version" type="String" setter="" getter="">
|
||||
macOS SDK version used to build application executable in the [code]major.minor[/code] format.
|
||||
</member>
|
||||
<member name="xcode/xcode_build" type="String" setter="" getter="">
|
||||
Xcode build number used to build application executable.
|
||||
</member>
|
||||
<member name="xcode/xcode_version" type="String" setter="" getter="">
|
||||
Xcode version used to build application executable.
|
||||
</member>
|
||||
</members>
|
||||
</class>
|
76
platform/macos/editor/embedded_game_view_plugin.h
Normal file
76
platform/macos/editor/embedded_game_view_plugin.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/**************************************************************************/
|
||||
/* embedded_game_view_plugin.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/run/game_view_plugin.h"
|
||||
|
||||
class EmbeddedProcessMacOS;
|
||||
|
||||
class GameViewDebuggerMacOS : public GameViewDebugger {
|
||||
GDCLASS(GameViewDebuggerMacOS, GameViewDebugger);
|
||||
|
||||
EmbeddedProcessMacOS *embedded_process = nullptr;
|
||||
|
||||
/// Message handler function for capture.
|
||||
|
||||
/// @brief A function pointer to the message handler function.
|
||||
typedef bool (GameViewDebuggerMacOS::*ParseMessageFunc)(const Array &p_args);
|
||||
|
||||
/// @brief A map of message handlers.
|
||||
static HashMap<String, ParseMessageFunc> parse_message_handlers;
|
||||
|
||||
/// @brief Initialize the message handlers.
|
||||
static void _init_capture_message_handlers();
|
||||
|
||||
bool _msg_set_context_id(const Array &p_args);
|
||||
bool _msg_cursor_set_shape(const Array &p_args);
|
||||
bool _msg_cursor_set_custom_image(const Array &p_args);
|
||||
bool _msg_mouse_set_mode(const Array &p_args);
|
||||
bool _msg_window_set_ime_active(const Array &p_args);
|
||||
bool _msg_window_set_ime_position(const Array &p_args);
|
||||
bool _msg_joy_start(const Array &p_args);
|
||||
bool _msg_joy_stop(const Array &p_args);
|
||||
bool _msg_warp_mouse(const Array &p_args);
|
||||
|
||||
public:
|
||||
virtual bool capture(const String &p_message, const Array &p_data, int p_session) override;
|
||||
|
||||
GameViewDebuggerMacOS(EmbeddedProcessMacOS *p_embedded_process);
|
||||
};
|
||||
|
||||
class GameViewPluginMacOS : public GameViewPluginBase {
|
||||
GDCLASS(GameViewPluginMacOS, GameViewPluginBase);
|
||||
|
||||
public:
|
||||
GameViewPluginMacOS();
|
||||
};
|
||||
|
||||
extern "C" void register_game_view_plugin();
|
173
platform/macos/editor/embedded_game_view_plugin.mm
Normal file
173
platform/macos/editor/embedded_game_view_plugin.mm
Normal file
@@ -0,0 +1,173 @@
|
||||
/**************************************************************************/
|
||||
/* embedded_game_view_plugin.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "embedded_game_view_plugin.h"
|
||||
|
||||
#include "embedded_process_macos.h"
|
||||
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/gui/window_wrapper.h"
|
||||
|
||||
HashMap<String, GameViewDebuggerMacOS::ParseMessageFunc> GameViewDebuggerMacOS::parse_message_handlers;
|
||||
|
||||
bool GameViewDebuggerMacOS::_msg_set_context_id(const Array &p_args) {
|
||||
ERR_FAIL_COND_V_MSG(p_args.size() != 1, false, "set_context_id: invalid number of arguments.");
|
||||
|
||||
embedded_process->set_context_id(p_args[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GameViewDebuggerMacOS::_msg_cursor_set_shape(const Array &p_args) {
|
||||
ERR_FAIL_COND_V_MSG(p_args.size() != 1, false, "cursor_set_shape: invalid number of arguments.");
|
||||
|
||||
Control::CursorShape shape = Control::CursorShape(p_args[0]);
|
||||
embedded_process->get_layer_host()->set_default_cursor_shape(static_cast<Control::CursorShape>(shape));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GameViewDebuggerMacOS::_msg_cursor_set_custom_image(const Array &p_args) {
|
||||
ERR_FAIL_COND_V_MSG(p_args.size() != 3, false, "cursor_set_custom_image: invalid number of arguments.");
|
||||
|
||||
Ref<Image> image;
|
||||
image.instantiate();
|
||||
PackedByteArray cursor_data = p_args[0];
|
||||
if (!cursor_data.is_empty()) {
|
||||
image->load_png_from_buffer(cursor_data);
|
||||
}
|
||||
DisplayServer::CursorShape shape = DisplayServer::CursorShape(p_args[1]);
|
||||
Vector2 hotspot = p_args[2];
|
||||
|
||||
embedded_process->get_layer_host()->cursor_set_custom_image(image, shape, hotspot);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GameViewDebuggerMacOS::_msg_mouse_set_mode(const Array &p_args) {
|
||||
ERR_FAIL_COND_V_MSG(p_args.size() != 1, false, "mouse_set_mode: invalid number of arguments.");
|
||||
|
||||
DisplayServer::MouseMode mode = DisplayServer::MouseMode(p_args[0]);
|
||||
embedded_process->mouse_set_mode(mode);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GameViewDebuggerMacOS::_msg_window_set_ime_active(const Array &p_args) {
|
||||
ERR_FAIL_COND_V_MSG(p_args.size() != 1, false, "window_set_ime_active: invalid number of arguments.");
|
||||
|
||||
bool active = p_args[0];
|
||||
DisplayServer::WindowID wid = embedded_process->get_window()->get_window_id();
|
||||
DisplayServer::get_singleton()->window_set_ime_active(active, wid);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GameViewDebuggerMacOS::_msg_window_set_ime_position(const Array &p_args) {
|
||||
ERR_FAIL_COND_V_MSG(p_args.size() != 1, false, "window_set_ime_position: invalid number of arguments.");
|
||||
|
||||
Point2i pos = p_args[0];
|
||||
Point2i xpos = embedded_process->get_layer_host()->get_global_transform_with_canvas().xform(pos);
|
||||
DisplayServer::WindowID wid = embedded_process->get_window()->get_window_id();
|
||||
DisplayServer::get_singleton()->window_set_ime_position(xpos, wid);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GameViewDebuggerMacOS::_msg_joy_start(const Array &p_args) {
|
||||
ERR_FAIL_COND_V_MSG(p_args.size() != 3, false, "joy_start: invalid number of arguments.");
|
||||
|
||||
int joy_id = p_args[0];
|
||||
float duration = p_args[1];
|
||||
Vector2 strength = p_args[2];
|
||||
Input::get_singleton()->start_joy_vibration(joy_id, strength.x, strength.y, duration);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GameViewDebuggerMacOS::_msg_joy_stop(const Array &p_args) {
|
||||
ERR_FAIL_COND_V_MSG(p_args.size() != 1, false, "joy_stop: invalid number of arguments.");
|
||||
|
||||
int joy_id = p_args[0];
|
||||
Input::get_singleton()->stop_joy_vibration(joy_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GameViewDebuggerMacOS::_msg_warp_mouse(const Array &p_args) {
|
||||
ERR_FAIL_COND_V_MSG(p_args.size() != 1, false, "warp_mouse: invalid number of arguments.");
|
||||
|
||||
Vector2i pos = p_args[0];
|
||||
embedded_process->get_layer_host()->warp_mouse(pos);
|
||||
return true;
|
||||
}
|
||||
|
||||
void GameViewDebuggerMacOS::_init_capture_message_handlers() {
|
||||
parse_message_handlers["game_view:set_context_id"] = &GameViewDebuggerMacOS::_msg_set_context_id;
|
||||
parse_message_handlers["game_view:cursor_set_shape"] = &GameViewDebuggerMacOS::_msg_cursor_set_shape;
|
||||
parse_message_handlers["game_view:cursor_set_custom_image"] = &GameViewDebuggerMacOS::_msg_cursor_set_custom_image;
|
||||
parse_message_handlers["game_view:mouse_set_mode"] = &GameViewDebuggerMacOS::_msg_mouse_set_mode;
|
||||
parse_message_handlers["game_view:window_set_ime_active"] = &GameViewDebuggerMacOS::_msg_window_set_ime_active;
|
||||
parse_message_handlers["game_view:window_set_ime_position"] = &GameViewDebuggerMacOS::_msg_window_set_ime_position;
|
||||
parse_message_handlers["game_view:warp_mouse"] = &GameViewDebuggerMacOS::_msg_warp_mouse;
|
||||
}
|
||||
|
||||
bool GameViewDebuggerMacOS::capture(const String &p_message, const Array &p_data, int p_session) {
|
||||
Ref<EditorDebuggerSession> session = get_session(p_session);
|
||||
ERR_FAIL_COND_V(session.is_null(), true);
|
||||
|
||||
ParseMessageFunc *fn_ptr = parse_message_handlers.getptr(p_message);
|
||||
if (fn_ptr) {
|
||||
return (this->**fn_ptr)(p_data);
|
||||
} else {
|
||||
return GameViewDebugger::capture(p_message, p_data, p_session);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
GameViewDebuggerMacOS::GameViewDebuggerMacOS(EmbeddedProcessMacOS *p_embedded_process) :
|
||||
embedded_process(p_embedded_process) {
|
||||
if (parse_message_handlers.is_empty()) {
|
||||
_init_capture_message_handlers();
|
||||
}
|
||||
}
|
||||
|
||||
GameViewPluginMacOS::GameViewPluginMacOS() {
|
||||
if (Engine::get_singleton()->is_recovery_mode_hint()) {
|
||||
return;
|
||||
}
|
||||
|
||||
EmbeddedProcessMacOS *embedded_process = memnew(EmbeddedProcessMacOS);
|
||||
|
||||
Ref<GameViewDebuggerMacOS> debugger;
|
||||
debugger.instantiate(embedded_process);
|
||||
|
||||
setup(debugger, embedded_process);
|
||||
}
|
||||
|
||||
extern "C" GameViewPluginBase *get_game_view_plugin() {
|
||||
return memnew(GameViewPluginMacOS);
|
||||
}
|
135
platform/macos/editor/embedded_process_macos.h
Normal file
135
platform/macos/editor/embedded_process_macos.h
Normal file
@@ -0,0 +1,135 @@
|
||||
/**************************************************************************/
|
||||
/* embedded_process_macos.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/run/embedded_process.h"
|
||||
|
||||
class DisplayServerMacOS;
|
||||
class EmbeddedProcessMacOS;
|
||||
|
||||
class LayerHost final : public Control {
|
||||
GDCLASS(LayerHost, Control);
|
||||
|
||||
ScriptEditorDebugger *script_debugger = nullptr;
|
||||
EmbeddedProcessMacOS *process = nullptr;
|
||||
bool window_focused = true;
|
||||
|
||||
struct CustomCursor {
|
||||
Ref<Image> image;
|
||||
Vector2 hotspot;
|
||||
CustomCursor() {}
|
||||
CustomCursor(const Ref<Image> &p_image, const Vector2 &p_hotspot) {
|
||||
image = p_image;
|
||||
hotspot = p_hotspot;
|
||||
}
|
||||
};
|
||||
HashMap<DisplayServer::CursorShape, CustomCursor> custom_cursors;
|
||||
|
||||
virtual void gui_input(const Ref<InputEvent> &p_event) override;
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
void cursor_set_custom_image(const Ref<Image> &p_image, DisplayServer::CursorShape p_shape, const Vector2 &p_hotspot);
|
||||
void set_script_debugger(ScriptEditorDebugger *p_debugger) {
|
||||
script_debugger = p_debugger;
|
||||
}
|
||||
|
||||
LayerHost(EmbeddedProcessMacOS *p_process);
|
||||
};
|
||||
|
||||
class EmbeddedProcessMacOS final : public EmbeddedProcessBase {
|
||||
GDCLASS(EmbeddedProcessMacOS, EmbeddedProcessBase);
|
||||
|
||||
enum class EmbeddingState {
|
||||
IDLE,
|
||||
IN_PROGRESS,
|
||||
COMPLETED,
|
||||
FAILED,
|
||||
};
|
||||
|
||||
DisplayServerMacOS *ds = nullptr;
|
||||
EmbeddingState embedding_state = EmbeddingState::IDLE;
|
||||
uint32_t context_id = 0;
|
||||
ScriptEditorDebugger *script_debugger = nullptr;
|
||||
LayerHost *layer_host = nullptr;
|
||||
OS::ProcessID current_process_id = 0;
|
||||
|
||||
// Embedded process state.
|
||||
|
||||
// The last mouse mode sent by the embedded process.
|
||||
DisplayServer::MouseMode mouse_mode = DisplayServer::MOUSE_MODE_VISIBLE;
|
||||
|
||||
// Helper functions.
|
||||
|
||||
void _try_embed_process();
|
||||
void update_embedded_process();
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
// MARK: - Message Handlers
|
||||
|
||||
void set_context_id(uint32_t p_context_id);
|
||||
void mouse_set_mode(DisplayServer::MouseMode p_mode);
|
||||
|
||||
uint32_t get_context_id() const { return context_id; }
|
||||
void set_script_debugger(ScriptEditorDebugger *p_debugger) override;
|
||||
|
||||
bool is_embedding_in_progress() const override {
|
||||
return embedding_state == EmbeddingState::IN_PROGRESS;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ bool is_embedding_completed() const override {
|
||||
return embedding_state == EmbeddingState::COMPLETED;
|
||||
}
|
||||
|
||||
bool is_process_focused() const override { return layer_host->has_focus(); }
|
||||
void embed_process(OS::ProcessID p_pid) override;
|
||||
int get_embedded_pid() const override { return current_process_id; }
|
||||
void reset() override;
|
||||
void request_close() override;
|
||||
void queue_update_embedded_process() override { update_embedded_process(); }
|
||||
|
||||
Rect2i get_adjusted_embedded_window_rect(const Rect2i &p_rect) const override;
|
||||
|
||||
_FORCE_INLINE_ LayerHost *get_layer_host() const { return layer_host; }
|
||||
|
||||
void display_state_changed();
|
||||
|
||||
// MARK: - Embedded process state
|
||||
_FORCE_INLINE_ DisplayServer::MouseMode get_mouse_mode() const { return mouse_mode; }
|
||||
|
||||
EmbeddedProcessMacOS();
|
||||
~EmbeddedProcessMacOS() override;
|
||||
};
|
341
platform/macos/editor/embedded_process_macos.mm
Normal file
341
platform/macos/editor/embedded_process_macos.mm
Normal file
@@ -0,0 +1,341 @@
|
||||
/**************************************************************************/
|
||||
/* embedded_process_macos.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "embedded_process_macos.h"
|
||||
|
||||
#include "platform/macos/display_server_embedded.h"
|
||||
#include "platform/macos/display_server_macos.h"
|
||||
|
||||
#include "core/input/input_event_codec.h"
|
||||
#include "editor/debugger/script_editor_debugger.h"
|
||||
#include "editor/editor_main_screen.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/settings/editor_settings.h"
|
||||
#include "scene/gui/control.h"
|
||||
#include "scene/main/window.h"
|
||||
|
||||
void EmbeddedProcessMacOS::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
set_notify_transform(true);
|
||||
} break;
|
||||
case NOTIFICATION_TRANSFORM_CHANGED:
|
||||
case NOTIFICATION_VISIBILITY_CHANGED: {
|
||||
update_embedded_process();
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void EmbeddedProcessMacOS::update_embedded_process() {
|
||||
layer_host->set_rect(get_adjusted_embedded_window_rect(get_rect()));
|
||||
if (is_embedding_completed()) {
|
||||
ds->embed_process_update(window->get_window_id(), this);
|
||||
Rect2i rect = get_screen_embedded_window_rect();
|
||||
script_debugger->send_message("embed:window_size", { rect.size });
|
||||
}
|
||||
}
|
||||
|
||||
void EmbeddedProcessMacOS::set_context_id(uint32_t p_context_id) {
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
|
||||
context_id = p_context_id;
|
||||
|
||||
_try_embed_process();
|
||||
}
|
||||
|
||||
void EmbeddedProcessMacOS::set_script_debugger(ScriptEditorDebugger *p_debugger) {
|
||||
script_debugger = p_debugger;
|
||||
layer_host->set_script_debugger(script_debugger);
|
||||
_try_embed_process();
|
||||
}
|
||||
|
||||
void EmbeddedProcessMacOS::embed_process(OS::ProcessID p_pid) {
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (current_process_id != 0) {
|
||||
// Stop embedding the last process.
|
||||
OS::get_singleton()->kill(current_process_id);
|
||||
}
|
||||
|
||||
reset();
|
||||
|
||||
current_process_id = p_pid;
|
||||
embedding_state = EmbeddingState::IN_PROGRESS;
|
||||
// Attempt to embed the process, but if it has just started and the window is not ready yet,
|
||||
// we will retry in this case.
|
||||
_try_embed_process();
|
||||
}
|
||||
|
||||
void EmbeddedProcessMacOS::reset() {
|
||||
if (!ds) {
|
||||
ds = static_cast<DisplayServerMacOS *>(DisplayServer::get_singleton());
|
||||
}
|
||||
if (current_process_id != 0 && is_embedding_completed()) {
|
||||
ds->remove_embedded_process(current_process_id);
|
||||
}
|
||||
current_process_id = 0;
|
||||
embedding_state = EmbeddingState::IDLE;
|
||||
context_id = 0;
|
||||
script_debugger = nullptr;
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
void EmbeddedProcessMacOS::request_close() {
|
||||
if (current_process_id != 0 && is_embedding_completed()) {
|
||||
script_debugger->send_message("embed:win_event", { DisplayServer::WINDOW_EVENT_CLOSE_REQUEST });
|
||||
}
|
||||
reset();
|
||||
}
|
||||
|
||||
void EmbeddedProcessMacOS::display_state_changed() {
|
||||
DisplayServerEmbeddedState state;
|
||||
state.screen_max_scale = ds->screen_get_max_scale();
|
||||
state.screen_dpi = ds->screen_get_dpi();
|
||||
DisplayServer::WindowID wid = window->get_window_id();
|
||||
state.screen_window_scale = ds->screen_get_scale(ds->window_get_current_screen(wid));
|
||||
state.display_id = ds->window_get_display_id(wid);
|
||||
|
||||
PackedByteArray data;
|
||||
state.serialize(data);
|
||||
script_debugger->send_message("embed:ds_state", { data });
|
||||
}
|
||||
|
||||
void EmbeddedProcessMacOS::_try_embed_process() {
|
||||
if (current_process_id == 0 || script_debugger == nullptr || context_id == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayServer::WindowID wid = window->get_window_id();
|
||||
Error err = ds->embed_process_update(wid, this);
|
||||
if (err == OK) {
|
||||
layer_host->set_rect(get_adjusted_embedded_window_rect(get_rect()));
|
||||
|
||||
// Replicate important DisplayServer state.
|
||||
display_state_changed();
|
||||
|
||||
Rect2i rect = get_screen_embedded_window_rect();
|
||||
script_debugger->send_message("embed:window_size", { rect.size });
|
||||
embedding_state = EmbeddingState::COMPLETED;
|
||||
queue_redraw();
|
||||
emit_signal(SNAME("embedding_completed"));
|
||||
|
||||
// Send initial joystick state.
|
||||
{
|
||||
Input *input = Input::get_singleton();
|
||||
TypedArray<int> joy_pads = input->get_connected_joypads();
|
||||
for (const Variant &idx : joy_pads) {
|
||||
String name = input->get_joy_name(idx);
|
||||
script_debugger->send_message("embed:joy_add", { idx, name });
|
||||
}
|
||||
}
|
||||
|
||||
layer_host->grab_focus();
|
||||
} else {
|
||||
// Another unknown error.
|
||||
reset();
|
||||
emit_signal(SNAME("embedding_failed"));
|
||||
}
|
||||
}
|
||||
|
||||
Rect2i EmbeddedProcessMacOS::get_adjusted_embedded_window_rect(const Rect2i &p_rect) const {
|
||||
Rect2i control_rect = Rect2i(p_rect.position + margin_top_left, (p_rect.size - get_margins_size()).maxi(1));
|
||||
if (window_size != Size2i()) {
|
||||
Rect2i desired_rect;
|
||||
if (!keep_aspect && control_rect.size.x >= window_size.x && control_rect.size.y >= window_size.y) {
|
||||
// Fixed at the desired size.
|
||||
desired_rect.size = window_size;
|
||||
} else {
|
||||
float ratio = MIN((float)control_rect.size.x / window_size.x, (float)control_rect.size.y / window_size.y);
|
||||
desired_rect.size = Size2i(window_size.x * ratio, window_size.y * ratio).maxi(1);
|
||||
}
|
||||
desired_rect.position = Size2i(control_rect.position.x + ((control_rect.size.x - desired_rect.size.x) / 2), control_rect.position.y + ((control_rect.size.y - desired_rect.size.y) / 2));
|
||||
return desired_rect;
|
||||
} else {
|
||||
// Stretch, use all the control area.
|
||||
return control_rect;
|
||||
}
|
||||
}
|
||||
|
||||
void EmbeddedProcessMacOS::mouse_set_mode(DisplayServer::MouseMode p_mode) {
|
||||
mouse_mode = p_mode;
|
||||
// If the mouse is anything other than visible, we must ensure the Game view is active and the layer focused.
|
||||
if (mouse_mode != DisplayServer::MOUSE_MODE_VISIBLE) {
|
||||
EditorNode::get_singleton()->get_editor_main_screen()->select(EditorMainScreen::EDITOR_GAME);
|
||||
layer_host->grab_focus();
|
||||
}
|
||||
DisplayServer::get_singleton()->mouse_set_mode(p_mode);
|
||||
}
|
||||
|
||||
EmbeddedProcessMacOS::EmbeddedProcessMacOS() :
|
||||
EmbeddedProcessBase() {
|
||||
layer_host = memnew(LayerHost(this));
|
||||
add_child(layer_host);
|
||||
layer_host->set_focus_mode(FOCUS_ALL);
|
||||
layer_host->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
|
||||
layer_host->set_custom_minimum_size(Size2(100, 100));
|
||||
|
||||
// This shortcut allows a user to forcibly release a captured mouse from within the editor, regardless of whether
|
||||
// the embedded process has implemented support to release the cursor.
|
||||
ED_SHORTCUT("game_view/release_mouse", TTRC("Release Mouse"), KeyModifierMask::ALT | Key::ESCAPE);
|
||||
}
|
||||
|
||||
EmbeddedProcessMacOS::~EmbeddedProcessMacOS() {
|
||||
if (current_process_id != 0) {
|
||||
// Stop embedding the last process.
|
||||
OS::get_singleton()->kill(current_process_id);
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
void LayerHost::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_MOUSE_ENTER: {
|
||||
DisplayServer *ds = DisplayServer::get_singleton();
|
||||
for (const KeyValue<DisplayServer::CursorShape, CustomCursor> &E : custom_cursors) {
|
||||
ds->cursor_set_custom_image(E.value.image, E.key, E.value.hotspot);
|
||||
}
|
||||
if (script_debugger) {
|
||||
script_debugger->send_message("embed:win_event", { DisplayServer::WINDOW_EVENT_MOUSE_ENTER });
|
||||
}
|
||||
} break;
|
||||
case NOTIFICATION_FOCUS_ENTER: {
|
||||
// Restore mouse capture, if necessary.
|
||||
DisplayServer *ds = DisplayServer::get_singleton();
|
||||
if (process->get_mouse_mode() != ds->mouse_get_mode()) {
|
||||
// Restore embedded process mouse mode.
|
||||
ds->mouse_set_mode(process->get_mouse_mode());
|
||||
}
|
||||
if (!window_focused && script_debugger) {
|
||||
script_debugger->send_message("embed:win_event", { DisplayServer::WINDOW_EVENT_FOCUS_IN });
|
||||
window_focused = true;
|
||||
}
|
||||
} break;
|
||||
case NOTIFICATION_MOUSE_EXIT: {
|
||||
DisplayServer *ds = DisplayServer::get_singleton();
|
||||
for (int i = 0; i < DisplayServer::CURSOR_MAX; i++) {
|
||||
ds->cursor_set_custom_image(Ref<Resource>(), (DisplayServer::CursorShape)i, Vector2());
|
||||
}
|
||||
if (script_debugger) {
|
||||
script_debugger->send_message("embed:win_event", { DisplayServer::WINDOW_EVENT_MOUSE_EXIT });
|
||||
}
|
||||
} break;
|
||||
case NOTIFICATION_FOCUS_EXIT: {
|
||||
// Temporarily set mouse state back to visible, so the user can interact with the editor.
|
||||
DisplayServer *ds = DisplayServer::get_singleton();
|
||||
if (ds->mouse_get_mode() != DisplayServer::MOUSE_MODE_VISIBLE) {
|
||||
ds->mouse_set_mode(DisplayServer::MOUSE_MODE_VISIBLE);
|
||||
}
|
||||
if (window_focused && script_debugger) {
|
||||
script_debugger->send_message("embed:win_event", { DisplayServer::WINDOW_EVENT_FOCUS_OUT });
|
||||
window_focused = false;
|
||||
}
|
||||
} break;
|
||||
case MainLoop::NOTIFICATION_OS_IME_UPDATE: {
|
||||
if (script_debugger && has_focus()) {
|
||||
const String ime_text = DisplayServer::get_singleton()->ime_get_text();
|
||||
const Vector2i ime_selection = DisplayServer::get_singleton()->ime_get_selection();
|
||||
script_debugger->send_message("embed:ime_update", { ime_text, ime_selection });
|
||||
}
|
||||
} break;
|
||||
case NOTIFICATION_EXIT_TREE:
|
||||
case NOTIFICATION_VISIBILITY_CHANGED: {
|
||||
if (!is_visible_in_tree()) {
|
||||
DisplayServer *ds = DisplayServer::get_singleton();
|
||||
for (int i = 0; i < DisplayServer::CURSOR_MAX; i++) {
|
||||
ds->cursor_set_custom_image(Ref<Resource>(), (DisplayServer::CursorShape)i, Vector2());
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case NOTIFICATION_WM_WINDOW_FOCUS_IN: {
|
||||
if (!window_focused && script_debugger) {
|
||||
script_debugger->send_message("embed:win_event", { DisplayServer::WINDOW_EVENT_FOCUS_IN });
|
||||
window_focused = true;
|
||||
}
|
||||
} break;
|
||||
case NOTIFICATION_WM_WINDOW_FOCUS_OUT: {
|
||||
if (window_focused && script_debugger) {
|
||||
script_debugger->send_message("embed:win_event", { DisplayServer::WINDOW_EVENT_FOCUS_OUT });
|
||||
window_focused = false;
|
||||
}
|
||||
} break;
|
||||
case NOTIFICATION_APPLICATION_FOCUS_IN: {
|
||||
if (script_debugger) {
|
||||
script_debugger->send_message("embed:notification", { NOTIFICATION_APPLICATION_FOCUS_IN });
|
||||
}
|
||||
} break;
|
||||
case NOTIFICATION_APPLICATION_FOCUS_OUT: {
|
||||
if (script_debugger) {
|
||||
script_debugger->send_message("embed:notification", { NOTIFICATION_APPLICATION_FOCUS_OUT });
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void LayerHost::cursor_set_custom_image(const Ref<Image> &p_image, DisplayServer::CursorShape p_shape, const Vector2 &p_hotspot) {
|
||||
custom_cursors[p_shape] = CustomCursor(p_image, p_hotspot);
|
||||
}
|
||||
|
||||
void LayerHost::gui_input(const Ref<InputEvent> &p_event) {
|
||||
if (!process->is_embedding_completed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (p_event->is_pressed()) {
|
||||
if (ED_IS_SHORTCUT("game_view/release_mouse", p_event)) {
|
||||
DisplayServer *ds = DisplayServer::get_singleton();
|
||||
if (ds->mouse_get_mode() != DisplayServer::MOUSE_MODE_VISIBLE) {
|
||||
ds->mouse_set_mode(DisplayServer::MOUSE_MODE_VISIBLE);
|
||||
script_debugger->send_message("embed:mouse_set_mode", { DisplayServer::MOUSE_MODE_VISIBLE });
|
||||
}
|
||||
accept_event();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Ref<InputEventJoypadMotion> jm = p_event;
|
||||
Ref<InputEventJoypadButton> jb = p_event;
|
||||
if (jm.is_valid() || jb.is_valid()) {
|
||||
accept_event();
|
||||
return;
|
||||
}
|
||||
|
||||
PackedByteArray data;
|
||||
if (encode_input_event(p_event, data)) {
|
||||
script_debugger->send_message("embed:event", { data });
|
||||
accept_event();
|
||||
}
|
||||
}
|
||||
|
||||
LayerHost::LayerHost(EmbeddedProcessMacOS *p_process) :
|
||||
process(p_process) {}
|
70
platform/macos/embedded_debugger.h
Normal file
70
platform/macos/embedded_debugger.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/**************************************************************************/
|
||||
/* embedded_debugger.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/templates/hash_map.h"
|
||||
#include "core/variant/array.h"
|
||||
|
||||
class DisplayServerEmbedded;
|
||||
|
||||
/// @brief Singleton class to process embedded debugging message in the child process.
|
||||
class EmbeddedDebugger {
|
||||
inline static EmbeddedDebugger *singleton = nullptr;
|
||||
|
||||
EmbeddedDebugger(DisplayServerEmbedded *p_ds);
|
||||
|
||||
public:
|
||||
static void initialize(DisplayServerEmbedded *p_ds);
|
||||
static void deinitialize();
|
||||
|
||||
~EmbeddedDebugger();
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
private:
|
||||
DisplayServerEmbedded *ds;
|
||||
|
||||
/// Message handler function for parse_message.
|
||||
typedef Error (EmbeddedDebugger::*ParseMessageFunc)(const Array &p_args);
|
||||
static HashMap<String, ParseMessageFunc> parse_message_handlers;
|
||||
static void _init_parse_message_handlers();
|
||||
|
||||
Error _msg_window_size(const Array &p_args);
|
||||
Error _msg_mouse_set_mode(const Array &p_args);
|
||||
Error _msg_event(const Array &p_args);
|
||||
Error _msg_win_event(const Array &p_args);
|
||||
Error _msg_notification(const Array &p_args);
|
||||
Error _msg_ime_update(const Array &p_args);
|
||||
Error _msg_ds_state(const Array &p_args);
|
||||
|
||||
public:
|
||||
static Error parse_message(void *p_user, const String &p_msg, const Array &p_args, bool &r_captured);
|
||||
#endif
|
||||
};
|
184
platform/macos/embedded_debugger.mm
Normal file
184
platform/macos/embedded_debugger.mm
Normal file
@@ -0,0 +1,184 @@
|
||||
/**************************************************************************/
|
||||
/* embedded_debugger.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "embedded_debugger.h"
|
||||
|
||||
#include "display_server_embedded.h"
|
||||
|
||||
#include "core/debugger/engine_debugger.h"
|
||||
#include "core/input/input_event_codec.h"
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
HashMap<String, EmbeddedDebugger::ParseMessageFunc> EmbeddedDebugger::parse_message_handlers;
|
||||
#endif
|
||||
|
||||
EmbeddedDebugger::EmbeddedDebugger(DisplayServerEmbedded *p_ds) {
|
||||
singleton = this;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
ds = p_ds;
|
||||
if (parse_message_handlers.is_empty()) {
|
||||
_init_parse_message_handlers();
|
||||
}
|
||||
EngineDebugger::register_message_capture("embed", EngineDebugger::Capture(this, EmbeddedDebugger::parse_message));
|
||||
#endif
|
||||
}
|
||||
|
||||
EmbeddedDebugger::~EmbeddedDebugger() {
|
||||
singleton = nullptr;
|
||||
}
|
||||
|
||||
void EmbeddedDebugger::initialize(DisplayServerEmbedded *p_ds) {
|
||||
if (EngineDebugger::is_active()) {
|
||||
memnew(EmbeddedDebugger(p_ds));
|
||||
}
|
||||
}
|
||||
|
||||
void EmbeddedDebugger::deinitialize() {
|
||||
if (singleton) {
|
||||
memdelete(singleton);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
void EmbeddedDebugger::_init_parse_message_handlers() {
|
||||
parse_message_handlers["window_size"] = &EmbeddedDebugger::_msg_window_size;
|
||||
parse_message_handlers["mouse_set_mode"] = &EmbeddedDebugger::_msg_mouse_set_mode;
|
||||
parse_message_handlers["event"] = &EmbeddedDebugger::_msg_event;
|
||||
parse_message_handlers["win_event"] = &EmbeddedDebugger::_msg_win_event;
|
||||
parse_message_handlers["notification"] = &EmbeddedDebugger::_msg_notification;
|
||||
parse_message_handlers["ime_update"] = &EmbeddedDebugger::_msg_ime_update;
|
||||
parse_message_handlers["ds_state"] = &EmbeddedDebugger::_msg_ds_state;
|
||||
}
|
||||
|
||||
Error EmbeddedDebugger::_msg_window_size(const Array &p_args) {
|
||||
ERR_FAIL_COND_V_MSG(p_args.size() != 1, ERR_INVALID_PARAMETER, "Invalid number of arguments for 'window_size' message.");
|
||||
Size2i size = p_args[0];
|
||||
ds->window_set_size(size);
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error EmbeddedDebugger::_msg_mouse_set_mode(const Array &p_args) {
|
||||
ERR_FAIL_COND_V_MSG(p_args.size() != 1, ERR_INVALID_PARAMETER, "Invalid number of arguments for 'mouse_set_mode' message.");
|
||||
DisplayServer::MouseMode mode = p_args[0];
|
||||
ds->mouse_set_mode(mode);
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error EmbeddedDebugger::_msg_event(const Array &p_args) {
|
||||
ERR_FAIL_COND_V_MSG(p_args.size() != 1, ERR_INVALID_PARAMETER, "Invalid number of arguments for 'event' message.");
|
||||
Input *input = Input::get_singleton();
|
||||
if (!input) {
|
||||
// Ignore if we've received an event before the process has initialized.
|
||||
return OK;
|
||||
}
|
||||
|
||||
PackedByteArray data = p_args[0];
|
||||
Ref<InputEvent> event;
|
||||
decode_input_event(data, event);
|
||||
|
||||
{
|
||||
Ref<InputEventMouse> e = event;
|
||||
if (e.is_valid()) {
|
||||
input->set_mouse_position(e->get_position());
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Ref<InputEventMagnifyGesture> e = event;
|
||||
if (e.is_valid()) {
|
||||
input->set_mouse_position(e->get_position());
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Ref<InputEventPanGesture> e = event;
|
||||
if (e.is_valid()) {
|
||||
input->set_mouse_position(e->get_position());
|
||||
}
|
||||
}
|
||||
|
||||
if (event.is_valid()) {
|
||||
input->parse_input_event(event);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error EmbeddedDebugger::_msg_win_event(const Array &p_args) {
|
||||
ERR_FAIL_COND_V_MSG(p_args.size() != 1, ERR_INVALID_PARAMETER, "Invalid number of arguments for 'win_event' message.");
|
||||
DisplayServer::WindowEvent win_event = p_args[0];
|
||||
ds->send_window_event(win_event, DisplayServer::MAIN_WINDOW_ID);
|
||||
if (win_event == DisplayServer::WindowEvent::WINDOW_EVENT_MOUSE_EXIT) {
|
||||
Input::get_singleton()->release_pressed_events();
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error EmbeddedDebugger::_msg_ime_update(const Array &p_args) {
|
||||
ERR_FAIL_COND_V_MSG(p_args.size() != 2, ERR_INVALID_PARAMETER, "Invalid number of arguments for 'ime_update' message.");
|
||||
String ime_text = p_args[0];
|
||||
Vector2i ime_selection = p_args[1];
|
||||
ds->update_im_text(ime_selection, ime_text);
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error EmbeddedDebugger::_msg_notification(const Array &p_args) {
|
||||
ERR_FAIL_COND_V_MSG(p_args.size() != 1, ERR_INVALID_PARAMETER, "Invalid number of arguments for 'notification' message.");
|
||||
int notification = p_args[0];
|
||||
if (OS::get_singleton()->get_main_loop()) {
|
||||
OS::get_singleton()->get_main_loop()->notification(notification);
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error EmbeddedDebugger::_msg_ds_state(const Array &p_args) {
|
||||
ERR_FAIL_COND_V_MSG(p_args.size() != 1, ERR_INVALID_PARAMETER, "Invalid number of arguments for 'ds_state' message.");
|
||||
PackedByteArray data = p_args[0];
|
||||
DisplayServerEmbeddedState state;
|
||||
state.deserialize(data);
|
||||
ds->set_state(state);
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error EmbeddedDebugger::parse_message(void *p_user, const String &p_msg, const Array &p_args, bool &r_captured) {
|
||||
EmbeddedDebugger *self = static_cast<EmbeddedDebugger *>(p_user);
|
||||
r_captured = true;
|
||||
|
||||
ParseMessageFunc *fn_ptr = parse_message_handlers.getptr(p_msg);
|
||||
if (fn_ptr) {
|
||||
return (self->**fn_ptr)(p_args);
|
||||
} else {
|
||||
// Any other messages with this prefix should be ignored.
|
||||
WARN_PRINT("Unknown message: " + p_msg);
|
||||
return ERR_SKIP;
|
||||
}
|
||||
}
|
||||
#endif
|
124
platform/macos/embedded_gl_manager.h
Normal file
124
platform/macos/embedded_gl_manager.h
Normal file
@@ -0,0 +1,124 @@
|
||||
/**************************************************************************/
|
||||
/* embedded_gl_manager.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(MACOS_ENABLED) && defined(GLES3_ENABLED)
|
||||
|
||||
#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>
|
||||
|
||||
GODOT_CLANG_WARNING_PUSH_AND_IGNORE("-Wdeprecated-declarations") // OpenGL is deprecated in macOS 10.14.
|
||||
|
||||
typedef CGLContextObj (*CGLGetCurrentContextPtr)(void);
|
||||
typedef CGLError (*CGLTexImageIOSurface2DPtr)(CGLContextObj ctx, GLenum target, GLenum internal_format,
|
||||
GLsizei width, GLsizei height, GLenum format, GLenum type, IOSurfaceRef ioSurface, GLuint plane);
|
||||
typedef const char *(*CGLErrorStringPtr)(CGLError);
|
||||
|
||||
class GLManagerEmbedded {
|
||||
/// @brief The number of framebuffers to create for each window.
|
||||
///
|
||||
/// Triple-buffering is used to avoid stuttering.
|
||||
static constexpr uint32_t BUFFER_COUNT = 3;
|
||||
|
||||
// The display ID for which vsync is used. If this value is -1, vsync is disabled.
|
||||
constexpr static uint32_t INVALID_DISPLAY_ID = static_cast<uint32_t>(-1);
|
||||
|
||||
struct FrameBuffer {
|
||||
IOSurfaceRef surface = nullptr;
|
||||
unsigned int tex = 0;
|
||||
unsigned int fbo = 0;
|
||||
};
|
||||
|
||||
struct GLWindow {
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
CALayer *layer = nullptr;
|
||||
NSOpenGLContext *context = nullptr;
|
||||
FrameBuffer framebuffers[BUFFER_COUNT];
|
||||
uint32_t current_fb = 0;
|
||||
bool is_valid = false;
|
||||
|
||||
void destroy_framebuffers();
|
||||
|
||||
~GLWindow() { destroy_framebuffers(); }
|
||||
};
|
||||
|
||||
RBMap<DisplayServer::WindowID, GLWindow> windows;
|
||||
typedef RBMap<DisplayServer::WindowID, GLWindow>::Element GLWindowElement;
|
||||
|
||||
NSOpenGLContext *shared_context = nullptr;
|
||||
DisplayServer::WindowID current_window = DisplayServer::INVALID_WINDOW_ID;
|
||||
|
||||
Error create_context(GLWindow &p_win);
|
||||
|
||||
bool framework_loaded = false;
|
||||
CGLGetCurrentContextPtr CGLGetCurrentContext = nullptr;
|
||||
CGLTexImageIOSurface2DPtr CGLTexImageIOSurface2D = nullptr;
|
||||
CGLErrorStringPtr CGLErrorString = nullptr;
|
||||
|
||||
uint32_t display_id = INVALID_DISPLAY_ID;
|
||||
CVDisplayLinkRef display_link = nullptr;
|
||||
bool vsync_enabled = false;
|
||||
bool display_link_running = false;
|
||||
dispatch_semaphore_t display_semaphore = nullptr;
|
||||
|
||||
void create_display_link();
|
||||
void release_display_link();
|
||||
|
||||
public:
|
||||
Error window_create(DisplayServer::WindowID p_window_id, CALayer *p_layer, 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);
|
||||
Size2i window_get_size(DisplayServer::WindowID p_window_id) const;
|
||||
|
||||
void set_display_id(uint32_t p_display_id);
|
||||
void set_vsync_enabled(bool p_enabled);
|
||||
bool is_vsync_enabled() const { return vsync_enabled; }
|
||||
|
||||
void release_current();
|
||||
void swap_buffers();
|
||||
|
||||
void window_make_current(DisplayServer::WindowID p_window_id);
|
||||
|
||||
Error initialize();
|
||||
|
||||
GLManagerEmbedded();
|
||||
~GLManagerEmbedded();
|
||||
};
|
||||
|
||||
GODOT_CLANG_WARNING_PUSH
|
||||
|
||||
#endif // MACOS_ENABLED && GLES3_ENABLED
|
334
platform/macos/embedded_gl_manager.mm
Normal file
334
platform/macos/embedded_gl_manager.mm
Normal file
@@ -0,0 +1,334 @@
|
||||
/**************************************************************************/
|
||||
/* embedded_gl_manager.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "embedded_gl_manager.h"
|
||||
|
||||
#import "drivers/gles3/storage/texture_storage.h"
|
||||
#import "platform_gl.h"
|
||||
|
||||
#if defined(MACOS_ENABLED) && defined(GLES3_ENABLED)
|
||||
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
GODOT_CLANG_WARNING_PUSH_AND_IGNORE("-Wdeprecated-declarations") // OpenGL is deprecated in macOS 10.14.
|
||||
|
||||
Error GLManagerEmbedded::create_context(GLWindow &p_win) {
|
||||
NSOpenGLPixelFormatAttribute attributes[] = {
|
||||
NSOpenGLPFADoubleBuffer,
|
||||
NSOpenGLPFAClosestPolicy,
|
||||
NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
|
||||
NSOpenGLPFAColorSize, 32,
|
||||
NSOpenGLPFADepthSize, 24,
|
||||
NSOpenGLPFAStencilSize, 8,
|
||||
0
|
||||
};
|
||||
|
||||
NSOpenGLPixelFormat *pixel_format = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];
|
||||
ERR_FAIL_NULL_V(pixel_format, ERR_CANT_CREATE);
|
||||
|
||||
p_win.context = [[NSOpenGLContext alloc] initWithFormat:pixel_format shareContext:shared_context];
|
||||
ERR_FAIL_NULL_V(p_win.context, ERR_CANT_CREATE);
|
||||
if (shared_context == nullptr) {
|
||||
shared_context = p_win.context;
|
||||
}
|
||||
|
||||
[p_win.context makeCurrentContext];
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error GLManagerEmbedded::window_create(DisplayServer::WindowID p_window_id, CALayer *p_layer, int p_width, int p_height) {
|
||||
GLWindow win;
|
||||
win.layer = p_layer;
|
||||
win.width = 0;
|
||||
win.height = 0;
|
||||
|
||||
if (create_context(win) != OK) {
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
windows[p_window_id] = win;
|
||||
window_make_current(p_window_id);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
void GLManagerEmbedded::window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height) {
|
||||
GLWindowElement *el = windows.find(p_window_id);
|
||||
ERR_FAIL_NULL_MSG(el, "Window resize failed: window does not exist.");
|
||||
|
||||
GLWindow &win = el->get();
|
||||
|
||||
if (win.width == (uint32_t)p_width && win.height == (uint32_t)p_height) {
|
||||
return;
|
||||
}
|
||||
|
||||
win.width = (uint32_t)p_width;
|
||||
win.height = (uint32_t)p_height;
|
||||
|
||||
win.destroy_framebuffers();
|
||||
|
||||
for (FrameBuffer &fb : win.framebuffers) {
|
||||
NSDictionary *surfaceProps = @{
|
||||
(NSString *)kIOSurfaceWidth : @(p_width),
|
||||
(NSString *)kIOSurfaceHeight : @(p_height),
|
||||
(NSString *)kIOSurfaceBytesPerElement : @(4),
|
||||
(NSString *)kIOSurfacePixelFormat : @(kCVPixelFormatType_32BGRA),
|
||||
};
|
||||
fb.surface = IOSurfaceCreate((__bridge CFDictionaryRef)surfaceProps);
|
||||
if (fb.surface == nullptr) {
|
||||
ERR_PRINT(vformat("Failed to create IOSurface: width=%d, height=%d", p_width, p_height));
|
||||
win.destroy_framebuffers();
|
||||
return;
|
||||
}
|
||||
|
||||
glGenTextures(1, &fb.tex);
|
||||
glBindTexture(GL_TEXTURE_RECTANGLE, fb.tex);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
CGLError err = CGLTexImageIOSurface2D(CGLGetCurrentContext(),
|
||||
GL_TEXTURE_RECTANGLE,
|
||||
GL_RGBA,
|
||||
p_width,
|
||||
p_height,
|
||||
GL_BGRA,
|
||||
GL_UNSIGNED_INT_8_8_8_8_REV,
|
||||
fb.surface,
|
||||
0);
|
||||
if (err != kCGLNoError) {
|
||||
String err_string = String(CGLErrorString(err));
|
||||
ERR_PRINT(vformat("CGLTexImageIOSurface2D failed (%d): %s", err, err_string));
|
||||
win.destroy_framebuffers();
|
||||
return;
|
||||
}
|
||||
|
||||
glGenFramebuffers(1, &fb.fbo);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fb.fbo);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE, fb.tex, 0);
|
||||
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||
ERR_PRINT("Unable to create framebuffer from IOSurface texture.");
|
||||
win.destroy_framebuffers();
|
||||
return;
|
||||
}
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glBindTexture(GL_TEXTURE_RECTANGLE, 0);
|
||||
}
|
||||
win.current_fb = 0;
|
||||
GLES3::TextureStorage::system_fbo = win.framebuffers[win.current_fb].fbo;
|
||||
win.is_valid = true;
|
||||
}
|
||||
|
||||
void GLManagerEmbedded::GLWindow::destroy_framebuffers() {
|
||||
is_valid = false;
|
||||
GLES3::TextureStorage::system_fbo = 0;
|
||||
|
||||
for (FrameBuffer &fb : framebuffers) {
|
||||
if (fb.fbo) {
|
||||
glDeleteFramebuffers(1, &fb.fbo);
|
||||
fb.fbo = 0;
|
||||
}
|
||||
|
||||
if (fb.tex) {
|
||||
glDeleteTextures(1, &fb.tex);
|
||||
fb.tex = 0;
|
||||
}
|
||||
|
||||
if (fb.surface) {
|
||||
IOSurfaceRef old_surface = fb.surface;
|
||||
fb.surface = nullptr;
|
||||
CFRelease(old_surface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Size2i GLManagerEmbedded::window_get_size(DisplayServer::WindowID p_window_id) const {
|
||||
const GLWindowElement *el = windows.find(p_window_id);
|
||||
if (el == nullptr) {
|
||||
return Size2i();
|
||||
}
|
||||
|
||||
const GLWindow &win = el->value();
|
||||
return Size2i(win.width, win.height);
|
||||
}
|
||||
|
||||
void GLManagerEmbedded::window_destroy(DisplayServer::WindowID p_window_id) {
|
||||
GLWindowElement *el = windows.find(p_window_id);
|
||||
if (el == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (current_window == p_window_id) {
|
||||
current_window = DisplayServer::INVALID_WINDOW_ID;
|
||||
}
|
||||
|
||||
windows.erase(el);
|
||||
}
|
||||
|
||||
void GLManagerEmbedded::release_current() {
|
||||
if (current_window == DisplayServer::INVALID_WINDOW_ID) {
|
||||
return;
|
||||
}
|
||||
|
||||
[NSOpenGLContext clearCurrentContext];
|
||||
current_window = DisplayServer::INVALID_WINDOW_ID;
|
||||
}
|
||||
|
||||
void GLManagerEmbedded::window_make_current(DisplayServer::WindowID p_window_id) {
|
||||
if (current_window == p_window_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const GLWindowElement *el = windows.find(p_window_id);
|
||||
if (el == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const GLWindow &win = el->value();
|
||||
[win.context makeCurrentContext];
|
||||
|
||||
current_window = p_window_id;
|
||||
}
|
||||
|
||||
void GLManagerEmbedded::swap_buffers() {
|
||||
GLWindow &win = windows[current_window];
|
||||
[win.context flushBuffer];
|
||||
|
||||
static bool last_valid = false;
|
||||
if (!win.is_valid) {
|
||||
if (last_valid) {
|
||||
ERR_PRINT("GLWindow framebuffers are invalid.");
|
||||
last_valid = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
last_valid = true;
|
||||
|
||||
if (display_link_running) {
|
||||
dispatch_semaphore_wait(display_semaphore, DISPATCH_TIME_FOREVER);
|
||||
}
|
||||
|
||||
[CATransaction begin];
|
||||
[CATransaction setDisableActions:YES];
|
||||
win.layer.contents = (__bridge id)win.framebuffers[win.current_fb].surface;
|
||||
[CATransaction commit];
|
||||
win.current_fb = (win.current_fb + 1) % BUFFER_COUNT;
|
||||
GLES3::TextureStorage::system_fbo = win.framebuffers[win.current_fb].fbo;
|
||||
}
|
||||
|
||||
Error GLManagerEmbedded::initialize() {
|
||||
return framework_loaded ? OK : ERR_CANT_CREATE;
|
||||
}
|
||||
|
||||
void GLManagerEmbedded::create_display_link() {
|
||||
DEV_ASSERT(display_link == nullptr);
|
||||
|
||||
CVReturn err = CVDisplayLinkCreateWithCGDisplay(CGMainDisplayID(), &display_link);
|
||||
ERR_FAIL_COND_MSG(err != kCVReturnSuccess, "Failed to create display link.");
|
||||
|
||||
__block dispatch_semaphore_t local_semaphore = display_semaphore;
|
||||
|
||||
CVDisplayLinkSetOutputHandler(display_link, ^CVReturn(CVDisplayLinkRef p_display_link, const CVTimeStamp *p_now, const CVTimeStamp *p_output_time, CVOptionFlags p_flags, CVOptionFlags *p_flags_out) {
|
||||
dispatch_semaphore_signal(local_semaphore);
|
||||
return kCVReturnSuccess;
|
||||
});
|
||||
}
|
||||
|
||||
void GLManagerEmbedded::release_display_link() {
|
||||
DEV_ASSERT(display_link != nullptr);
|
||||
if (CVDisplayLinkIsRunning(display_link)) {
|
||||
CVDisplayLinkStop(display_link);
|
||||
}
|
||||
CVDisplayLinkRelease(display_link);
|
||||
display_link = nullptr;
|
||||
}
|
||||
|
||||
void GLManagerEmbedded::set_display_id(uint32_t p_display_id) {
|
||||
if (display_id == p_display_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
CVReturn err = CVDisplayLinkSetCurrentCGDisplay(display_link, static_cast<CGDirectDisplayID>(p_display_id));
|
||||
ERR_FAIL_COND_MSG(err != kCVReturnSuccess, "Failed to set display ID for display link.");
|
||||
}
|
||||
|
||||
void GLManagerEmbedded::set_vsync_enabled(bool p_enabled) {
|
||||
if (p_enabled == vsync_enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
vsync_enabled = p_enabled;
|
||||
|
||||
if (vsync_enabled) {
|
||||
if (!CVDisplayLinkIsRunning(display_link)) {
|
||||
CVReturn err = CVDisplayLinkStart(display_link);
|
||||
ERR_FAIL_COND_MSG(err != kCVReturnSuccess, "Failed to start display link.");
|
||||
display_link_running = true;
|
||||
}
|
||||
} else {
|
||||
if (CVDisplayLinkIsRunning(display_link)) {
|
||||
CVReturn err = CVDisplayLinkStop(display_link);
|
||||
ERR_FAIL_COND_MSG(err != kCVReturnSuccess, "Failed to stop display link.");
|
||||
display_link_running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GLManagerEmbedded::GLManagerEmbedded() {
|
||||
display_semaphore = dispatch_semaphore_create(BUFFER_COUNT);
|
||||
|
||||
create_display_link();
|
||||
|
||||
NSBundle *framework = [NSBundle bundleWithIdentifier:@"com.apple.opengl"];
|
||||
if ([framework load]) {
|
||||
void *library_handle = dlopen([framework.executablePath UTF8String], RTLD_NOW);
|
||||
if (library_handle) {
|
||||
CGLGetCurrentContext = (CGLGetCurrentContextPtr)dlsym(library_handle, "CGLGetCurrentContext");
|
||||
CGLTexImageIOSurface2D = (CGLTexImageIOSurface2DPtr)dlsym(library_handle, "CGLTexImageIOSurface2D");
|
||||
CGLErrorString = (CGLErrorStringPtr)dlsym(library_handle, "CGLErrorString");
|
||||
framework_loaded = CGLGetCurrentContext && CGLTexImageIOSurface2D && CGLErrorString;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GLManagerEmbedded::~GLManagerEmbedded() {
|
||||
release_display_link();
|
||||
release_current();
|
||||
}
|
||||
|
||||
GODOT_CLANG_WARNING_POP
|
||||
|
||||
#endif // MACOS_ENABLED && GLES3_ENABLED
|
54
platform/macos/export/export.cpp
Normal file
54
platform/macos/export/export.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
/**************************************************************************/
|
||||
/* export.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "export.h"
|
||||
|
||||
#include "export_plugin.h"
|
||||
|
||||
void register_macos_exporter_types() {
|
||||
GDREGISTER_VIRTUAL_CLASS(EditorExportPlatformMacOS);
|
||||
}
|
||||
|
||||
void register_macos_exporter() {
|
||||
// TODO: Move to editor_settings.cpp
|
||||
#ifndef ANDROID_ENABLED
|
||||
EDITOR_DEF_BASIC("export/macos/rcodesign", "");
|
||||
#ifdef WINDOWS_ENABLED
|
||||
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/macos/rcodesign", PROPERTY_HINT_GLOBAL_FILE, "*.exe"));
|
||||
#else
|
||||
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/macos/rcodesign", PROPERTY_HINT_GLOBAL_FILE));
|
||||
#endif
|
||||
#endif
|
||||
|
||||
Ref<EditorExportPlatformMacOS> platform;
|
||||
platform.instantiate();
|
||||
|
||||
EditorExport::get_singleton()->add_export_platform(platform);
|
||||
}
|
34
platform/macos/export/export.h
Normal file
34
platform/macos/export/export.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/**************************************************************************/
|
||||
/* export.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
void register_macos_exporter_types();
|
||||
void register_macos_exporter();
|
2743
platform/macos/export/export_plugin.cpp
Normal file
2743
platform/macos/export/export_plugin.cpp
Normal file
File diff suppressed because it is too large
Load Diff
173
platform/macos/export/export_plugin.h
Normal file
173
platform/macos/export/export_plugin.h
Normal file
@@ -0,0 +1,173 @@
|
||||
/**************************************************************************/
|
||||
/* export_plugin.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/io/dir_access.h"
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/io/image.h"
|
||||
#include "core/io/marshalls.h"
|
||||
#include "core/io/resource_saver.h"
|
||||
#include "core/os/os.h"
|
||||
#include "editor/export/editor_export.h"
|
||||
#include "editor/settings/editor_settings.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
// Optional environment variables for defining confidential information. If any
|
||||
// of these is set, they will override the values set in the credentials file.
|
||||
const String ENV_MAC_CODESIGN_CERT_FILE = "GODOT_MACOS_CODESIGN_CERTIFICATE_FILE";
|
||||
const String ENV_MAC_CODESIGN_CERT_PASS = "GODOT_MACOS_CODESIGN_CERTIFICATE_PASSWORD";
|
||||
const String ENV_MAC_CODESIGN_PROFILE = "GODOT_MACOS_CODESIGN_PROVISIONING_PROFILE";
|
||||
const String ENV_MAC_NOTARIZATION_UUID = "GODOT_MACOS_NOTARIZATION_API_UUID";
|
||||
const String ENV_MAC_NOTARIZATION_KEY = "GODOT_MACOS_NOTARIZATION_API_KEY";
|
||||
const String ENV_MAC_NOTARIZATION_KEY_ID = "GODOT_MACOS_NOTARIZATION_API_KEY_ID";
|
||||
const String ENV_MAC_NOTARIZATION_APPLE_ID = "GODOT_MACOS_NOTARIZATION_APPLE_ID_NAME";
|
||||
const String ENV_MAC_NOTARIZATION_APPLE_PASS = "GODOT_MACOS_NOTARIZATION_APPLE_ID_PASSWORD";
|
||||
|
||||
class EditorExportPlatformMacOS : public EditorExportPlatform {
|
||||
GDCLASS(EditorExportPlatformMacOS, EditorExportPlatform);
|
||||
|
||||
int version_code = 0;
|
||||
|
||||
Ref<ImageTexture> logo;
|
||||
|
||||
struct SSHCleanupCommand {
|
||||
String host;
|
||||
String port;
|
||||
Vector<String> ssh_args;
|
||||
String cmd_args;
|
||||
bool wait = false;
|
||||
|
||||
SSHCleanupCommand() {}
|
||||
SSHCleanupCommand(const String &p_host, const String &p_port, const Vector<String> &p_ssh_arg, const String &p_cmd_args, bool p_wait = false) {
|
||||
host = p_host;
|
||||
port = p_port;
|
||||
ssh_args = p_ssh_arg;
|
||||
cmd_args = p_cmd_args;
|
||||
wait = p_wait;
|
||||
}
|
||||
};
|
||||
|
||||
Ref<ImageTexture> run_icon;
|
||||
Ref<ImageTexture> stop_icon;
|
||||
|
||||
Vector<SSHCleanupCommand> cleanup_commands;
|
||||
OS::ProcessID ssh_pid = 0;
|
||||
int menu_options = 0;
|
||||
|
||||
void _fix_privacy_manifest(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &plist);
|
||||
void _fix_plist(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &plist, const String &p_binary);
|
||||
void _make_icon(const Ref<EditorExportPreset> &p_preset, const Ref<Image> &p_icon, Vector<uint8_t> &p_data);
|
||||
|
||||
Error _notarize(const Ref<EditorExportPreset> &p_preset, const String &p_path);
|
||||
void _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path, bool p_warn = true, bool p_set_id = false);
|
||||
void _code_sign_directory(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path, const String &p_helper_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, const String &p_helper_ent_path,
|
||||
bool p_should_error_on_non_code_sign, bool p_sandbox);
|
||||
Error _export_macos_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, const String &p_helper_ent_path, bool p_sandbox);
|
||||
Error _create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name);
|
||||
Error _create_pkg(const Ref<EditorExportPreset> &p_preset, const String &p_pkg_path, const String &p_app_path_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; }
|
||||
|
||||
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;
|
||||
}
|
||||
bool is_shebang(const String &p_path) const;
|
||||
|
||||
protected:
|
||||
virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const override;
|
||||
virtual void get_export_options(List<ExportOption> *r_options) const override;
|
||||
virtual bool get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option) const override;
|
||||
virtual String get_export_option_warning(const EditorExportPreset *p_preset, const StringName &p_name) const override;
|
||||
|
||||
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 bool is_executable(const String &p_path) const override;
|
||||
virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override;
|
||||
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override;
|
||||
|
||||
virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const override;
|
||||
virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const override;
|
||||
|
||||
virtual void get_platform_features(List<String> *r_features) const override {
|
||||
r_features->push_back("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 {
|
||||
}
|
||||
|
||||
virtual Ref<Texture2D> get_run_icon() const override;
|
||||
virtual bool poll_export() override;
|
||||
virtual Ref<Texture2D> get_option_icon(int p_index) const override;
|
||||
virtual int get_options_count() const override;
|
||||
virtual String get_option_label(int p_index) const override;
|
||||
virtual String get_option_tooltip(int p_index) const override;
|
||||
virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) override;
|
||||
virtual void cleanup() override;
|
||||
|
||||
EditorExportPlatformMacOS();
|
||||
};
|
1
platform/macos/export/logo.svg
Normal file
1
platform/macos/export/logo.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 25.6 25.6"><path fill="#1c9ef8" d="M.948 19.474V6.126c0-2.767 2.42-5.178 5.178-5.178h6.904s-2.992 7.506-2.992 13.233c0 .346.337.69.69.69h2.877c0 6.648.906 8.436 1.266 9.781H6.126c-2.75 0-5.178-2.407-5.178-5.178z"/><path fill="none" stroke="#323232" stroke-linecap="round" stroke-width="1.036" d="M7.162 8.312v1.496"/><path fill="#e1e6ed" d="M10.729 14.871c-.352 0-.69-.344-.69-.69C10.038 8.414 13.03.948 13.03.948h6.444c2.77 0 5.178 2.416 5.178 5.178v13.348c0 2.754-2.407 5.178-5.178 5.178h-4.603c-.357-1.333-1.266-2.887-1.266-9.78z"/><g fill="none" stroke="#323232" stroke-linecap="round"><path stroke-width="1.036" d="M17.575 8.255V9.75"/><path stroke-width=".69" d="M5.55 16.943c1.037 1.794 3.907 2.876 6.79 2.876 2.877 0 5.745-1.068 6.789-2.876"/></g></svg>
|
After Width: | Height: | Size: 838 B |
1
platform/macos/export/run_icon.svg
Normal file
1
platform/macos/export/run_icon.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16"><path fill="#e0e0e0" d="M4.418 1.055a3.364 3.364 0 0 0-3.364 3.364v7.164a3.364 3.364 0 0 0 3.364 3.364h7.164a3.364 3.364 0 0 0 3.364-3.364V4.418a3.364 3.364 0 0 0-3.364-3.364H7.729Zm3.875 1.164h3.291a2.2 2.2 0 0 1 2.2 2.2v7.164a2.2 2.2 0 0 1-2.2 2.2h-2.15a8.884 8.884 0 0 1-.358-1.598c-.135.02-.487.082-.693.117a7.345 7.345 0 0 1-1.254 0 7.114 7.114 0 0 1-.926-.139 6.057 6.057 0 0 1-.867-.271 3.843 3.843 0 0 1-.988-.566 3.214 3.214 0 0 1-.397-.378 2.8 2.8 0 0 1-.318-.441.56.56 0 0 1 .968-.56c.083.138.188.26.312.362.096.082.2.158.307.224.12.075.243.142.371.201.285.139.583.247.89.319a5.35 5.35 0 0 0 1.282.158c.065 0 .129-.005.184-.006.056 0 .102-.005.148-.008l.096-.006c.114-.009.228-.02.31-.032.083-.013.11-.021.143-.028.099-.022.204-.058.327-.089a28.438 28.438 0 0 1-.06-1.929V8.53H6.887c.048-1.963.746-4.357 1.181-5.677.1-.293.184-.527.225-.633ZM5.531 6.394a.56.56 0 0 1-1.118 0v-.9a.56.56 0 0 1 1.118 0Zm3.432 4.646.02.306c.02.264.049.527.082.788l.011.05c1.154-.235 2.281-.773 2.806-1.68a.56.56 0 1 0-.968-.56c-.261.454-1.082.873-1.951 1.097zM10 6.364a.56.56 0 0 0 1.118 0v-.9a.56.56 0 0 0-1.118 0z"/></svg>
|
After Width: | Height: | Size: 1.2 KiB |
66
platform/macos/gl_manager_macos_angle.h
Normal file
66
platform/macos/gl_manager_macos_angle.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/**************************************************************************/
|
||||
/* gl_manager_macos_angle.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(MACOS_ENABLED) && defined(GLES3_ENABLED)
|
||||
|
||||
#include "core/os/os.h"
|
||||
#include "core/templates/local_vector.h"
|
||||
#include "drivers/egl/egl_manager.h"
|
||||
#include "servers/display_server.h"
|
||||
|
||||
// Suppress redefinition conflicts
|
||||
#define FontVariation __FontVariation
|
||||
#define BitMap __BitMap
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <ApplicationServices/ApplicationServices.h>
|
||||
#import <CoreVideo/CoreVideo.h>
|
||||
|
||||
#undef BitMap
|
||||
#undef FontVariation
|
||||
|
||||
class GLManagerANGLE_MacOS : public EGLManager {
|
||||
private:
|
||||
virtual const char *_get_platform_extension_name() const override;
|
||||
virtual EGLenum _get_platform_extension_enum() const override;
|
||||
virtual EGLenum _get_platform_api_enum() const override;
|
||||
virtual Vector<EGLAttrib> _get_platform_display_attributes() const override;
|
||||
virtual Vector<EGLint> _get_platform_context_attribs() const override;
|
||||
|
||||
public:
|
||||
void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height) {}
|
||||
|
||||
GLManagerANGLE_MacOS() {}
|
||||
~GLManagerANGLE_MacOS() {}
|
||||
};
|
||||
|
||||
#endif // MACOS_ENABLED && GLES3_ENABLED
|
70
platform/macos/gl_manager_macos_angle.mm
Normal file
70
platform/macos/gl_manager_macos_angle.mm
Normal file
@@ -0,0 +1,70 @@
|
||||
/**************************************************************************/
|
||||
/* gl_manager_macos_angle.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "gl_manager_macos_angle.h"
|
||||
|
||||
#if defined(MACOS_ENABLED) && defined(GLES3_ENABLED)
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
#import <EGL/eglext_angle.h>
|
||||
|
||||
const char *GLManagerANGLE_MacOS::_get_platform_extension_name() const {
|
||||
return "EGL_ANGLE_platform_angle";
|
||||
}
|
||||
|
||||
EGLenum GLManagerANGLE_MacOS::_get_platform_extension_enum() const {
|
||||
return EGL_PLATFORM_ANGLE_ANGLE;
|
||||
}
|
||||
|
||||
Vector<EGLAttrib> GLManagerANGLE_MacOS::_get_platform_display_attributes() const {
|
||||
Vector<EGLAttrib> ret;
|
||||
ret.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE);
|
||||
ret.push_back(EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE);
|
||||
ret.push_back(EGL_NONE);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
EGLenum GLManagerANGLE_MacOS::_get_platform_api_enum() const {
|
||||
return EGL_OPENGL_ES_API;
|
||||
}
|
||||
|
||||
Vector<EGLint> GLManagerANGLE_MacOS::_get_platform_context_attribs() const {
|
||||
Vector<EGLint> ret;
|
||||
ret.push_back(EGL_CONTEXT_CLIENT_VERSION);
|
||||
ret.push_back(3);
|
||||
ret.push_back(EGL_NONE);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif // MACOS_ENABLED && GLES3_ENABLED
|
93
platform/macos/gl_manager_macos_legacy.h
Normal file
93
platform/macos/gl_manager_macos_legacy.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/**************************************************************************/
|
||||
/* gl_manager_macos_legacy.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(MACOS_ENABLED) && defined(GLES3_ENABLED)
|
||||
|
||||
#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>
|
||||
|
||||
GODOT_CLANG_WARNING_PUSH_AND_IGNORE("-Wdeprecated-declarations") // OpenGL is deprecated in macOS 10.14.
|
||||
|
||||
typedef CGLError (*CGLEnablePtr)(CGLContextObj ctx, CGLContextEnable pname);
|
||||
typedef CGLError (*CGLSetParameterPtr)(CGLContextObj ctx, CGLContextParameter pname, const GLint *params);
|
||||
typedef CGLContextObj (*CGLGetCurrentContextPtr)(void);
|
||||
|
||||
class GLManagerLegacy_MacOS {
|
||||
struct GLWindow {
|
||||
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 framework_loaded = false;
|
||||
bool use_vsync = false;
|
||||
CGLEnablePtr CGLEnable = nullptr;
|
||||
CGLSetParameterPtr CGLSetParameter = nullptr;
|
||||
CGLGetCurrentContextPtr CGLGetCurrentContext = nullptr;
|
||||
|
||||
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);
|
||||
|
||||
void release_current();
|
||||
void swap_buffers();
|
||||
|
||||
void window_make_current(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;
|
||||
|
||||
NSOpenGLContext *get_context(DisplayServer::WindowID p_window_id);
|
||||
|
||||
GLManagerLegacy_MacOS();
|
||||
~GLManagerLegacy_MacOS();
|
||||
};
|
||||
|
||||
GODOT_CLANG_WARNING_PUSH
|
||||
|
||||
#endif // MACOS_ENABLED && GLES3_ENABLED
|
208
platform/macos/gl_manager_macos_legacy.mm
Normal file
208
platform/macos/gl_manager_macos_legacy.mm
Normal file
@@ -0,0 +1,208 @@
|
||||
/**************************************************************************/
|
||||
/* gl_manager_macos_legacy.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "gl_manager_macos_legacy.h"
|
||||
|
||||
#if defined(MACOS_ENABLED) && defined(GLES3_ENABLED)
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
GODOT_CLANG_WARNING_PUSH_AND_IGNORE("-Wdeprecated-declarations") // OpenGL is deprecated in macOS 10.14.
|
||||
|
||||
Error GLManagerLegacy_MacOS::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_NULL_V(pixel_format, ERR_CANT_CREATE);
|
||||
|
||||
win.context = [[NSOpenGLContext alloc] initWithFormat:pixel_format shareContext:shared_context];
|
||||
ERR_FAIL_NULL_V(win.context, ERR_CANT_CREATE);
|
||||
if (shared_context == nullptr) {
|
||||
shared_context = win.context;
|
||||
}
|
||||
|
||||
[win.context setView:win.window_view];
|
||||
[win.context makeCurrentContext];
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error GLManagerLegacy_MacOS::window_create(DisplayServer::WindowID p_window_id, id p_view, int p_width, int p_height) {
|
||||
GLWindow win;
|
||||
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 GLManagerLegacy_MacOS::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];
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
void GLManagerLegacy_MacOS::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 GLManagerLegacy_MacOS::release_current() {
|
||||
if (current_window == DisplayServer::INVALID_WINDOW_ID) {
|
||||
return;
|
||||
}
|
||||
|
||||
[NSOpenGLContext clearCurrentContext];
|
||||
current_window = DisplayServer::INVALID_WINDOW_ID;
|
||||
}
|
||||
|
||||
void GLManagerLegacy_MacOS::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 GLManagerLegacy_MacOS::swap_buffers() {
|
||||
GLWindow &win = windows[current_window];
|
||||
[win.context flushBuffer];
|
||||
}
|
||||
|
||||
void GLManagerLegacy_MacOS::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 GLManagerLegacy_MacOS::initialize() {
|
||||
return framework_loaded ? OK : ERR_CANT_CREATE;
|
||||
}
|
||||
|
||||
void GLManagerLegacy_MacOS::set_use_vsync(bool p_use) {
|
||||
use_vsync = p_use;
|
||||
|
||||
CGLContextObj ctx = CGLGetCurrentContext();
|
||||
if (ctx) {
|
||||
GLint swapInterval = p_use ? 1 : 0;
|
||||
if (CGLSetParameter(ctx, kCGLCPSwapInterval, &swapInterval) != kCGLNoError) {
|
||||
WARN_PRINT("Could not set V-Sync mode.");
|
||||
}
|
||||
use_vsync = p_use;
|
||||
}
|
||||
}
|
||||
|
||||
bool GLManagerLegacy_MacOS::is_using_vsync() const {
|
||||
return use_vsync;
|
||||
}
|
||||
|
||||
NSOpenGLContext *GLManagerLegacy_MacOS::get_context(DisplayServer::WindowID p_window_id) {
|
||||
if (!windows.has(p_window_id)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GLWindow &win = windows[p_window_id];
|
||||
return win.context;
|
||||
}
|
||||
|
||||
GLManagerLegacy_MacOS::GLManagerLegacy_MacOS() {
|
||||
NSBundle *framework = [NSBundle bundleWithPath:@"/System/Library/Frameworks/OpenGL.framework"];
|
||||
if (framework) {
|
||||
void *library_handle = dlopen([framework.executablePath UTF8String], RTLD_NOW);
|
||||
if (library_handle) {
|
||||
CGLEnable = (CGLEnablePtr)dlsym(library_handle, "CGLEnable");
|
||||
CGLSetParameter = (CGLSetParameterPtr)dlsym(library_handle, "CGLSetParameter");
|
||||
CGLGetCurrentContext = (CGLGetCurrentContextPtr)dlsym(library_handle, "CGLGetCurrentContext");
|
||||
|
||||
framework_loaded = CGLEnable && CGLSetParameter && CGLGetCurrentContext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GLManagerLegacy_MacOS::~GLManagerLegacy_MacOS() {
|
||||
release_current();
|
||||
}
|
||||
|
||||
GODOT_CLANG_WARNING_POP
|
||||
|
||||
#endif // MACOS_ENABLED && GLES3_ENABLED
|
50
platform/macos/godot_application.h
Normal file
50
platform/macos/godot_application.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/**************************************************************************/
|
||||
/* godot_application.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/os/os.h"
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <IOKit/hidsystem/ev_keymap.h>
|
||||
|
||||
@class GodotApplicationDelegate;
|
||||
|
||||
@interface GodotApplication : NSApplication
|
||||
|
||||
extern "C" GodotApplication *GodotApp;
|
||||
|
||||
@property(readonly, nonatomic) GodotApplicationDelegate *godotDelegate;
|
||||
|
||||
- (GodotApplication *)init;
|
||||
|
||||
- (void)activateApplication;
|
||||
@end
|
196
platform/macos/godot_application.mm
Normal file
196
platform/macos/godot_application.mm
Normal file
@@ -0,0 +1,196 @@
|
||||
/**************************************************************************/
|
||||
/* godot_application.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "godot_application.h"
|
||||
|
||||
#import "display_server_macos.h"
|
||||
#import "godot_application_delegate.h"
|
||||
#import "os_macos.h"
|
||||
|
||||
GodotApplication *GodotApp = nil;
|
||||
|
||||
@interface GodotApplication ()
|
||||
- (void)forceUnbundledWindowActivationHackStep1;
|
||||
- (void)forceUnbundledWindowActivationHackStep2;
|
||||
- (void)forceUnbundledWindowActivationHackStep3;
|
||||
@end
|
||||
|
||||
@implementation GodotApplication
|
||||
|
||||
- (GodotApplication *)init {
|
||||
self = [super init];
|
||||
|
||||
GodotApp = self;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (GodotApplicationDelegate *)godotDelegate {
|
||||
return (GodotApplicationDelegate *)self.delegate;
|
||||
}
|
||||
|
||||
- (void)activateApplication {
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
|
||||
const char *bundled_id = getenv("__CFBundleIdentifier");
|
||||
NSString *nsbundleid_env = [NSString stringWithUTF8String:(bundled_id != nullptr) ? bundled_id : ""];
|
||||
NSString *nsbundleid = [[NSBundle mainBundle] bundleIdentifier];
|
||||
if (nsappname == nil || isatty(STDOUT_FILENO) || isatty(STDIN_FILENO) || isatty(STDERR_FILENO) || ![nsbundleid isEqualToString:nsbundleid_env]) {
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (!OS_MacOS::is_debugger_attached()) {
|
||||
#else
|
||||
{
|
||||
#endif
|
||||
// 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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)mediaKeyEvent:(int)key state:(BOOL)state repeat:(BOOL)repeat {
|
||||
Key keycode = Key::NONE;
|
||||
switch (key) {
|
||||
case NX_KEYTYPE_SOUND_UP: {
|
||||
keycode = Key::VOLUMEUP;
|
||||
} break;
|
||||
case NX_KEYTYPE_SOUND_DOWN: {
|
||||
keycode = Key::VOLUMEUP;
|
||||
} break;
|
||||
//NX_KEYTYPE_BRIGHTNESS_UP
|
||||
//NX_KEYTYPE_BRIGHTNESS_DOWN
|
||||
case NX_KEYTYPE_CAPS_LOCK: {
|
||||
keycode = Key::CAPSLOCK;
|
||||
} break;
|
||||
case NX_KEYTYPE_HELP: {
|
||||
keycode = Key::HELP;
|
||||
} break;
|
||||
case NX_POWER_KEY: {
|
||||
keycode = Key::STANDBY;
|
||||
} break;
|
||||
case NX_KEYTYPE_MUTE: {
|
||||
keycode = Key::VOLUMEMUTE;
|
||||
} break;
|
||||
//NX_KEYTYPE_CONTRAST_UP
|
||||
//NX_KEYTYPE_CONTRAST_DOWN
|
||||
//NX_KEYTYPE_LAUNCH_PANEL
|
||||
//NX_KEYTYPE_EJECT
|
||||
//NX_KEYTYPE_VIDMIRROR
|
||||
//NX_KEYTYPE_FAST
|
||||
//NX_KEYTYPE_REWIND
|
||||
//NX_KEYTYPE_ILLUMINATION_UP
|
||||
//NX_KEYTYPE_ILLUMINATION_DOWN
|
||||
//NX_KEYTYPE_ILLUMINATION_TOGGLE
|
||||
case NX_KEYTYPE_PLAY: {
|
||||
keycode = Key::MEDIAPLAY;
|
||||
} break;
|
||||
case NX_KEYTYPE_NEXT: {
|
||||
keycode = Key::MEDIANEXT;
|
||||
} break;
|
||||
case NX_KEYTYPE_PREVIOUS: {
|
||||
keycode = Key::MEDIAPREVIOUS;
|
||||
} break;
|
||||
default: {
|
||||
keycode = Key::NONE;
|
||||
} break;
|
||||
}
|
||||
|
||||
DisplayServerMacOS *ds = Object::cast_to<DisplayServerMacOS>(DisplayServer::get_singleton());
|
||||
if (ds && keycode != Key::NONE) {
|
||||
DisplayServerMacOS::KeyEvent ke;
|
||||
|
||||
ke.window_id = ds->_get_focused_window_or_popup();
|
||||
ke.macos_state = 0;
|
||||
ke.pressed = state;
|
||||
ke.echo = repeat;
|
||||
ke.keycode = keycode;
|
||||
ke.physical_keycode = keycode;
|
||||
ke.key_label = keycode;
|
||||
ke.unicode = 0;
|
||||
ke.raw = true;
|
||||
|
||||
ds->push_to_key_event_buffer(ke);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)sendEvent:(NSEvent *)event {
|
||||
if ([event type] == NSEventTypeSystemDefined && [event subtype] == 8) {
|
||||
int keyCode = (([event data1] & 0xFFFF0000) >> 16);
|
||||
int keyFlags = ([event data1] & 0x0000FFFF);
|
||||
int keyState = (((keyFlags & 0xFF00) >> 8)) == 0xA;
|
||||
int keyRepeat = (keyFlags & 0x1);
|
||||
|
||||
[self mediaKeyEvent:keyCode state:keyState repeat:keyRepeat];
|
||||
}
|
||||
|
||||
DisplayServerMacOS *ds = Object::cast_to<DisplayServerMacOS>(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];
|
||||
}
|
||||
}
|
||||
|
||||
- (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];
|
||||
}
|
||||
|
||||
@end
|
48
platform/macos/godot_application_delegate.h
Normal file
48
platform/macos/godot_application_delegate.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/**************************************************************************/
|
||||
/* godot_application_delegate.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/os/os.h"
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
class OS_MacOS_NSApp;
|
||||
|
||||
@interface GodotApplicationDelegate : NSObject <NSUserInterfaceItemSearching, NSApplicationDelegate>
|
||||
|
||||
- (GodotApplicationDelegate *)initWithOS:(OS_MacOS_NSApp *)os;
|
||||
|
||||
- (bool)getHighContrast;
|
||||
- (bool)getReduceMotion;
|
||||
- (bool)getReduceTransparency;
|
||||
- (bool)getVoiceOver;
|
||||
@end
|
324
platform/macos/godot_application_delegate.mm
Normal file
324
platform/macos/godot_application_delegate.mm
Normal file
@@ -0,0 +1,324 @@
|
||||
/**************************************************************************/
|
||||
/* godot_application_delegate.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "godot_application_delegate.h"
|
||||
|
||||
#import "display_server_macos.h"
|
||||
#import "key_mapping_macos.h"
|
||||
#import "native_menu_macos.h"
|
||||
#import "os_macos.h"
|
||||
|
||||
#import "main/main.h"
|
||||
|
||||
#import <Carbon/Carbon.h>
|
||||
|
||||
@interface GodotApplicationDelegate ()
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;
|
||||
- (void)accessibilityDisplayOptionsChange:(NSNotification *)notification;
|
||||
@end
|
||||
|
||||
@implementation GodotApplicationDelegate {
|
||||
bool high_contrast;
|
||||
bool reduce_motion;
|
||||
bool reduce_transparency;
|
||||
bool voice_over;
|
||||
OS_MacOS_NSApp *os_mac;
|
||||
}
|
||||
|
||||
- (GodotApplicationDelegate *)initWithOS:(OS_MacOS_NSApp *)os {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
os_mac = os;
|
||||
}
|
||||
|
||||
[[NSWorkspace sharedWorkspace] addObserver:self forKeyPath:@"voiceOverEnabled" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:(void *)godot_ac_ctx];
|
||||
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(accessibilityDisplayOptionsChange:) name:NSWorkspaceAccessibilityDisplayOptionsDidChangeNotification object:nil];
|
||||
high_contrast = [[NSWorkspace sharedWorkspace] accessibilityDisplayShouldIncreaseContrast];
|
||||
reduce_motion = [[NSWorkspace sharedWorkspace] accessibilityDisplayShouldReduceMotion];
|
||||
reduce_transparency = [[NSWorkspace sharedWorkspace] accessibilityDisplayShouldReduceTransparency];
|
||||
voice_over = [[NSWorkspace sharedWorkspace] isVoiceOverEnabled];
|
||||
|
||||
[[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(system_theme_changed:) name:@"AppleInterfaceThemeChangedNotification" object:nil];
|
||||
[[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(system_theme_changed:) name:@"AppleColorPreferencesChangedNotification" object:nil];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)app {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)localizedTitlesForItem:(id)item {
|
||||
NSArray *item_name = @[ item[1] ];
|
||||
return item_name;
|
||||
}
|
||||
|
||||
- (void)searchForItemsWithSearchString:(NSString *)searchString resultLimit:(NSInteger)resultLimit matchedItemHandler:(void (^)(NSArray *items))handleMatchedItems {
|
||||
NSMutableArray *found_items = [[NSMutableArray alloc] init];
|
||||
|
||||
DisplayServerMacOS *ds = Object::cast_to<DisplayServerMacOS>(DisplayServer::get_singleton());
|
||||
if (ds && ds->_help_get_search_callback().is_valid()) {
|
||||
Callable cb = ds->_help_get_search_callback();
|
||||
|
||||
Variant ret;
|
||||
Variant search_string = String::utf8([searchString UTF8String]);
|
||||
Variant result_limit = (uint64_t)resultLimit;
|
||||
Callable::CallError ce;
|
||||
const Variant *args[2] = { &search_string, &result_limit };
|
||||
|
||||
cb.callp(args, 2, ret, ce);
|
||||
if (ce.error != Callable::CallError::CALL_OK) {
|
||||
ERR_PRINT(vformat(RTR("Failed to execute help search callback: %s."), Variant::get_callable_error_text(cb, args, 2, ce)));
|
||||
}
|
||||
Dictionary results = ret;
|
||||
for (const Variant *E = results.next(); E; E = results.next(E)) {
|
||||
const String &key = *E;
|
||||
const String &value = results[*E];
|
||||
if (key.length() > 0 && value.length() > 0) {
|
||||
NSArray *item = @[ [NSString stringWithUTF8String:key.utf8().get_data()], [NSString stringWithUTF8String:value.utf8().get_data()] ];
|
||||
[found_items addObject:item];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleMatchedItems(found_items);
|
||||
}
|
||||
|
||||
- (void)performActionForItem:(id)item {
|
||||
DisplayServerMacOS *ds = Object::cast_to<DisplayServerMacOS>(DisplayServer::get_singleton());
|
||||
if (ds && ds->_help_get_action_callback().is_valid()) {
|
||||
Callable cb = ds->_help_get_action_callback();
|
||||
|
||||
Variant ret;
|
||||
Variant item_string = String::utf8([item[0] UTF8String]);
|
||||
Callable::CallError ce;
|
||||
const Variant *args[1] = { &item_string };
|
||||
|
||||
cb.callp(args, 1, ret, ce);
|
||||
if (ce.error != Callable::CallError::CALL_OK) {
|
||||
ERR_PRINT(vformat(RTR("Failed to execute help action callback: %s."), Variant::get_callable_error_text(cb, args, 1, ce)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)system_theme_changed:(NSNotification *)notification {
|
||||
DisplayServerMacOS *ds = Object::cast_to<DisplayServerMacOS>(DisplayServer::get_singleton());
|
||||
if (ds) {
|
||||
ds->emit_system_theme_changed();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
|
||||
os_mac->start_main();
|
||||
}
|
||||
|
||||
static const char *godot_ac_ctx = "gd_accessibility_observer_ctx";
|
||||
|
||||
- (void)dealloc {
|
||||
[[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:@"AppleInterfaceThemeChangedNotification" object:nil];
|
||||
[[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:@"AppleColorPreferencesChangedNotification" object:nil];
|
||||
[[NSWorkspace sharedWorkspace] removeObserver:self forKeyPath:@"voiceOverEnabled" context:(void *)godot_ac_ctx];
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
|
||||
if (context == (void *)godot_ac_ctx) {
|
||||
voice_over = [[NSWorkspace sharedWorkspace] isVoiceOverEnabled];
|
||||
} else {
|
||||
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)accessibilityDisplayOptionsChange:(NSNotification *)notification {
|
||||
high_contrast = [[NSWorkspace sharedWorkspace] accessibilityDisplayShouldIncreaseContrast];
|
||||
reduce_motion = [[NSWorkspace sharedWorkspace] accessibilityDisplayShouldReduceMotion];
|
||||
reduce_transparency = [[NSWorkspace sharedWorkspace] accessibilityDisplayShouldReduceTransparency];
|
||||
}
|
||||
|
||||
- (bool)getHighContrast {
|
||||
return high_contrast;
|
||||
}
|
||||
|
||||
- (bool)getReduceMotion {
|
||||
return reduce_motion;
|
||||
}
|
||||
|
||||
- (bool)getReduceTransparency {
|
||||
return reduce_transparency;
|
||||
}
|
||||
|
||||
- (bool)getVoiceOver {
|
||||
return voice_over;
|
||||
}
|
||||
|
||||
- (void)application:(NSApplication *)application openURLs:(NSArray<NSURL *> *)urls {
|
||||
List<String> args;
|
||||
for (NSURL *url in urls) {
|
||||
if ([url isFileURL]) {
|
||||
args.push_back(String::utf8([url.path UTF8String]));
|
||||
} else {
|
||||
args.push_back(vformat("--uri=\"%s\"", String::utf8([url.absoluteString UTF8String])));
|
||||
}
|
||||
}
|
||||
if (!args.is_empty()) {
|
||||
if (os_mac->get_main_loop()) {
|
||||
// Application is already running, open a new instance with the URL/files as command line arguments.
|
||||
os_mac->create_instance(args);
|
||||
} else if (os_mac->get_cmd_argc() == 0) {
|
||||
// Application is just started, add to the list of command line arguments and continue.
|
||||
os_mac->set_cmdline_platform_args(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)application:(NSApplication *)sender openFiles:(NSArray<NSString *> *)filenames {
|
||||
List<String> args;
|
||||
for (NSString *filename in filenames) {
|
||||
NSURL *url = [NSURL URLWithString:filename];
|
||||
args.push_back(String::utf8([url.path UTF8String]));
|
||||
}
|
||||
if (!args.is_empty()) {
|
||||
if (os_mac->get_main_loop()) {
|
||||
// Application is already running, open a new instance with the URL/files as command line arguments.
|
||||
os_mac->create_instance(args);
|
||||
} else if (os_mac->get_cmd_argc() == 0) {
|
||||
// Application is just started, add to the list of command line arguments and continue.
|
||||
os_mac->set_cmdline_platform_args(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationDidResignActive:(NSNotification *)notification {
|
||||
DisplayServerMacOS *ds = Object::cast_to<DisplayServerMacOS>(DisplayServer::get_singleton());
|
||||
if (ds) {
|
||||
ds->mouse_process_popups(true);
|
||||
}
|
||||
if (os_mac->get_main_loop()) {
|
||||
os_mac->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT);
|
||||
}
|
||||
}
|
||||
|
||||
static const CGKeyCode modifiers[8] = {
|
||||
kVK_Command,
|
||||
kVK_RightCommand,
|
||||
kVK_Shift,
|
||||
kVK_RightShift,
|
||||
kVK_Option,
|
||||
kVK_RightOption,
|
||||
kVK_Control,
|
||||
kVK_RightControl,
|
||||
};
|
||||
|
||||
// The list of modifier flags we care about for raising pressed events when the application becomes active.
|
||||
constexpr static NSEventModifierFlags FLAGS = NSEventModifierFlagCommand | NSEventModifierFlagShift | NSEventModifierFlagOption | NSEventModifierFlagControl;
|
||||
|
||||
- (void)applicationDidBecomeActive:(NSNotification *)notification {
|
||||
if (os_mac->get_main_loop()) {
|
||||
os_mac->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN);
|
||||
}
|
||||
DisplayServerMacOS *ds = Object::cast_to<DisplayServerMacOS>(DisplayServer::get_singleton());
|
||||
if (!ds) {
|
||||
return;
|
||||
}
|
||||
Input *input = Input::get_singleton();
|
||||
if (!input) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Poll the modifier keys and submit pressed events if they are down when the application becomes active.
|
||||
int mod = NSEvent.modifierFlags;
|
||||
if ((mod & FLAGS) == 0) {
|
||||
// No flags we care about.
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayServer::WindowID window_id = ds->get_focused_window();
|
||||
NSEventModifierFlags flags = static_cast<NSEventModifierFlags>(mod);
|
||||
|
||||
for (const CGKeyCode key : modifiers) {
|
||||
bool is_down = CGEventSourceKeyState(kCGEventSourceStateHIDSystemState, key);
|
||||
if (likely(!is_down)) {
|
||||
continue;
|
||||
}
|
||||
Ref<InputEventKey> ke;
|
||||
ke.instantiate();
|
||||
|
||||
ke->set_window_id(window_id);
|
||||
ke->set_echo(false);
|
||||
ke->set_pressed(true);
|
||||
ds->get_key_modifier_state(flags, ke);
|
||||
ke->set_keycode(KeyMappingMacOS::remap_key(key, mod, false));
|
||||
ke->set_physical_keycode(KeyMappingMacOS::translate_key(key));
|
||||
ke->set_key_label(KeyMappingMacOS::remap_key(key, mod, true));
|
||||
ke->set_location(KeyMappingMacOS::translate_location(key));
|
||||
input->parse_input_event(ke);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)globalMenuCallback:(id)sender {
|
||||
DisplayServerMacOS *ds = Object::cast_to<DisplayServerMacOS>(DisplayServer::get_singleton());
|
||||
if (ds) {
|
||||
return ds->menu_callback(sender);
|
||||
}
|
||||
}
|
||||
|
||||
- (NSMenu *)applicationDockMenu:(NSApplication *)sender {
|
||||
if (NativeMenu::get_singleton()) {
|
||||
NativeMenuMacOS *nmenu = (NativeMenuMacOS *)NativeMenu::get_singleton();
|
||||
return nmenu->_get_dock_menu();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(NSNotification *)notification {
|
||||
os_mac->cleanup();
|
||||
exit(os_mac->get_exit_code());
|
||||
}
|
||||
|
||||
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
|
||||
if (os_mac->os_should_terminate()) {
|
||||
return NSTerminateNow;
|
||||
}
|
||||
|
||||
DisplayServerMacOS *ds = Object::cast_to<DisplayServerMacOS>(DisplayServer::get_singleton());
|
||||
if (ds && ds->has_window(DisplayServerMacOS::MAIN_WINDOW_ID)) {
|
||||
ds->send_window_event(ds->get_window(DisplayServerMacOS::MAIN_WINDOW_ID), DisplayServerMacOS::WINDOW_EVENT_CLOSE_REQUEST);
|
||||
}
|
||||
|
||||
return NSTerminateCancel;
|
||||
}
|
||||
|
||||
- (void)showAbout:(id)sender {
|
||||
if (os_mac->get_main_loop()) {
|
||||
os_mac->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_ABOUT);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
54
platform/macos/godot_button_view.h
Normal file
54
platform/macos/godot_button_view.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/**************************************************************************/
|
||||
/* godot_button_view.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "servers/display_server.h"
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface GodotButtonView : NSView {
|
||||
NSTrackingArea *tracking_area;
|
||||
NSPoint offset;
|
||||
CGFloat spacing;
|
||||
bool mouse_in_group;
|
||||
bool rtl;
|
||||
NSButton *close_button;
|
||||
NSButton *miniaturize_button;
|
||||
NSButton *zoom_button;
|
||||
}
|
||||
|
||||
- (void)initButtons:(CGFloat)button_spacing offset:(NSPoint)button_offset rtl:(bool)is_rtl;
|
||||
- (void)displayButtons;
|
||||
- (void)setOffset:(NSPoint)button_offset;
|
||||
- (NSPoint)getOffset;
|
||||
|
||||
@end
|
139
platform/macos/godot_button_view.mm
Normal file
139
platform/macos/godot_button_view.mm
Normal file
@@ -0,0 +1,139 @@
|
||||
/**************************************************************************/
|
||||
/* godot_button_view.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "godot_button_view.h"
|
||||
|
||||
@implementation GodotButtonView
|
||||
|
||||
- (id)initWithFrame:(NSRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
|
||||
tracking_area = nil;
|
||||
offset = NSMakePoint(8, 8);
|
||||
spacing = 20;
|
||||
mouse_in_group = false;
|
||||
rtl = false;
|
||||
close_button = nullptr;
|
||||
miniaturize_button = nullptr;
|
||||
zoom_button = nullptr;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)initButtons:(CGFloat)button_spacing offset:(NSPoint)button_offset rtl:(bool)is_rtl {
|
||||
spacing = button_spacing;
|
||||
rtl = is_rtl;
|
||||
|
||||
close_button = [NSWindow standardWindowButton:NSWindowCloseButton forStyleMask:NSWindowStyleMaskTitled];
|
||||
[close_button setFrameOrigin:NSMakePoint(rtl ? spacing * 2 : 0, 0)];
|
||||
[self addSubview:close_button];
|
||||
|
||||
miniaturize_button = [NSWindow standardWindowButton:NSWindowMiniaturizeButton forStyleMask:NSWindowStyleMaskTitled];
|
||||
[miniaturize_button setFrameOrigin:NSMakePoint(spacing, 0)];
|
||||
[self addSubview:miniaturize_button];
|
||||
|
||||
zoom_button = [NSWindow standardWindowButton:NSWindowZoomButton forStyleMask:NSWindowStyleMaskTitled];
|
||||
[zoom_button setFrameOrigin:NSMakePoint(rtl ? 0 : spacing * 2, 0)];
|
||||
[self addSubview:zoom_button];
|
||||
|
||||
offset.y = button_offset.y - zoom_button.frame.size.height / 2;
|
||||
offset.x = button_offset.x - zoom_button.frame.size.width / 2;
|
||||
|
||||
if (rtl) {
|
||||
[self setFrameSize:NSMakeSize(close_button.frame.origin.x + close_button.frame.size.width, close_button.frame.size.height)];
|
||||
} else {
|
||||
[self setFrameSize:NSMakeSize(zoom_button.frame.origin.x + zoom_button.frame.size.width, zoom_button.frame.size.height)];
|
||||
}
|
||||
[self displayButtons];
|
||||
}
|
||||
|
||||
- (void)setOffset:(NSPoint)button_offset {
|
||||
if (zoom_button) {
|
||||
offset.y = button_offset.y - zoom_button.frame.size.height / 2;
|
||||
offset.x = button_offset.x - zoom_button.frame.size.width / 2;
|
||||
|
||||
[self viewDidMoveToWindow];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSPoint)getOffset {
|
||||
return offset;
|
||||
}
|
||||
|
||||
- (void)viewDidMoveToWindow {
|
||||
if (!self.window) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (rtl) {
|
||||
[self setAutoresizingMask:NSViewMinXMargin | NSViewMinYMargin];
|
||||
[self setFrameOrigin:NSMakePoint(self.window.frame.size.width - self.frame.size.width - offset.x, self.window.frame.size.height - self.frame.size.height - offset.y)];
|
||||
} else {
|
||||
[self setAutoresizingMask:NSViewMaxXMargin | NSViewMinYMargin];
|
||||
[self setFrameOrigin:NSMakePoint(offset.x, self.window.frame.size.height - self.frame.size.height - offset.y)];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)_mouseInGroup:(NSButton *)button {
|
||||
return mouse_in_group;
|
||||
}
|
||||
|
||||
- (void)updateTrackingAreas {
|
||||
if (tracking_area != nil) {
|
||||
[self removeTrackingArea:tracking_area];
|
||||
}
|
||||
|
||||
NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | NSTrackingInVisibleRect;
|
||||
tracking_area = [[NSTrackingArea alloc] initWithRect:NSZeroRect options:options owner:self userInfo:nil];
|
||||
|
||||
[self addTrackingArea:tracking_area];
|
||||
}
|
||||
|
||||
- (void)mouseEntered:(NSEvent *)event {
|
||||
[super mouseEntered:event];
|
||||
|
||||
mouse_in_group = true;
|
||||
[self displayButtons];
|
||||
}
|
||||
|
||||
- (void)mouseExited:(NSEvent *)event {
|
||||
[super mouseExited:event];
|
||||
|
||||
mouse_in_group = false;
|
||||
[self displayButtons];
|
||||
}
|
||||
|
||||
- (void)displayButtons {
|
||||
for (NSView *subview in self.subviews) {
|
||||
[subview setNeedsDisplay:YES];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
81
platform/macos/godot_content_view.h
Normal file
81
platform/macos/godot_content_view.h
Normal file
@@ -0,0 +1,81 @@
|
||||
/**************************************************************************/
|
||||
/* godot_content_view.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#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 GodotContentLayerDelegate : NSObject <CALayerDelegate> {
|
||||
DisplayServer::WindowID window_id;
|
||||
bool need_redraw;
|
||||
}
|
||||
|
||||
- (void)setWindowID:(DisplayServer::WindowID)wid;
|
||||
- (void)setNeedRedraw:(bool)redraw;
|
||||
|
||||
@end
|
||||
|
||||
GODOT_CLANG_WARNING_PUSH_AND_IGNORE("-Wdeprecated-declarations") // OpenGL is deprecated in macOS 10.14.
|
||||
|
||||
@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;
|
||||
bool ime_suppress_next_keyup;
|
||||
id layer_delegate;
|
||||
NSMutableSet<NSString *> *registered_observers;
|
||||
}
|
||||
|
||||
- (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 pressed:(bool)pressed outofstream:(bool)outofstream;
|
||||
- (void)setWindowID:(DisplayServer::WindowID)wid;
|
||||
- (void)updateLayerDelegate;
|
||||
- (void)cancelComposition;
|
||||
|
||||
@end
|
||||
|
||||
GODOT_CLANG_WARNING_POP
|
915
platform/macos/godot_content_view.mm
Normal file
915
platform/macos/godot_content_view.mm
Normal file
@@ -0,0 +1,915 @@
|
||||
/**************************************************************************/
|
||||
/* godot_content_view.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "godot_content_view.h"
|
||||
|
||||
#import "display_server_macos.h"
|
||||
#import "godot_window.h"
|
||||
#import "key_mapping_macos.h"
|
||||
|
||||
#include "main/main.h"
|
||||
|
||||
@implementation GodotContentLayerDelegate
|
||||
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
window_id = DisplayServer::INVALID_WINDOW_ID;
|
||||
need_redraw = false;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setWindowID:(DisplayServerMacOS::WindowID)wid {
|
||||
window_id = wid;
|
||||
}
|
||||
|
||||
- (void)setNeedRedraw:(bool)redraw {
|
||||
need_redraw = redraw;
|
||||
}
|
||||
|
||||
- (void)displayLayer:(CALayer *)layer {
|
||||
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
|
||||
if (OS::get_singleton()->get_main_loop() && ds->get_is_resizing() && need_redraw) {
|
||||
Main::force_redraw();
|
||||
if (!Main::is_iterating()) { // Avoid cyclic loop.
|
||||
Main::iteration();
|
||||
}
|
||||
need_redraw = false;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GodotContentView
|
||||
|
||||
- (BOOL)acceptsFirstMouse:(NSEvent *)event {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)setFrameSize:(NSSize)newSize {
|
||||
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
|
||||
if (ds && ds->has_window(window_id)) {
|
||||
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
|
||||
NSRect frameRect = [wd.window_object frame];
|
||||
if (wd.fs_transition || wd.initial_size) {
|
||||
self.layerContentsPlacement = NSViewLayerContentsPlacementScaleAxesIndependently;
|
||||
wd.initial_size = false;
|
||||
} else {
|
||||
bool left = (wd.last_frame_rect.origin.x != frameRect.origin.x);
|
||||
bool bottom = (wd.last_frame_rect.origin.y != frameRect.origin.y);
|
||||
bool right = (wd.last_frame_rect.origin.x + wd.last_frame_rect.size.width != frameRect.origin.x + frameRect.size.width);
|
||||
bool top = (wd.last_frame_rect.origin.y + wd.last_frame_rect.size.height != frameRect.origin.y + frameRect.size.height);
|
||||
|
||||
if (left && top) {
|
||||
self.layerContentsPlacement = NSViewLayerContentsPlacementBottomRight;
|
||||
} else if (left && bottom) {
|
||||
self.layerContentsPlacement = NSViewLayerContentsPlacementTopRight;
|
||||
} else if (left) {
|
||||
self.layerContentsPlacement = NSViewLayerContentsPlacementRight;
|
||||
} else if (right && top) {
|
||||
self.layerContentsPlacement = NSViewLayerContentsPlacementBottomLeft;
|
||||
} else if (right && bottom) {
|
||||
self.layerContentsPlacement = NSViewLayerContentsPlacementTopLeft;
|
||||
} else if (right) {
|
||||
self.layerContentsPlacement = NSViewLayerContentsPlacementLeft;
|
||||
}
|
||||
}
|
||||
wd.last_frame_rect = frameRect;
|
||||
}
|
||||
|
||||
[super setFrameSize:newSize];
|
||||
[layer_delegate setNeedRedraw:true];
|
||||
[self.layer setNeedsDisplay]; // Force "drawRect" call.
|
||||
}
|
||||
|
||||
- (void)updateLayerDelegate {
|
||||
self.layer.delegate = layer_delegate;
|
||||
self.layer.autoresizingMask = kCALayerHeightSizable | kCALayerWidthSizable;
|
||||
self.layer.needsDisplayOnBoundsChange = YES;
|
||||
}
|
||||
|
||||
- (void)addObserver:(NSObject *)targetObserver forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context {
|
||||
[registered_observers addObject:[[targetObserver description] stringByAppendingString:keyPath]];
|
||||
[super addObserver:targetObserver forKeyPath:keyPath options:options context:context];
|
||||
}
|
||||
|
||||
- (void)removeObserver:(NSObject *)targetObserver forKeyPath:(NSString *)keyPath {
|
||||
if ([registered_observers containsObject:[[targetObserver description] stringByAppendingString:keyPath]]) {
|
||||
@try {
|
||||
[super removeObserver:targetObserver forKeyPath:keyPath];
|
||||
[registered_observers removeObject:[[targetObserver description] stringByAppendingString:keyPath]];
|
||||
} @catch (NSException *exception) {
|
||||
ERR_PRINT("NSException: " + String::utf8([exception reason].UTF8String));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
layer_delegate = [[GodotContentLayerDelegate alloc] 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;
|
||||
registered_observers = [[NSMutableSet alloc] init];
|
||||
[self updateTrackingAreas];
|
||||
|
||||
self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize;
|
||||
self.layerContentsPlacement = NSViewLayerContentsPlacementTopLeft;
|
||||
|
||||
[self registerForDraggedTypes:[NSArray arrayWithObject:NSPasteboardTypeFileURL]];
|
||||
marked_text = [[NSMutableAttributedString alloc] init];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setWindowID:(DisplayServerMacOS::WindowID)wid {
|
||||
window_id = wid;
|
||||
[layer_delegate setWindowID:window_id];
|
||||
}
|
||||
|
||||
// MARK: Backing Layer
|
||||
|
||||
- (CALayer *)makeBackingLayer {
|
||||
CAMetalLayer *layer = [CAMetalLayer new];
|
||||
layer.edgeAntialiasingMask = 0;
|
||||
layer.masksToBounds = NO;
|
||||
layer.presentsWithTransaction = NO;
|
||||
[layer removeAllAnimations];
|
||||
return layer;
|
||||
}
|
||||
|
||||
- (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;
|
||||
}
|
||||
|
||||
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
|
||||
if (!ds || !ds->has_window(window_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
|
||||
if (wd.im_active) {
|
||||
ime_input_event_in_progress = true;
|
||||
ds->pop_last_key_event();
|
||||
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 {
|
||||
if (ime_input_event_in_progress) {
|
||||
ime_suppress_next_keyup = true;
|
||||
}
|
||||
ime_input_event_in_progress = false;
|
||||
[[marked_text mutableString] setString:@""];
|
||||
|
||||
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
|
||||
if (!ds || !ds->has_window(window_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayServerMacOS::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 {
|
||||
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
|
||||
if (!ds || !ds->has_window(window_id)) {
|
||||
return NSMakeRect(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
DisplayServerMacOS::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 {
|
||||
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;
|
||||
}
|
||||
|
||||
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
|
||||
if (!ds || !ds->has_window(window_id)) {
|
||||
[self cancelComposition];
|
||||
return;
|
||||
}
|
||||
|
||||
Char16String text;
|
||||
text.resize_uninitialized([characters length] + 1);
|
||||
[characters getCharacters:(unichar *)text.ptrw() range:NSMakeRange(0, [characters length])];
|
||||
|
||||
String u32text = String::utf16(text.ptr(), text.length());
|
||||
|
||||
for (int i = 0; i < u32text.length(); i++) {
|
||||
const char32_t codepoint = u32text[i];
|
||||
if ((codepoint & 0xFF00) == 0xF700) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DisplayServerMacOS::KeyEvent ke;
|
||||
|
||||
ke.window_id = window_id;
|
||||
ke.macos_state = 0;
|
||||
ke.pressed = true;
|
||||
ke.echo = false;
|
||||
ke.raw = false; // IME input event.
|
||||
ke.keycode = Key::NONE;
|
||||
ke.physical_keycode = Key::NONE;
|
||||
ke.key_label = Key::NONE;
|
||||
ke.unicode = fix_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 {
|
||||
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
|
||||
if (!ds || !ds->has_window(window_id)) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
|
||||
if (wd.drop_files_callback.is_valid()) {
|
||||
Vector<String> files;
|
||||
NSPasteboard *pboard = [sender draggingPasteboard];
|
||||
|
||||
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]));
|
||||
}
|
||||
Variant v_files = files;
|
||||
const Variant *v_args[1] = { &v_files };
|
||||
Variant ret;
|
||||
Callable::CallError ce;
|
||||
wd.drop_files_callback.callp((const Variant **)&v_args, 1, ret, ce);
|
||||
if (ce.error != Callable::CallError::CALL_OK) {
|
||||
ERR_FAIL_V_MSG(NO, vformat("Failed to execute drop files callback: %s.", Variant::get_callable_error_text(wd.drop_files_callback, v_args, 1, ce)));
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
// MARK: Focus
|
||||
|
||||
- (BOOL)canBecomeKeyView {
|
||||
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
|
||||
if (!ds || !ds->has_window(window_id)) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
|
||||
return !wd.no_focus;
|
||||
}
|
||||
|
||||
- (BOOL)acceptsFirstResponder {
|
||||
return YES;
|
||||
}
|
||||
|
||||
// MARK: Mouse
|
||||
|
||||
- (void)cursorUpdate:(NSEvent *)event {
|
||||
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
|
||||
if (!ds) {
|
||||
return;
|
||||
}
|
||||
|
||||
ds->cursor_update_shape();
|
||||
}
|
||||
|
||||
- (void)processMouseEvent:(NSEvent *)event index:(MouseButton)index pressed:(bool)pressed outofstream:(bool)outofstream {
|
||||
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
|
||||
if (!ds || !ds->has_window(window_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
|
||||
|
||||
Ref<InputEventMouseButton> mb;
|
||||
mb.instantiate();
|
||||
mb->set_window_id(window_id);
|
||||
if (outofstream) {
|
||||
ds->update_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream]);
|
||||
} else {
|
||||
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(ds->mouse_get_button_state());
|
||||
if (!outofstream && index == MouseButton::LEFT && pressed) {
|
||||
mb->set_double_click([event clickCount] == 2);
|
||||
}
|
||||
|
||||
Input::get_singleton()->parse_input_event(mb);
|
||||
}
|
||||
|
||||
- (void)mouseDown:(NSEvent *)event {
|
||||
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
|
||||
if (ds && ds->has_window(window_id)) {
|
||||
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
|
||||
wd.edge = DisplayServer::WINDOW_EDGE_MAX;
|
||||
}
|
||||
if (([event modifierFlags] & NSEventModifierFlagControl)) {
|
||||
mouse_down_control = true;
|
||||
[self processMouseEvent:event index:MouseButton::RIGHT pressed:true outofstream:false];
|
||||
} else {
|
||||
mouse_down_control = false;
|
||||
[self processMouseEvent:event index:MouseButton::LEFT pressed:true outofstream:false];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)mouseDragged:(NSEvent *)event {
|
||||
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
|
||||
if (ds && ds->has_window(window_id)) {
|
||||
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
|
||||
if (wd.edge != DisplayServer::WINDOW_EDGE_MAX) {
|
||||
Size2i max_size = wd.max_size / ds->screen_get_max_scale();
|
||||
Size2i min_size = wd.min_size / ds->screen_get_max_scale();
|
||||
NSRect frame = [wd.window_object frame];
|
||||
switch (wd.edge) {
|
||||
case DisplayServer::WINDOW_EDGE_TOP_LEFT: {
|
||||
int clamped_dx = CLAMP(frame.size.width - event.deltaX, min_size.x, max_size.x) - frame.size.width;
|
||||
int clamped_dy = CLAMP(frame.size.height - event.deltaY, min_size.y, max_size.y) - frame.size.height;
|
||||
[wd.window_object setFrame:NSMakeRect(frame.origin.x - clamped_dx, frame.origin.y, frame.size.width + clamped_dx, frame.size.height + clamped_dy) display:YES];
|
||||
} break;
|
||||
case DisplayServer::WINDOW_EDGE_TOP: {
|
||||
int clamped_dy = CLAMP(frame.size.height - event.deltaY, min_size.y, max_size.y) - frame.size.height;
|
||||
[wd.window_object setFrame:NSMakeRect(frame.origin.x, frame.origin.y, frame.size.width, frame.size.height + clamped_dy) display:YES];
|
||||
} break;
|
||||
case DisplayServer::WINDOW_EDGE_TOP_RIGHT: {
|
||||
int clamped_dx = CLAMP(frame.size.width + event.deltaX, min_size.x, max_size.x) - frame.size.width;
|
||||
int clamped_dy = CLAMP(frame.size.height - event.deltaY, min_size.y, max_size.y) - frame.size.height;
|
||||
[wd.window_object setFrame:NSMakeRect(frame.origin.x, frame.origin.y, frame.size.width + clamped_dx, frame.size.height + clamped_dy) display:YES];
|
||||
} break;
|
||||
case DisplayServer::WINDOW_EDGE_LEFT: {
|
||||
int clamped_dx = CLAMP(frame.size.width - event.deltaX, min_size.x, max_size.x) - frame.size.width;
|
||||
[wd.window_object setFrame:NSMakeRect(frame.origin.x - clamped_dx, frame.origin.y, frame.size.width + clamped_dx, frame.size.height) display:YES];
|
||||
} break;
|
||||
case DisplayServer::WINDOW_EDGE_RIGHT: {
|
||||
int clamped_dx = CLAMP(frame.size.width + event.deltaX, min_size.x, max_size.x) - frame.size.width;
|
||||
[wd.window_object setFrame:NSMakeRect(frame.origin.x, frame.origin.y, frame.size.width + clamped_dx, frame.size.height) display:YES];
|
||||
} break;
|
||||
case DisplayServer::WINDOW_EDGE_BOTTOM_LEFT: {
|
||||
int clamped_dx = CLAMP(frame.size.width - event.deltaX, min_size.x, max_size.x) - frame.size.width;
|
||||
int clamped_dy = CLAMP(frame.size.height + event.deltaY, min_size.y, max_size.y) - frame.size.height;
|
||||
[wd.window_object setFrame:NSMakeRect(frame.origin.x - clamped_dx, frame.origin.y - clamped_dy, frame.size.width + clamped_dx, frame.size.height + clamped_dy) display:YES];
|
||||
} break;
|
||||
case DisplayServer::WINDOW_EDGE_BOTTOM: {
|
||||
int clamped_dy = CLAMP(frame.size.height + event.deltaY, min_size.y, max_size.y) - frame.size.height;
|
||||
[wd.window_object setFrame:NSMakeRect(frame.origin.x, frame.origin.y - clamped_dy, frame.size.width, frame.size.height + clamped_dy) display:YES];
|
||||
} break;
|
||||
case DisplayServer::WINDOW_EDGE_BOTTOM_RIGHT: {
|
||||
int clamped_dx = CLAMP(frame.size.width + event.deltaX, min_size.x, max_size.x) - frame.size.width;
|
||||
int clamped_dy = CLAMP(frame.size.height + event.deltaY, min_size.y, max_size.y) - frame.size.height;
|
||||
[wd.window_object setFrame:NSMakeRect(frame.origin.x, frame.origin.y - clamped_dy, frame.size.width + clamped_dx, frame.size.height + clamped_dy) display:YES];
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
[self mouseMoved:event];
|
||||
}
|
||||
|
||||
- (void)mouseUp:(NSEvent *)event {
|
||||
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
|
||||
if (ds && ds->has_window(window_id)) {
|
||||
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
|
||||
wd.edge = DisplayServer::WINDOW_EDGE_MAX;
|
||||
}
|
||||
if (mouse_down_control) {
|
||||
[self processMouseEvent:event index:MouseButton::RIGHT pressed:false outofstream:false];
|
||||
} else {
|
||||
[self processMouseEvent:event index:MouseButton::LEFT pressed:false outofstream:false];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)mouseMoved:(NSEvent *)event {
|
||||
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
|
||||
if (!ds || !ds->has_window(window_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayServerMacOS::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());
|
||||
mm->set_screen_velocity(mm->get_velocity());
|
||||
const Vector2i relativeMotion = Vector2i(delta.x, delta.y) * ds->screen_get_max_scale();
|
||||
mm->set_relative(relativeMotion);
|
||||
mm->set_relative_screen_position(relativeMotion);
|
||||
ds->get_key_modifier_state([event modifierFlags], mm);
|
||||
|
||||
const NSRect contentRect = [wd.window_view frame];
|
||||
if (NSPointInRect([event locationInWindow], contentRect)) {
|
||||
ds->mouse_enter_window(window_id);
|
||||
}
|
||||
|
||||
Input::get_singleton()->parse_input_event(mm);
|
||||
}
|
||||
|
||||
- (void)rightMouseDown:(NSEvent *)event {
|
||||
[self processMouseEvent:event index:MouseButton::RIGHT pressed:true outofstream:false];
|
||||
}
|
||||
|
||||
- (void)rightMouseDragged:(NSEvent *)event {
|
||||
[self mouseMoved:event];
|
||||
}
|
||||
|
||||
- (void)rightMouseUp:(NSEvent *)event {
|
||||
[self processMouseEvent:event index:MouseButton::RIGHT pressed:false outofstream:false];
|
||||
}
|
||||
|
||||
- (void)otherMouseDown:(NSEvent *)event {
|
||||
if ((int)[event buttonNumber] == 2) {
|
||||
[self processMouseEvent:event index:MouseButton::MIDDLE pressed:true outofstream:false];
|
||||
} else if ((int)[event buttonNumber] == 3) {
|
||||
[self processMouseEvent:event index:MouseButton::MB_XBUTTON1 pressed:true outofstream:false];
|
||||
} else if ((int)[event buttonNumber] == 4) {
|
||||
[self processMouseEvent:event index:MouseButton::MB_XBUTTON2 pressed:true outofstream:false];
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)otherMouseDragged:(NSEvent *)event {
|
||||
[self mouseMoved:event];
|
||||
}
|
||||
|
||||
- (void)otherMouseUp:(NSEvent *)event {
|
||||
if ((int)[event buttonNumber] == 2) {
|
||||
[self processMouseEvent:event index:MouseButton::MIDDLE pressed:false outofstream:false];
|
||||
} else if ((int)[event buttonNumber] == 3) {
|
||||
[self processMouseEvent:event index:MouseButton::MB_XBUTTON1 pressed:false outofstream:false];
|
||||
} else if ((int)[event buttonNumber] == 4) {
|
||||
[self processMouseEvent:event index:MouseButton::MB_XBUTTON2 pressed:false outofstream:false];
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)swipeWithEvent:(NSEvent *)event {
|
||||
// Swipe gesture on Trackpad/Magic Mouse, or physical back/forward mouse buttons.
|
||||
if ([event phase] == NSEventPhaseEnded || [event phase] == NSEventPhaseChanged) {
|
||||
if (Math::is_equal_approx([event deltaX], 1.0)) {
|
||||
// Swipe left (back).
|
||||
[self processMouseEvent:event index:MouseButton::MB_XBUTTON1 pressed:true outofstream:true];
|
||||
[self processMouseEvent:event index:MouseButton::MB_XBUTTON1 pressed:false outofstream:true];
|
||||
} else if (Math::is_equal_approx([event deltaX], -1.0)) {
|
||||
// Swipe right (forward).
|
||||
[self processMouseEvent:event index:MouseButton::MB_XBUTTON2 pressed:true outofstream:true];
|
||||
[self processMouseEvent:event index:MouseButton::MB_XBUTTON2 pressed:false outofstream:true];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)mouseExited:(NSEvent *)event {
|
||||
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
|
||||
if (!ds || !ds->has_window(window_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ds->mouse_get_mode() != DisplayServer::MOUSE_MODE_CAPTURED) {
|
||||
ds->mouse_exit_window(window_id);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)mouseEntered:(NSEvent *)event {
|
||||
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
|
||||
if (!ds || !ds->has_window(window_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ds->mouse_get_mode() != DisplayServer::MOUSE_MODE_CAPTURED) {
|
||||
ds->mouse_enter_window(window_id);
|
||||
}
|
||||
|
||||
ds->cursor_update_shape();
|
||||
}
|
||||
|
||||
- (void)magnifyWithEvent:(NSEvent *)event {
|
||||
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
|
||||
if (!ds || !ds->has_window(window_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayServerMacOS::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 | NSTrackingActiveWhenFirstResponder | 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 {
|
||||
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
|
||||
if (!ds || !ds->has_window(window_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayServerMacOS::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(KeyMappingMacOS::remap_key([event keyCode], [event modifierFlags], true))) {
|
||||
// Fallback unicode character handler used if IME is not active.
|
||||
Char16String text;
|
||||
text.resize_uninitialized([characters length] + 1);
|
||||
[characters getCharacters:(unichar *)text.ptrw() range:NSMakeRange(0, [characters length])];
|
||||
|
||||
String u32text = String::utf16(text.ptr(), text.length());
|
||||
|
||||
DisplayServerMacOS::KeyEvent ke;
|
||||
ke.window_id = window_id;
|
||||
ke.macos_state = [event modifierFlags];
|
||||
ke.pressed = true;
|
||||
ke.echo = [event isARepeat];
|
||||
ke.keycode = KeyMappingMacOS::remap_key([event keyCode], [event modifierFlags], false);
|
||||
ke.physical_keycode = KeyMappingMacOS::translate_key([event keyCode]);
|
||||
ke.key_label = KeyMappingMacOS::remap_key([event keyCode], [event modifierFlags], true);
|
||||
ke.raw = true;
|
||||
|
||||
if (u32text.is_empty()) {
|
||||
ke.unicode = 0;
|
||||
ds->push_to_key_event_buffer(ke);
|
||||
}
|
||||
for (int i = 0; i < u32text.length(); i++) {
|
||||
const char32_t codepoint = u32text[i];
|
||||
ke.unicode = fix_unicode(codepoint);
|
||||
ds->push_to_key_event_buffer(ke);
|
||||
}
|
||||
} else {
|
||||
DisplayServerMacOS::KeyEvent ke;
|
||||
|
||||
ke.window_id = window_id;
|
||||
ke.macos_state = [event modifierFlags];
|
||||
ke.pressed = true;
|
||||
ke.echo = [event isARepeat];
|
||||
ke.keycode = KeyMappingMacOS::remap_key([event keyCode], [event modifierFlags], false);
|
||||
ke.physical_keycode = KeyMappingMacOS::translate_key([event keyCode]);
|
||||
ke.key_label = KeyMappingMacOS::remap_key([event keyCode], [event modifierFlags], true);
|
||||
ke.unicode = 0;
|
||||
ke.location = KeyMappingMacOS::translate_location([event keyCode]);
|
||||
ke.raw = false;
|
||||
|
||||
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 {
|
||||
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
|
||||
if (!ds || !ds->has_window(window_id)) {
|
||||
return;
|
||||
}
|
||||
ignore_momentum_scroll = true;
|
||||
|
||||
DisplayServerMacOS::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.macos_state = mod;
|
||||
ke.keycode = KeyMappingMacOS::remap_key(key, mod, false);
|
||||
ke.physical_keycode = KeyMappingMacOS::translate_key(key);
|
||||
ke.key_label = KeyMappingMacOS::remap_key(key, mod, true);
|
||||
ke.unicode = 0;
|
||||
ke.location = KeyMappingMacOS::translate_location(key);
|
||||
|
||||
ds->push_to_key_event_buffer(ke);
|
||||
}
|
||||
|
||||
- (void)keyUp:(NSEvent *)event {
|
||||
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
|
||||
if (!ds || !ds->has_window(window_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore all input if IME input is in progress.
|
||||
if (ime_suppress_next_keyup) {
|
||||
ime_suppress_next_keyup = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ime_input_event_in_progress) {
|
||||
DisplayServerMacOS::KeyEvent ke;
|
||||
|
||||
ke.window_id = window_id;
|
||||
ke.macos_state = [event modifierFlags];
|
||||
ke.pressed = false;
|
||||
ke.echo = [event isARepeat];
|
||||
ke.keycode = KeyMappingMacOS::remap_key([event keyCode], [event modifierFlags], false);
|
||||
ke.physical_keycode = KeyMappingMacOS::translate_key([event keyCode]);
|
||||
ke.key_label = KeyMappingMacOS::remap_key([event keyCode], [event modifierFlags], true);
|
||||
ke.unicode = 0;
|
||||
ke.location = KeyMappingMacOS::translate_location([event keyCode]);
|
||||
ke.raw = true;
|
||||
|
||||
ds->push_to_key_event_buffer(ke);
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Scroll and pan
|
||||
|
||||
- (void)processScrollEvent:(NSEvent *)event button:(MouseButton)button factor:(double)factor {
|
||||
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
|
||||
if (!ds || !ds->has_window(window_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
|
||||
MouseButtonMask 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);
|
||||
BitField<MouseButtonMask> scroll_mask = ds->mouse_get_button_state();
|
||||
scroll_mask.set_flag(mask);
|
||||
sc->set_button_mask(scroll_mask);
|
||||
|
||||
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);
|
||||
scroll_mask.clear_flag(mask);
|
||||
sc->set_button_mask(scroll_mask);
|
||||
|
||||
Input::get_singleton()->parse_input_event(sc);
|
||||
}
|
||||
|
||||
- (void)processPanEvent:(NSEvent *)event dx:(double)dx dy:(double)dy {
|
||||
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
|
||||
if (!ds || !ds->has_window(window_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayServerMacOS::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 {
|
||||
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
|
||||
if (!ds || !ds->has_window(window_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayServerMacOS::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 (std::abs(delta_x)) {
|
||||
[self processScrollEvent:event button:(0 > delta_x ? MouseButton::WHEEL_RIGHT : MouseButton::WHEEL_LEFT) factor:std::abs(delta_x * 0.3)];
|
||||
}
|
||||
if (std::abs(delta_y)) {
|
||||
[self processScrollEvent:event button:(0 < delta_y ? MouseButton::WHEEL_UP : MouseButton::WHEEL_DOWN) factor:std::abs(delta_y * 0.3)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
133
platform/macos/godot_main_macos.mm
Normal file
133
platform/macos/godot_main_macos.mm
Normal file
@@ -0,0 +1,133 @@
|
||||
/**************************************************************************/
|
||||
/* godot_main_macos.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "os_macos.h"
|
||||
|
||||
#import "godot_application.h"
|
||||
|
||||
#include "main/main.h"
|
||||
|
||||
#if defined(SANITIZERS_ENABLED)
|
||||
#include <sys/resource.h>
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
#if defined(VULKAN_ENABLED)
|
||||
setenv("MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE", "1", 1); // MoltenVK - enable full component swizzling support.
|
||||
setenv("MVK_CONFIG_SWAPCHAIN_MIN_MAG_FILTER_USE_NEAREST", "0", 1); // MoltenVK - use linear surface scaling. TODO: remove when full DPI scaling is implemented.
|
||||
#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
|
||||
|
||||
LocalVector<char *> args;
|
||||
args.resize(argc);
|
||||
uint32_t argsc = 0;
|
||||
|
||||
int wait_for_debugger = 0; // wait 5 second by default
|
||||
bool is_embedded = false;
|
||||
bool is_headless = false;
|
||||
|
||||
for (int i = 0; i < argc; i++) {
|
||||
if (strcmp("-NSDocumentRevisionsDebugMode", argv[i]) == 0) {
|
||||
// remove "-NSDocumentRevisionsDebugMode" and the next argument
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp("--os-debug", argv[i]) == 0) {
|
||||
i++;
|
||||
wait_for_debugger = 5000; // wait 5 seconds by default
|
||||
if (i < argc && strncmp(argv[i], "--", 2) != 0) {
|
||||
wait_for_debugger = atoi(argv[i]);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp("--embedded", argv[i]) == 0) {
|
||||
is_embedded = true;
|
||||
}
|
||||
if (strcmp("--headless", argv[i]) == 0 || strcmp("--doctool", argv[i]) == 0) {
|
||||
is_headless = true;
|
||||
}
|
||||
if (i < argc - 1 && strcmp("--display-driver", argv[i]) == 0 && strcmp("headless", argv[i + 1]) == 0) {
|
||||
is_headless = true;
|
||||
}
|
||||
|
||||
args.ptr()[argsc] = argv[i];
|
||||
argsc++;
|
||||
}
|
||||
|
||||
uint32_t remaining_args = argsc - 1;
|
||||
|
||||
OS_MacOS *os = nullptr;
|
||||
if (is_embedded) {
|
||||
#ifdef DEBUG_ENABLED
|
||||
os = memnew(OS_MacOS_Embedded(args[0], remaining_args, remaining_args > 0 ? &args[1] : nullptr));
|
||||
#else
|
||||
WARN_PRINT("Embedded mode is not supported in release builds.");
|
||||
return EXIT_FAILURE;
|
||||
#endif
|
||||
} else if (is_headless) {
|
||||
os = memnew(OS_MacOS_Headless(args[0], remaining_args, remaining_args > 0 ? &args[1] : nullptr));
|
||||
} else {
|
||||
os = memnew(OS_MacOS_NSApp(args[0], remaining_args, remaining_args > 0 ? &args[1] : nullptr));
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (wait_for_debugger > 0) {
|
||||
os->wait_for_debugger(wait_for_debugger);
|
||||
print_verbose("Continuing execution.");
|
||||
}
|
||||
#else
|
||||
if (wait_for_debugger > 0) {
|
||||
WARN_PRINT("--os-debug is not supported in release builds.");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (is_embedded) {
|
||||
// No dock icon for the embedded process, as it is hosted in the Godot editor.
|
||||
ProcessSerialNumber psn = { 0, kCurrentProcess };
|
||||
(void)TransformProcessType(&psn, kProcessTransformToBackgroundApplication);
|
||||
}
|
||||
|
||||
// We must override main when testing is enabled.
|
||||
TEST_MAIN_OVERRIDE
|
||||
|
||||
os->run();
|
||||
|
||||
int exit_code = os->get_exit_code();
|
||||
|
||||
memdelete(os);
|
||||
|
||||
return exit_code;
|
||||
}
|
41
platform/macos/godot_menu_delegate.h
Normal file
41
platform/macos/godot_menu_delegate.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/**************************************************************************/
|
||||
/* godot_menu_delegate.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface GodotMenuDelegate : NSObject <NSMenuDelegate> {
|
||||
}
|
||||
|
||||
- (void)doNothing:(id)sender;
|
||||
|
||||
@end
|
124
platform/macos/godot_menu_delegate.mm
Normal file
124
platform/macos/godot_menu_delegate.mm
Normal file
@@ -0,0 +1,124 @@
|
||||
/**************************************************************************/
|
||||
/* godot_menu_delegate.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "godot_menu_delegate.h"
|
||||
|
||||
#import "display_server_macos.h"
|
||||
#import "godot_menu_item.h"
|
||||
#import "key_mapping_macos.h"
|
||||
#import "native_menu_macos.h"
|
||||
|
||||
@implementation GodotMenuDelegate
|
||||
|
||||
- (void)doNothing:(id)sender {
|
||||
}
|
||||
|
||||
- (void)menuWillOpen:(NSMenu *)menu {
|
||||
if (NativeMenu::get_singleton()) {
|
||||
NativeMenuMacOS *nmenu = (NativeMenuMacOS *)NativeMenu::get_singleton();
|
||||
nmenu->_menu_open(menu);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)menuNeedsUpdate:(NSMenu *)menu {
|
||||
if (NativeMenu::get_singleton()) {
|
||||
NativeMenuMacOS *nmenu = (NativeMenuMacOS *)NativeMenu::get_singleton();
|
||||
nmenu->_menu_need_update(menu);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)menuDidClose:(NSMenu *)menu {
|
||||
if (NativeMenu::get_singleton()) {
|
||||
NativeMenuMacOS *nmenu = (NativeMenuMacOS *)NativeMenu::get_singleton();
|
||||
nmenu->_menu_close(menu);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)menu:(NSMenu *)menu willHighlightItem:(NSMenuItem *)item {
|
||||
if (item) {
|
||||
GodotMenuItem *value = [item representedObject];
|
||||
if (value && value->hover_callback.is_valid()) {
|
||||
// If custom callback is set, use it.
|
||||
Variant ret;
|
||||
Callable::CallError ce;
|
||||
const Variant *args[1] = { &value->meta };
|
||||
|
||||
value->hover_callback.callp(args, 1, ret, ce);
|
||||
if (ce.error != Callable::CallError::CALL_OK) {
|
||||
ERR_PRINT(vformat("Failed to execute menu hover callback: %s.", Variant::get_callable_error_text(value->hover_callback, args, 1, ce)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)menuHasKeyEquivalent:(NSMenu *)menu forEvent:(NSEvent *)event target:(id *)target action:(SEL *)action {
|
||||
NSString *ev_key = [[event charactersIgnoringModifiers] lowercaseString];
|
||||
NSUInteger ev_modifiers = [event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask;
|
||||
for (int i = 0; i < [menu numberOfItems]; i++) {
|
||||
const NSMenuItem *menu_item = [menu itemAtIndex:i];
|
||||
if ([menu_item isEnabled] && [[menu_item keyEquivalent] compare:ev_key] == NSOrderedSame) {
|
||||
NSUInteger item_modifiers = [menu_item keyEquivalentModifierMask];
|
||||
|
||||
if (ev_modifiers == item_modifiers) {
|
||||
GodotMenuItem *value = [menu_item representedObject];
|
||||
if (value) {
|
||||
if (value->key_callback.is_valid()) {
|
||||
// If custom callback is set, use it.
|
||||
Variant ret;
|
||||
Callable::CallError ce;
|
||||
const Variant *args[1] = { &value->meta };
|
||||
|
||||
value->key_callback.callp(args, 1, ret, ce);
|
||||
if (ce.error != Callable::CallError::CALL_OK) {
|
||||
ERR_PRINT(vformat("Failed to execute menu key callback: %s.", Variant::get_callable_error_text(value->key_callback, args, 1, ce)));
|
||||
}
|
||||
} else {
|
||||
// Otherwise redirect event to the engine.
|
||||
if (DisplayServer::get_singleton()) {
|
||||
if ([[NSApplication sharedApplication] keyWindow].sheet) {
|
||||
[[[[NSApplication sharedApplication] keyWindow] sheetParent] sendEvent:event];
|
||||
} else {
|
||||
[[[NSApplication sharedApplication] keyWindow] sendEvent:event];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Suppress default menu action.
|
||||
*target = self;
|
||||
*action = @selector(doNothing:);
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
61
platform/macos/godot_menu_item.h
Normal file
61
platform/macos/godot_menu_item.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/**************************************************************************/
|
||||
/* godot_menu_item.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "servers/display_server.h"
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#define MENU_TAG_START 0x00004447
|
||||
#define MENU_TAG_END 0xFFFF4447
|
||||
|
||||
enum GlobalMenuCheckType {
|
||||
CHECKABLE_TYPE_NONE,
|
||||
CHECKABLE_TYPE_CHECK_BOX,
|
||||
CHECKABLE_TYPE_RADIO_BUTTON,
|
||||
};
|
||||
|
||||
@interface GodotMenuItem : NSObject {
|
||||
@public
|
||||
Callable callback;
|
||||
Callable key_callback;
|
||||
Callable hover_callback;
|
||||
Variant meta;
|
||||
Key accel;
|
||||
GlobalMenuCheckType checkable_type;
|
||||
bool checked;
|
||||
int max_states;
|
||||
int state;
|
||||
Ref<Image> img;
|
||||
}
|
||||
|
||||
@end
|
49
platform/macos/godot_menu_item.mm
Normal file
49
platform/macos/godot_menu_item.mm
Normal file
@@ -0,0 +1,49 @@
|
||||
/**************************************************************************/
|
||||
/* godot_menu_item.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "godot_menu_item.h"
|
||||
|
||||
@implementation GodotMenuItem
|
||||
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
|
||||
self->callback = Callable();
|
||||
self->key_callback = Callable();
|
||||
self->checkable_type = GlobalMenuCheckType::CHECKABLE_TYPE_NONE;
|
||||
self->checked = false;
|
||||
self->max_states = 0;
|
||||
self->state = 0;
|
||||
self->accel = Key::NONE;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
64
platform/macos/godot_open_save_delegate.h
Normal file
64
platform/macos/godot_open_save_delegate.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/**************************************************************************/
|
||||
/* godot_open_save_delegate.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
|
||||
|
||||
#include "core/templates/hash_map.h"
|
||||
#include "core/variant/typed_array.h"
|
||||
#include "core/variant/variant.h"
|
||||
|
||||
@interface GodotOpenSaveDelegate : NSObject <NSOpenSavePanelDelegate> {
|
||||
NSSavePanel *dialog;
|
||||
NSMutableArray *allowed_types;
|
||||
|
||||
HashMap<int, String> ctr_ids;
|
||||
Dictionary options;
|
||||
int cur_index;
|
||||
int ctr_id;
|
||||
|
||||
String root;
|
||||
}
|
||||
|
||||
- (void)makeAccessoryView:(NSSavePanel *)p_panel filters:(const Vector<String> &)p_filters options:(const TypedArray<Dictionary> &)p_options;
|
||||
- (void)setFileTypes:(NSMutableArray *)p_allowed_types;
|
||||
- (void)popupOptionAction:(id)p_sender;
|
||||
- (void)popupCheckAction:(id)p_sender;
|
||||
- (void)popupFileAction:(id)p_sender;
|
||||
- (int)getIndex;
|
||||
- (Dictionary)getSelection;
|
||||
- (int)setDefaultInt:(const String &)p_name value:(int)p_value;
|
||||
- (int)setDefaultBool:(const String &)p_name value:(bool)p_value;
|
||||
- (void)setRootPath:(const String &)p_root_path;
|
||||
|
||||
@end
|
363
platform/macos/godot_open_save_delegate.mm
Normal file
363
platform/macos/godot_open_save_delegate.mm
Normal file
@@ -0,0 +1,363 @@
|
||||
/**************************************************************************/
|
||||
/* godot_open_save_delegate.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "godot_open_save_delegate.h"
|
||||
|
||||
@implementation GodotOpenSaveDelegate
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if ((self = [super init])) {
|
||||
dialog = nullptr;
|
||||
cur_index = 0;
|
||||
ctr_id = 1;
|
||||
allowed_types = nullptr;
|
||||
root = String();
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)makeAccessoryView:(NSSavePanel *)p_panel filters:(const Vector<String> &)p_filters options:(const TypedArray<Dictionary> &)p_options {
|
||||
dialog = p_panel;
|
||||
|
||||
NSMutableArray *constraints = [NSMutableArray array];
|
||||
|
||||
NSView *base_view = [[NSView alloc] initWithFrame:NSZeroRect];
|
||||
base_view.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
|
||||
NSGridView *view = [NSGridView gridViewWithNumberOfColumns:2 rows:0];
|
||||
view.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
view.columnSpacing = 10;
|
||||
view.rowSpacing = 10;
|
||||
view.rowAlignment = NSGridRowAlignmentLastBaseline;
|
||||
|
||||
int option_count = 0;
|
||||
|
||||
for (int i = 0; i < p_options.size(); i++) {
|
||||
const Dictionary &item = p_options[i];
|
||||
if (!item.has("name") || !item.has("values") || !item.has("default")) {
|
||||
continue;
|
||||
}
|
||||
const String &name = item["name"];
|
||||
const Vector<String> &values = item["values"];
|
||||
int default_idx = item["default"];
|
||||
|
||||
NSTextField *label = [NSTextField labelWithString:[NSString stringWithUTF8String:name.utf8().get_data()]];
|
||||
if (@available(macOS 10.14, *)) {
|
||||
label.textColor = NSColor.secondaryLabelColor;
|
||||
}
|
||||
if (@available(macOS 11.10, *)) {
|
||||
label.font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]];
|
||||
}
|
||||
|
||||
NSView *popup = nullptr;
|
||||
if (values.is_empty()) {
|
||||
NSButton *popup_check = [NSButton checkboxWithTitle:@"" target:self action:@selector(popupCheckAction:)];
|
||||
int tag = [self setDefaultBool:name value:(bool)default_idx];
|
||||
popup_check.state = (default_idx) ? NSControlStateValueOn : NSControlStateValueOff;
|
||||
popup_check.tag = tag;
|
||||
popup = popup_check;
|
||||
} else {
|
||||
NSPopUpButton *popup_list = [[NSPopUpButton alloc] initWithFrame:NSZeroRect pullsDown:NO];
|
||||
for (int i = 0; i < values.size(); i++) {
|
||||
[popup_list addItemWithTitle:[NSString stringWithUTF8String:values[i].utf8().get_data()]];
|
||||
}
|
||||
int tag = [self setDefaultInt:name value:default_idx];
|
||||
[popup_list selectItemAtIndex:default_idx];
|
||||
popup_list.tag = tag;
|
||||
popup_list.target = self;
|
||||
popup_list.action = @selector(popupOptionAction:);
|
||||
popup = popup_list;
|
||||
}
|
||||
|
||||
[view addRowWithViews:[NSArray arrayWithObjects:label, popup, nil]];
|
||||
|
||||
option_count++;
|
||||
}
|
||||
|
||||
NSMutableArray *new_allowed_types = [[NSMutableArray alloc] init];
|
||||
bool has_type_popup = false;
|
||||
{
|
||||
NSTextField *label = [NSTextField labelWithString:[NSString stringWithUTF8String:RTR("Format").utf8().get_data()]];
|
||||
if (@available(macOS 10.14, *)) {
|
||||
label.textColor = NSColor.secondaryLabelColor;
|
||||
}
|
||||
if (@available(macOS 11.10, *)) {
|
||||
label.font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]];
|
||||
}
|
||||
|
||||
if (p_filters.size() > 1) {
|
||||
NSPopUpButton *popup = [[NSPopUpButton alloc] initWithFrame:NSZeroRect pullsDown:NO];
|
||||
for (int i = 0; i < p_filters.size(); i++) {
|
||||
Vector<String> tokens = p_filters[i].split(";");
|
||||
if (tokens.size() >= 1) {
|
||||
String flt = tokens[0].strip_edges();
|
||||
String mime = (tokens.size() >= 3) ? tokens[2].strip_edges() : String();
|
||||
int filter_slice_count = flt.get_slice_count(",");
|
||||
|
||||
NSMutableArray *type_filters = [[NSMutableArray alloc] init];
|
||||
for (int j = 0; j < filter_slice_count; j++) {
|
||||
String str = (flt.get_slicec(',', j).strip_edges());
|
||||
if (!str.is_empty()) {
|
||||
if (@available(macOS 11, *)) {
|
||||
UTType *ut = nullptr;
|
||||
if (str == "*.*") {
|
||||
ut = UTTypeData;
|
||||
} else {
|
||||
ut = [UTType typeWithFilenameExtension:[NSString stringWithUTF8String:str.replace("*.", "").strip_edges().utf8().get_data()]];
|
||||
}
|
||||
if (ut) {
|
||||
[type_filters addObject:ut];
|
||||
}
|
||||
} else {
|
||||
[type_filters addObject:[NSString stringWithUTF8String:str.replace("*.", "").strip_edges().utf8().get_data()]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (@available(macOS 11, *)) {
|
||||
filter_slice_count = mime.get_slice_count(",");
|
||||
for (int j = 0; j < filter_slice_count; j++) {
|
||||
String str = mime.get_slicec(',', j).strip_edges();
|
||||
if (!str.is_empty()) {
|
||||
UTType *ut = [UTType typeWithMIMEType:[NSString stringWithUTF8String:str.strip_edges().utf8().get_data()]];
|
||||
if (ut) {
|
||||
[type_filters addObject:ut];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ([type_filters count] > 0) {
|
||||
NSString *name_str = [NSString stringWithUTF8String:((tokens.size() == 1) ? tokens[0] : tokens[1].strip_edges()).utf8().get_data()];
|
||||
[new_allowed_types addObject:type_filters];
|
||||
[popup addItemWithTitle:name_str];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (popup.numberOfItems > 1) {
|
||||
has_type_popup = true;
|
||||
popup.target = self;
|
||||
popup.action = @selector(popupFileAction:);
|
||||
|
||||
[view addRowWithViews:[NSArray arrayWithObjects:label, popup, nil]];
|
||||
}
|
||||
} else if (p_filters.size() == 1) {
|
||||
Vector<String> tokens = p_filters[0].split(";");
|
||||
if (tokens.size() >= 1) {
|
||||
String flt = tokens[0].strip_edges();
|
||||
String mime = (tokens.size() >= 3) ? tokens[2] : String();
|
||||
int filter_slice_count = flt.get_slice_count(",");
|
||||
|
||||
NSMutableArray *type_filters = [[NSMutableArray alloc] init];
|
||||
for (int j = 0; j < filter_slice_count; j++) {
|
||||
String str = (flt.get_slicec(',', j).strip_edges());
|
||||
if (!str.is_empty()) {
|
||||
if (@available(macOS 11, *)) {
|
||||
UTType *ut = nullptr;
|
||||
if (str == "*.*") {
|
||||
ut = UTTypeData;
|
||||
} else {
|
||||
ut = [UTType typeWithFilenameExtension:[NSString stringWithUTF8String:str.replace("*.", "").strip_edges().utf8().get_data()]];
|
||||
}
|
||||
if (ut) {
|
||||
[type_filters addObject:ut];
|
||||
}
|
||||
} else {
|
||||
[type_filters addObject:[NSString stringWithUTF8String:str.replace("*.", "").strip_edges().utf8().get_data()]];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (@available(macOS 11, *)) {
|
||||
filter_slice_count = mime.get_slice_count(",");
|
||||
for (int j = 0; j < filter_slice_count; j++) {
|
||||
String str = mime.get_slicec(',', j).strip_edges();
|
||||
if (!str.is_empty()) {
|
||||
UTType *ut = [UTType typeWithMIMEType:[NSString stringWithUTF8String:str.strip_edges().utf8().get_data()]];
|
||||
if (ut) {
|
||||
[type_filters addObject:ut];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ([type_filters count] > 0) {
|
||||
[new_allowed_types addObject:type_filters];
|
||||
}
|
||||
}
|
||||
}
|
||||
[self setFileTypes:new_allowed_types];
|
||||
}
|
||||
|
||||
[base_view addSubview:view];
|
||||
[constraints addObject:[view.topAnchor constraintEqualToAnchor:base_view.topAnchor constant:10]];
|
||||
[constraints addObject:[base_view.bottomAnchor constraintEqualToAnchor:view.bottomAnchor constant:10]];
|
||||
[constraints addObject:[base_view.centerXAnchor constraintEqualToAnchor:view.centerXAnchor constant:10]];
|
||||
[NSLayoutConstraint activateConstraints:constraints];
|
||||
|
||||
if (option_count > 0 || has_type_popup) {
|
||||
[p_panel setAccessoryView:base_view];
|
||||
}
|
||||
if ([new_allowed_types count] > 0) {
|
||||
NSMutableArray *type_filters = [new_allowed_types objectAtIndex:0];
|
||||
if (@available(macOS 11, *)) {
|
||||
if (type_filters && [type_filters count] == 1 && [type_filters objectAtIndex:0] == UTTypeData) {
|
||||
[p_panel setAllowedContentTypes:@[ UTTypeData ]];
|
||||
[p_panel setAllowsOtherFileTypes:true];
|
||||
} else {
|
||||
[p_panel setAllowsOtherFileTypes:false];
|
||||
[p_panel setAllowedContentTypes:type_filters];
|
||||
}
|
||||
} else {
|
||||
if (type_filters && [type_filters count] == 1 && [[type_filters objectAtIndex:0] isEqualToString:@"*"]) {
|
||||
[p_panel setAllowedFileTypes:nil];
|
||||
[p_panel setAllowsOtherFileTypes:true];
|
||||
} else {
|
||||
[p_panel setAllowsOtherFileTypes:false];
|
||||
[p_panel setAllowedFileTypes:type_filters];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (@available(macOS 11, *)) {
|
||||
[p_panel setAllowedContentTypes:@[ UTTypeData ]];
|
||||
} else {
|
||||
[p_panel setAllowedFileTypes:nil];
|
||||
}
|
||||
[p_panel setAllowsOtherFileTypes:true];
|
||||
}
|
||||
}
|
||||
|
||||
- (int)getIndex {
|
||||
return cur_index;
|
||||
}
|
||||
|
||||
- (Dictionary)getSelection {
|
||||
return options;
|
||||
}
|
||||
|
||||
- (int)setDefaultInt:(const String &)p_name value:(int)p_value {
|
||||
int cid = ctr_id++;
|
||||
options[p_name] = p_value;
|
||||
ctr_ids[cid] = p_name;
|
||||
|
||||
return cid;
|
||||
}
|
||||
|
||||
- (int)setDefaultBool:(const String &)p_name value:(bool)p_value {
|
||||
int cid = ctr_id++;
|
||||
options[p_name] = p_value;
|
||||
ctr_ids[cid] = p_name;
|
||||
|
||||
return cid;
|
||||
}
|
||||
|
||||
- (void)setFileTypes:(NSMutableArray *)p_allowed_types {
|
||||
allowed_types = p_allowed_types;
|
||||
}
|
||||
|
||||
- (instancetype)initWithDialog:(NSSavePanel *)p_dialog {
|
||||
if ((self = [super init])) {
|
||||
dialog = p_dialog;
|
||||
cur_index = 0;
|
||||
ctr_id = 1;
|
||||
allowed_types = nullptr;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)popupCheckAction:(id)p_sender {
|
||||
NSButton *btn = p_sender;
|
||||
if (btn && ctr_ids.has(btn.tag)) {
|
||||
options[ctr_ids[btn.tag]] = ([btn state] == NSControlStateValueOn);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)popupOptionAction:(id)p_sender {
|
||||
NSPopUpButton *btn = p_sender;
|
||||
if (btn && ctr_ids.has(btn.tag)) {
|
||||
options[ctr_ids[btn.tag]] = (int)[btn indexOfSelectedItem];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)popupFileAction:(id)p_sender {
|
||||
NSPopUpButton *btn = p_sender;
|
||||
if (btn) {
|
||||
NSUInteger index = [btn indexOfSelectedItem];
|
||||
if (allowed_types && index < [allowed_types count]) {
|
||||
NSMutableArray *type_filters = [allowed_types objectAtIndex:index];
|
||||
if (@available(macOS 11, *)) {
|
||||
if (type_filters && [type_filters count] == 1 && [type_filters objectAtIndex:0] == UTTypeData) {
|
||||
[dialog setAllowedContentTypes:@[ UTTypeData ]];
|
||||
[dialog setAllowsOtherFileTypes:true];
|
||||
} else {
|
||||
[dialog setAllowsOtherFileTypes:false];
|
||||
[dialog setAllowedContentTypes:type_filters];
|
||||
}
|
||||
} else {
|
||||
if (type_filters && [type_filters count] == 1 && [[type_filters objectAtIndex:0] isEqualToString:@"*"]) {
|
||||
[dialog setAllowedFileTypes:nil];
|
||||
[dialog setAllowsOtherFileTypes:true];
|
||||
} else {
|
||||
[dialog setAllowsOtherFileTypes:false];
|
||||
[dialog setAllowedFileTypes:type_filters];
|
||||
}
|
||||
}
|
||||
cur_index = index;
|
||||
} else {
|
||||
if (@available(macOS 11, *)) {
|
||||
[dialog setAllowedContentTypes:@[ UTTypeData ]];
|
||||
} else {
|
||||
[dialog setAllowedFileTypes:nil];
|
||||
}
|
||||
[dialog setAllowsOtherFileTypes:true];
|
||||
cur_index = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setRootPath:(const String &)p_root_path {
|
||||
root = p_root_path;
|
||||
}
|
||||
|
||||
- (BOOL)panel:(id)sender validateURL:(NSURL *)url error:(NSError *_Nullable *)outError {
|
||||
if (root.is_empty()) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
NSString *ns_path = url.URLByStandardizingPath.URLByResolvingSymlinksInPath.path;
|
||||
String path = String::utf8([ns_path UTF8String]).simplify_path();
|
||||
if (!path.begins_with(root.simplify_path())) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
47
platform/macos/godot_status_item.h
Normal file
47
platform/macos/godot_status_item.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/**************************************************************************/
|
||||
/* godot_status_item.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/input/input_enums.h"
|
||||
#include "core/variant/callable.h"
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface GodotStatusItemDelegate : NSObject {
|
||||
Callable cb;
|
||||
}
|
||||
|
||||
- (IBAction)click:(id)sender;
|
||||
|
||||
- (void)setCallback:(const Callable &)callback;
|
||||
|
||||
@end
|
83
platform/macos/godot_status_item.mm
Normal file
83
platform/macos/godot_status_item.mm
Normal file
@@ -0,0 +1,83 @@
|
||||
/**************************************************************************/
|
||||
/* godot_status_item.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "godot_status_item.h"
|
||||
|
||||
#import "display_server_macos.h"
|
||||
|
||||
@implementation GodotStatusItemDelegate
|
||||
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (IBAction)click:(id)sender {
|
||||
NSEvent *current_event = [NSApp currentEvent];
|
||||
MouseButton index = MouseButton::LEFT;
|
||||
if (current_event) {
|
||||
if (current_event.type == NSEventTypeLeftMouseDown) {
|
||||
index = MouseButton::LEFT;
|
||||
} else if (current_event.type == NSEventTypeRightMouseDown) {
|
||||
index = MouseButton::RIGHT;
|
||||
} else if (current_event.type == NSEventTypeOtherMouseDown) {
|
||||
if ((int)[current_event buttonNumber] == 2) {
|
||||
index = MouseButton::MIDDLE;
|
||||
} else if ((int)[current_event buttonNumber] == 3) {
|
||||
index = MouseButton::MB_XBUTTON1;
|
||||
} else if ((int)[current_event buttonNumber] == 4) {
|
||||
index = MouseButton::MB_XBUTTON2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
|
||||
if (!ds) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cb.is_valid()) {
|
||||
Variant v_button = index;
|
||||
Variant v_pos = ds->mouse_get_position();
|
||||
const Variant *v_args[2] = { &v_button, &v_pos };
|
||||
Variant ret;
|
||||
Callable::CallError ce;
|
||||
cb.callp((const Variant **)&v_args, 2, ret, ce);
|
||||
if (ce.error != Callable::CallError::CALL_OK) {
|
||||
ERR_PRINT(vformat("Failed to execute status indicator callback: %s.", Variant::get_callable_error_text(cb, v_args, 2, ce)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setCallback:(const Callable &)callback {
|
||||
cb = callback;
|
||||
}
|
||||
|
||||
@end
|
46
platform/macos/godot_window.h
Normal file
46
platform/macos/godot_window.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/**************************************************************************/
|
||||
/* godot_window.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "servers/display_server.h"
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface GodotWindow : NSWindow {
|
||||
DisplayServer::WindowID window_id;
|
||||
NSTimeInterval anim_duration;
|
||||
}
|
||||
|
||||
- (void)setWindowID:(DisplayServer::WindowID)wid;
|
||||
- (void)setAnimDuration:(NSTimeInterval)duration;
|
||||
|
||||
@end
|
82
platform/macos/godot_window.mm
Normal file
82
platform/macos/godot_window.mm
Normal file
@@ -0,0 +1,82 @@
|
||||
/**************************************************************************/
|
||||
/* godot_window.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "godot_window.h"
|
||||
|
||||
#import "display_server_macos.h"
|
||||
|
||||
@implementation GodotWindow
|
||||
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
window_id = DisplayServer::INVALID_WINDOW_ID;
|
||||
anim_duration = -1.0f;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setAnimDuration:(NSTimeInterval)duration {
|
||||
anim_duration = duration;
|
||||
}
|
||||
|
||||
- (NSTimeInterval)animationResizeTime:(NSRect)newFrame {
|
||||
if (anim_duration > 0) {
|
||||
return anim_duration;
|
||||
} else {
|
||||
return [super animationResizeTime:newFrame];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setWindowID:(DisplayServerMacOS::WindowID)wid {
|
||||
window_id = wid;
|
||||
}
|
||||
|
||||
- (BOOL)canBecomeKeyWindow {
|
||||
// Required for NSWindowStyleMaskBorderless windows.
|
||||
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
|
||||
if (!ds || !ds->has_window(window_id)) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
|
||||
return !wd.no_focus;
|
||||
}
|
||||
|
||||
- (BOOL)canBecomeMainWindow {
|
||||
// Required for NSWindowStyleMaskBorderless windows.
|
||||
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
|
||||
if (!ds || !ds->has_window(window_id)) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
|
||||
return !wd.no_focus && !wd.is_popup;
|
||||
}
|
||||
|
||||
@end
|
46
platform/macos/godot_window_delegate.h
Normal file
46
platform/macos/godot_window_delegate.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/**************************************************************************/
|
||||
/* godot_window_delegate.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "servers/display_server.h"
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
class DisplayServerMacOS;
|
||||
|
||||
@interface GodotWindowDelegate : NSObject <NSWindowDelegate>
|
||||
|
||||
- (void)setWindowID:(DisplayServer::WindowID)wid;
|
||||
|
||||
- (instancetype)initWithDisplayServer:(DisplayServerMacOS *)p_ds;
|
||||
|
||||
@end
|
397
platform/macos/godot_window_delegate.mm
Normal file
397
platform/macos/godot_window_delegate.mm
Normal file
@@ -0,0 +1,397 @@
|
||||
/**************************************************************************/
|
||||
/* godot_window_delegate.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "godot_window_delegate.h"
|
||||
|
||||
#import "display_server_macos.h"
|
||||
#import "godot_button_view.h"
|
||||
#import "godot_content_view.h"
|
||||
#import "godot_window.h"
|
||||
|
||||
@implementation GodotWindowDelegate {
|
||||
DisplayServer::WindowID window_id;
|
||||
DisplayServerMacOS *ds;
|
||||
}
|
||||
|
||||
- (instancetype)initWithDisplayServer:(DisplayServerMacOS *)p_ds {
|
||||
if (self = [super init]) {
|
||||
ds = p_ds;
|
||||
window_id = DisplayServerMacOS::INVALID_WINDOW_ID;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setWindowID:(DisplayServer::WindowID)wid {
|
||||
window_id = wid;
|
||||
}
|
||||
|
||||
- (BOOL)windowShouldClose:(id)sender {
|
||||
if (!ds->has_window(window_id)) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
ds->send_window_event(ds->get_window(window_id), DisplayServerMacOS::WINDOW_EVENT_CLOSE_REQUEST);
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)windowWillClose:(NSNotification *)notification {
|
||||
if (!ds->has_window(window_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ds->popup_close(window_id);
|
||||
|
||||
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
|
||||
while (wd.transient_children.size()) {
|
||||
ds->window_set_transient(*wd.transient_children.begin(), DisplayServerMacOS::INVALID_WINDOW_ID);
|
||||
}
|
||||
|
||||
if (wd.transient_parent != DisplayServerMacOS::INVALID_WINDOW_ID) {
|
||||
ds->window_set_transient(window_id, DisplayServerMacOS::INVALID_WINDOW_ID);
|
||||
}
|
||||
|
||||
ds->mouse_exit_window(window_id);
|
||||
ds->window_destroy(window_id);
|
||||
}
|
||||
|
||||
- (void)windowWillEnterFullScreen:(NSNotification *)notification {
|
||||
if (!ds->has_window(window_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
|
||||
wd.fs_transition = true;
|
||||
|
||||
// Temporary disable borderless and transparent state.
|
||||
if ([wd.window_object styleMask] == NSWindowStyleMaskBorderless) {
|
||||
[wd.window_object setStyleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable];
|
||||
}
|
||||
if (wd.layered_window) {
|
||||
ds->set_window_per_pixel_transparency_enabled(false, window_id);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)windowDidFailToEnterFullScreen:(NSWindow *)window {
|
||||
if (!ds->has_window(window_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
|
||||
wd.fs_transition = false;
|
||||
}
|
||||
|
||||
- (void)windowDidEnterFullScreen:(NSNotification *)notification {
|
||||
if (!ds->has_window(window_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
|
||||
wd.fullscreen = true;
|
||||
wd.fs_transition = false;
|
||||
|
||||
// Reset window size limits.
|
||||
[wd.window_object setContentMinSize:NSMakeSize(0, 0)];
|
||||
[wd.window_object setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
|
||||
|
||||
// Reset custom window buttons.
|
||||
if ([wd.window_object styleMask] & NSWindowStyleMaskFullSizeContentView) {
|
||||
ds->window_set_custom_window_buttons(wd, false);
|
||||
}
|
||||
|
||||
ds->send_window_event(wd, DisplayServerMacOS::WINDOW_EVENT_TITLEBAR_CHANGE);
|
||||
|
||||
// Force window resize event and redraw.
|
||||
[self windowDidResize:notification];
|
||||
}
|
||||
|
||||
- (void)windowWillExitFullScreen:(NSNotification *)notification {
|
||||
if (!ds->has_window(window_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
|
||||
wd.fs_transition = true;
|
||||
|
||||
// Restore custom window buttons.
|
||||
if ([wd.window_object styleMask] & NSWindowStyleMaskFullSizeContentView) {
|
||||
ds->window_set_custom_window_buttons(wd, true);
|
||||
}
|
||||
|
||||
ds->send_window_event(wd, DisplayServerMacOS::WINDOW_EVENT_TITLEBAR_CHANGE);
|
||||
}
|
||||
|
||||
- (void)windowDidFailToExitFullScreen:(NSWindow *)window {
|
||||
if (!ds->has_window(window_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
|
||||
wd.fs_transition = false;
|
||||
|
||||
if ([wd.window_object styleMask] & NSWindowStyleMaskFullSizeContentView) {
|
||||
ds->window_set_custom_window_buttons(wd, false);
|
||||
}
|
||||
|
||||
ds->send_window_event(wd, DisplayServerMacOS::WINDOW_EVENT_TITLEBAR_CHANGE);
|
||||
}
|
||||
|
||||
- (void)windowDidExitFullScreen:(NSNotification *)notification {
|
||||
if (!ds->has_window(window_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
|
||||
if (wd.exclusive_fullscreen) {
|
||||
ds->update_presentation_mode();
|
||||
}
|
||||
|
||||
wd.fullscreen = false;
|
||||
wd.exclusive_fullscreen = false;
|
||||
wd.fs_transition = 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 borderless, transparent and resizability state.
|
||||
if (wd.borderless) {
|
||||
[wd.window_object setStyleMask:NSWindowStyleMaskBorderless];
|
||||
[wd.window_object setHasShadow:NO];
|
||||
} else {
|
||||
[wd.window_object setStyleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | (wd.extend_to_title ? NSWindowStyleMaskFullSizeContentView : 0) | (wd.resize_disabled ? 0 : NSWindowStyleMaskResizable)];
|
||||
[wd.window_object setHasShadow:YES];
|
||||
}
|
||||
if (wd.layered_window) {
|
||||
ds->set_window_per_pixel_transparency_enabled(true, window_id);
|
||||
}
|
||||
|
||||
// Restore on-top state.
|
||||
if (ds->is_always_on_top_recursive(window_id)) {
|
||||
[wd.window_object setLevel:NSFloatingWindowLevel];
|
||||
}
|
||||
|
||||
// Force window resize event and redraw.
|
||||
[self windowDidResize:notification];
|
||||
}
|
||||
|
||||
- (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame {
|
||||
if (ds->has_window(window_id)) {
|
||||
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
|
||||
if (!wd.borderless && !(NSEqualRects([wd.window_object frame], [[wd.window_object screen] visibleFrame]))) {
|
||||
wd.pre_zoom_rect = [wd.window_object frame];
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)windowDidChangeBackingProperties:(NSNotification *)notification {
|
||||
if (!ds->has_window(window_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayServerMacOS::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, DisplayServerMacOS::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 {
|
||||
if (ds->has_window(window_id)) {
|
||||
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
|
||||
wd.last_frame_rect = [wd.window_object frame];
|
||||
ds->set_is_resizing(true);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)windowDidEndLiveResize:(NSNotification *)notification {
|
||||
ds->set_is_resizing(false);
|
||||
}
|
||||
|
||||
- (void)windowDidResize:(NSNotification *)notification {
|
||||
if (!ds->has_window(window_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayServerMacOS::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_valid()) {
|
||||
wd.rect_changed_callback.call(Rect2i(ds->window_get_position(window_id), ds->window_get_size(window_id)));
|
||||
}
|
||||
}
|
||||
|
||||
- (void)windowDidChangeScreen:(NSNotification *)notification {
|
||||
if (!ds->has_window(window_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ds->reparent_check(window_id);
|
||||
}
|
||||
|
||||
- (void)windowDidMove:(NSNotification *)notification {
|
||||
if (!ds->has_window(window_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
|
||||
ds->release_pressed_events();
|
||||
|
||||
if (wd.rect_changed_callback.is_valid()) {
|
||||
wd.rect_changed_callback.call(Rect2i(ds->window_get_position(window_id), ds->window_get_size(window_id)));
|
||||
}
|
||||
}
|
||||
|
||||
- (void)windowDidBecomeKey:(NSNotification *)notification {
|
||||
if (!ds->has_window(window_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
|
||||
|
||||
if (wd.window_button_view) {
|
||||
[wd.window_button_view displayButtons];
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
[self windowDidResize:notification]; // Emit resize event, to ensure content is resized if the window was resized while it was hidden.
|
||||
|
||||
wd.focused = true;
|
||||
ds->set_last_focused_window(window_id);
|
||||
#ifdef ACCESSKIT_ENABLED
|
||||
ds->accessibility_set_window_focused(window_id, true);
|
||||
#endif
|
||||
ds->send_window_event(wd, DisplayServerMacOS::WINDOW_EVENT_FOCUS_IN);
|
||||
}
|
||||
|
||||
- (void)windowDidResignKey:(NSNotification *)notification {
|
||||
if (!ds->has_window(window_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
|
||||
|
||||
if (wd.window_button_view) {
|
||||
[wd.window_button_view displayButtons];
|
||||
}
|
||||
|
||||
wd.focused = false;
|
||||
ds->release_pressed_events();
|
||||
#ifdef ACCESSKIT_ENABLED
|
||||
ds->accessibility_set_window_focused(window_id, false);
|
||||
#endif
|
||||
ds->send_window_event(wd, DisplayServerMacOS::WINDOW_EVENT_FOCUS_OUT);
|
||||
}
|
||||
|
||||
- (void)windowDidMiniaturize:(NSNotification *)notification {
|
||||
if (!ds->has_window(window_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
|
||||
|
||||
wd.focused = false;
|
||||
ds->release_pressed_events();
|
||||
#ifdef ACCESSKIT_ENABLED
|
||||
ds->accessibility_set_window_focused(window_id, false);
|
||||
#endif
|
||||
ds->send_window_event(wd, DisplayServerMacOS::WINDOW_EVENT_FOCUS_OUT);
|
||||
}
|
||||
|
||||
- (void)windowDidDeminiaturize:(NSNotification *)notification {
|
||||
if (!ds->has_window(window_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
|
||||
wd.is_visible = ([wd.window_object occlusionState] & NSWindowOcclusionStateVisible) && [wd.window_object isVisible];
|
||||
if ([wd.window_object isKeyWindow]) {
|
||||
wd.focused = true;
|
||||
ds->set_last_focused_window(window_id);
|
||||
#ifdef ACCESSKIT_ENABLED
|
||||
ds->accessibility_set_window_focused(window_id, true);
|
||||
#endif
|
||||
ds->send_window_event(wd, DisplayServerMacOS::WINDOW_EVENT_FOCUS_IN);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)windowDidChangeOcclusionState:(NSNotification *)notification {
|
||||
if (!ds->has_window(window_id)) {
|
||||
return;
|
||||
}
|
||||
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
|
||||
wd.is_visible = ([wd.window_object occlusionState] & NSWindowOcclusionStateVisible) && [wd.window_object isVisible];
|
||||
}
|
||||
|
||||
@end
|
52
platform/macos/key_mapping_macos.h
Normal file
52
platform/macos/key_mapping_macos.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/**************************************************************************/
|
||||
/* key_mapping_macos.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/os/keyboard.h"
|
||||
|
||||
class KeyMappingMacOS {
|
||||
KeyMappingMacOS() {}
|
||||
|
||||
static bool is_numpad_key(unsigned int p_key);
|
||||
|
||||
public:
|
||||
static void initialize();
|
||||
|
||||
// Mappings input.
|
||||
static Key translate_key(unsigned int p_key);
|
||||
static unsigned int unmap_key(Key p_key);
|
||||
static Key remap_key(unsigned int p_key, unsigned int p_state, bool p_unicode);
|
||||
static KeyLocation translate_location(unsigned int p_key);
|
||||
|
||||
// Mapping for menu shortcuts.
|
||||
static String keycode_get_native_string(Key p_keycode);
|
||||
static unsigned int keycode_get_native_mask(Key p_keycode);
|
||||
};
|
449
platform/macos/key_mapping_macos.mm
Normal file
449
platform/macos/key_mapping_macos.mm
Normal file
@@ -0,0 +1,449 @@
|
||||
/**************************************************************************/
|
||||
/* key_mapping_macos.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "key_mapping_macos.h"
|
||||
|
||||
#include "core/templates/hash_map.h"
|
||||
#include "core/templates/hash_set.h"
|
||||
|
||||
#import <Carbon/Carbon.h>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
struct HashMapHasherKeys {
|
||||
static _FORCE_INLINE_ uint32_t hash(const Key p_key) { return hash_fmix32(static_cast<uint32_t>(p_key)); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const char32_t p_uchar) { return hash_fmix32(p_uchar); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const unsigned p_key) { return hash_fmix32(p_key); }
|
||||
};
|
||||
|
||||
HashSet<unsigned int> numpad_keys;
|
||||
HashMap<unsigned int, Key, HashMapHasherKeys> keysym_map;
|
||||
HashMap<Key, unsigned int, HashMapHasherKeys> keysym_map_inv;
|
||||
HashMap<Key, char32_t, HashMapHasherKeys> keycode_map;
|
||||
HashMap<unsigned int, KeyLocation, HashMapHasherKeys> location_map;
|
||||
|
||||
void KeyMappingMacOS::initialize() {
|
||||
numpad_keys.insert(0x41); //kVK_ANSI_KeypadDecimal
|
||||
numpad_keys.insert(0x43); //kVK_ANSI_KeypadMultiply
|
||||
numpad_keys.insert(0x45); //kVK_ANSI_KeypadPlus
|
||||
numpad_keys.insert(0x47); //kVK_ANSI_KeypadClear
|
||||
numpad_keys.insert(0x4b); //kVK_ANSI_KeypadDivide
|
||||
numpad_keys.insert(0x4c); //kVK_ANSI_KeypadEnter
|
||||
numpad_keys.insert(0x4e); //kVK_ANSI_KeypadMinus
|
||||
numpad_keys.insert(0x51); //kVK_ANSI_KeypadEquals
|
||||
numpad_keys.insert(0x52); //kVK_ANSI_Keypad0
|
||||
numpad_keys.insert(0x53); //kVK_ANSI_Keypad1
|
||||
numpad_keys.insert(0x54); //kVK_ANSI_Keypad2
|
||||
numpad_keys.insert(0x55); //kVK_ANSI_Keypad3
|
||||
numpad_keys.insert(0x56); //kVK_ANSI_Keypad4
|
||||
numpad_keys.insert(0x57); //kVK_ANSI_Keypad5
|
||||
numpad_keys.insert(0x58); //kVK_ANSI_Keypad6
|
||||
numpad_keys.insert(0x59); //kVK_ANSI_Keypad7
|
||||
numpad_keys.insert(0x5b); //kVK_ANSI_Keypad8
|
||||
numpad_keys.insert(0x5c); //kVK_ANSI_Keypad9
|
||||
numpad_keys.insert(0x5f); //kVK_JIS_KeypadComma
|
||||
|
||||
keysym_map[0x00] = Key::A;
|
||||
keysym_map[0x01] = Key::S;
|
||||
keysym_map[0x02] = Key::D;
|
||||
keysym_map[0x03] = Key::F;
|
||||
keysym_map[0x04] = Key::H;
|
||||
keysym_map[0x05] = Key::G;
|
||||
keysym_map[0x06] = Key::Z;
|
||||
keysym_map[0x07] = Key::X;
|
||||
keysym_map[0x08] = Key::C;
|
||||
keysym_map[0x09] = Key::V;
|
||||
keysym_map[0x0a] = Key::SECTION;
|
||||
keysym_map[0x0b] = Key::B;
|
||||
keysym_map[0x0c] = Key::Q;
|
||||
keysym_map[0x0d] = Key::W;
|
||||
keysym_map[0x0e] = Key::E;
|
||||
keysym_map[0x0f] = Key::R;
|
||||
keysym_map[0x10] = Key::Y;
|
||||
keysym_map[0x11] = Key::T;
|
||||
keysym_map[0x12] = Key::KEY_1;
|
||||
keysym_map[0x13] = Key::KEY_2;
|
||||
keysym_map[0x14] = Key::KEY_3;
|
||||
keysym_map[0x15] = Key::KEY_4;
|
||||
keysym_map[0x16] = Key::KEY_6;
|
||||
keysym_map[0x17] = Key::KEY_5;
|
||||
keysym_map[0x18] = Key::EQUAL;
|
||||
keysym_map[0x19] = Key::KEY_9;
|
||||
keysym_map[0x1a] = Key::KEY_7;
|
||||
keysym_map[0x1b] = Key::MINUS;
|
||||
keysym_map[0x1c] = Key::KEY_8;
|
||||
keysym_map[0x1d] = Key::KEY_0;
|
||||
keysym_map[0x1e] = Key::BRACKETRIGHT;
|
||||
keysym_map[0x1f] = Key::O;
|
||||
keysym_map[0x20] = Key::U;
|
||||
keysym_map[0x21] = Key::BRACKETLEFT;
|
||||
keysym_map[0x22] = Key::I;
|
||||
keysym_map[0x23] = Key::P;
|
||||
keysym_map[0x24] = Key::ENTER;
|
||||
keysym_map[0x25] = Key::L;
|
||||
keysym_map[0x26] = Key::J;
|
||||
keysym_map[0x27] = Key::APOSTROPHE;
|
||||
keysym_map[0x28] = Key::K;
|
||||
keysym_map[0x29] = Key::SEMICOLON;
|
||||
keysym_map[0x2a] = Key::BACKSLASH;
|
||||
keysym_map[0x2b] = Key::COMMA;
|
||||
keysym_map[0x2c] = Key::SLASH;
|
||||
keysym_map[0x2d] = Key::N;
|
||||
keysym_map[0x2e] = Key::M;
|
||||
keysym_map[0x2f] = Key::PERIOD;
|
||||
keysym_map[0x30] = Key::TAB;
|
||||
keysym_map[0x31] = Key::SPACE;
|
||||
keysym_map[0x32] = Key::QUOTELEFT;
|
||||
keysym_map[0x33] = Key::BACKSPACE;
|
||||
keysym_map[0x35] = Key::ESCAPE;
|
||||
keysym_map[0x36] = Key::META;
|
||||
keysym_map[0x37] = Key::META;
|
||||
keysym_map[0x38] = Key::SHIFT;
|
||||
keysym_map[0x39] = Key::CAPSLOCK;
|
||||
keysym_map[0x3a] = Key::ALT;
|
||||
keysym_map[0x3b] = Key::CTRL;
|
||||
keysym_map[0x3c] = Key::SHIFT;
|
||||
keysym_map[0x3d] = Key::ALT;
|
||||
keysym_map[0x3e] = Key::CTRL;
|
||||
keysym_map[0x40] = Key::F17;
|
||||
keysym_map[0x41] = Key::KP_PERIOD;
|
||||
keysym_map[0x43] = Key::KP_MULTIPLY;
|
||||
keysym_map[0x45] = Key::KP_ADD;
|
||||
keysym_map[0x47] = Key::NUMLOCK;
|
||||
keysym_map[0x48] = Key::VOLUMEUP;
|
||||
keysym_map[0x49] = Key::VOLUMEDOWN;
|
||||
keysym_map[0x4a] = Key::VOLUMEMUTE;
|
||||
keysym_map[0x4b] = Key::KP_DIVIDE;
|
||||
keysym_map[0x4c] = Key::KP_ENTER;
|
||||
keysym_map[0x4e] = Key::KP_SUBTRACT;
|
||||
keysym_map[0x4f] = Key::F18;
|
||||
keysym_map[0x50] = Key::F19;
|
||||
keysym_map[0x51] = Key::EQUAL;
|
||||
keysym_map[0x52] = Key::KP_0;
|
||||
keysym_map[0x53] = Key::KP_1;
|
||||
keysym_map[0x54] = Key::KP_2;
|
||||
keysym_map[0x55] = Key::KP_3;
|
||||
keysym_map[0x56] = Key::KP_4;
|
||||
keysym_map[0x57] = Key::KP_5;
|
||||
keysym_map[0x58] = Key::KP_6;
|
||||
keysym_map[0x59] = Key::KP_7;
|
||||
keysym_map[0x5a] = Key::F20;
|
||||
keysym_map[0x5b] = Key::KP_8;
|
||||
keysym_map[0x5c] = Key::KP_9;
|
||||
keysym_map[0x5d] = Key::YEN;
|
||||
keysym_map[0x5e] = Key::UNDERSCORE;
|
||||
keysym_map[0x5f] = Key::COMMA;
|
||||
keysym_map[0x60] = Key::F5;
|
||||
keysym_map[0x61] = Key::F6;
|
||||
keysym_map[0x62] = Key::F7;
|
||||
keysym_map[0x63] = Key::F3;
|
||||
keysym_map[0x64] = Key::F8;
|
||||
keysym_map[0x65] = Key::F9;
|
||||
keysym_map[0x66] = Key::JIS_EISU;
|
||||
keysym_map[0x67] = Key::F11;
|
||||
keysym_map[0x68] = Key::JIS_KANA;
|
||||
keysym_map[0x69] = Key::F13;
|
||||
keysym_map[0x6a] = Key::F16;
|
||||
keysym_map[0x6b] = Key::F14;
|
||||
keysym_map[0x6d] = Key::F10;
|
||||
keysym_map[0x6e] = Key::MENU;
|
||||
keysym_map[0x6f] = Key::F12;
|
||||
keysym_map[0x71] = Key::F15;
|
||||
keysym_map[0x72] = Key::INSERT;
|
||||
keysym_map[0x73] = Key::HOME;
|
||||
keysym_map[0x74] = Key::PAGEUP;
|
||||
keysym_map[0x75] = Key::KEY_DELETE;
|
||||
keysym_map[0x76] = Key::F4;
|
||||
keysym_map[0x77] = Key::END;
|
||||
keysym_map[0x78] = Key::F2;
|
||||
keysym_map[0x79] = Key::PAGEDOWN;
|
||||
keysym_map[0x7a] = Key::F1;
|
||||
keysym_map[0x7b] = Key::LEFT;
|
||||
keysym_map[0x7c] = Key::RIGHT;
|
||||
keysym_map[0x7d] = Key::DOWN;
|
||||
keysym_map[0x7e] = Key::UP;
|
||||
|
||||
for (const KeyValue<unsigned int, Key> &E : keysym_map) {
|
||||
keysym_map_inv[E.value] = E.key;
|
||||
}
|
||||
|
||||
keycode_map[Key::ESCAPE] = 0x001B;
|
||||
keycode_map[Key::TAB] = 0x0009;
|
||||
keycode_map[Key::BACKTAB] = 0x007F;
|
||||
keycode_map[Key::BACKSPACE] = 0x0008;
|
||||
keycode_map[Key::ENTER] = 0x000D;
|
||||
keycode_map[Key::INSERT] = NSInsertFunctionKey;
|
||||
keycode_map[Key::KEY_DELETE] = 0x007F;
|
||||
keycode_map[Key::PAUSE] = NSPauseFunctionKey;
|
||||
keycode_map[Key::PRINT] = NSPrintScreenFunctionKey;
|
||||
keycode_map[Key::SYSREQ] = NSSysReqFunctionKey;
|
||||
keycode_map[Key::CLEAR] = NSClearLineFunctionKey;
|
||||
keycode_map[Key::HOME] = 0x2196;
|
||||
keycode_map[Key::END] = 0x2198;
|
||||
keycode_map[Key::LEFT] = 0x001C;
|
||||
keycode_map[Key::UP] = 0x001E;
|
||||
keycode_map[Key::RIGHT] = 0x001D;
|
||||
keycode_map[Key::DOWN] = 0x001F;
|
||||
keycode_map[Key::PAGEUP] = 0x21DE;
|
||||
keycode_map[Key::PAGEDOWN] = 0x21DF;
|
||||
keycode_map[Key::NUMLOCK] = NSClearLineFunctionKey;
|
||||
keycode_map[Key::SCROLLLOCK] = NSScrollLockFunctionKey;
|
||||
keycode_map[Key::F1] = NSF1FunctionKey;
|
||||
keycode_map[Key::F2] = NSF2FunctionKey;
|
||||
keycode_map[Key::F3] = NSF3FunctionKey;
|
||||
keycode_map[Key::F4] = NSF4FunctionKey;
|
||||
keycode_map[Key::F5] = NSF5FunctionKey;
|
||||
keycode_map[Key::F6] = NSF6FunctionKey;
|
||||
keycode_map[Key::F7] = NSF7FunctionKey;
|
||||
keycode_map[Key::F8] = NSF8FunctionKey;
|
||||
keycode_map[Key::F9] = NSF9FunctionKey;
|
||||
keycode_map[Key::F10] = NSF10FunctionKey;
|
||||
keycode_map[Key::F11] = NSF11FunctionKey;
|
||||
keycode_map[Key::F12] = NSF12FunctionKey;
|
||||
keycode_map[Key::F13] = NSF13FunctionKey;
|
||||
keycode_map[Key::F14] = NSF14FunctionKey;
|
||||
keycode_map[Key::F15] = NSF15FunctionKey;
|
||||
keycode_map[Key::F16] = NSF16FunctionKey;
|
||||
keycode_map[Key::F17] = NSF17FunctionKey;
|
||||
keycode_map[Key::F18] = NSF18FunctionKey;
|
||||
keycode_map[Key::F19] = NSF19FunctionKey;
|
||||
keycode_map[Key::F20] = NSF20FunctionKey;
|
||||
keycode_map[Key::F21] = NSF21FunctionKey;
|
||||
keycode_map[Key::F22] = NSF22FunctionKey;
|
||||
keycode_map[Key::F23] = NSF23FunctionKey;
|
||||
keycode_map[Key::F24] = NSF24FunctionKey;
|
||||
keycode_map[Key::F25] = NSF25FunctionKey;
|
||||
keycode_map[Key::F26] = NSF26FunctionKey;
|
||||
keycode_map[Key::F27] = NSF27FunctionKey;
|
||||
keycode_map[Key::F28] = NSF28FunctionKey;
|
||||
keycode_map[Key::F29] = NSF29FunctionKey;
|
||||
keycode_map[Key::F30] = NSF30FunctionKey;
|
||||
keycode_map[Key::F31] = NSF31FunctionKey;
|
||||
keycode_map[Key::F32] = NSF32FunctionKey;
|
||||
keycode_map[Key::F33] = NSF33FunctionKey;
|
||||
keycode_map[Key::F34] = NSF34FunctionKey;
|
||||
keycode_map[Key::F35] = NSF35FunctionKey;
|
||||
keycode_map[Key::MENU] = NSMenuFunctionKey;
|
||||
keycode_map[Key::HELP] = NSHelpFunctionKey;
|
||||
keycode_map[Key::STOP] = NSStopFunctionKey;
|
||||
keycode_map[Key::LAUNCH0] = NSUserFunctionKey;
|
||||
keycode_map[Key::SPACE] = 0x0020;
|
||||
keycode_map[Key::EXCLAM] = '!';
|
||||
keycode_map[Key::QUOTEDBL] = '\"';
|
||||
keycode_map[Key::NUMBERSIGN] = '#';
|
||||
keycode_map[Key::DOLLAR] = '$';
|
||||
keycode_map[Key::PERCENT] = '\%';
|
||||
keycode_map[Key::AMPERSAND] = '&';
|
||||
keycode_map[Key::APOSTROPHE] = '\'';
|
||||
keycode_map[Key::PARENLEFT] = '(';
|
||||
keycode_map[Key::PARENRIGHT] = ')';
|
||||
keycode_map[Key::ASTERISK] = '*';
|
||||
keycode_map[Key::PLUS] = '+';
|
||||
keycode_map[Key::COMMA] = ',';
|
||||
keycode_map[Key::MINUS] = '-';
|
||||
keycode_map[Key::PERIOD] = '.';
|
||||
keycode_map[Key::SLASH] = '/';
|
||||
keycode_map[Key::KEY_0] = '0';
|
||||
keycode_map[Key::KEY_1] = '1';
|
||||
keycode_map[Key::KEY_2] = '2';
|
||||
keycode_map[Key::KEY_3] = '3';
|
||||
keycode_map[Key::KEY_4] = '4';
|
||||
keycode_map[Key::KEY_5] = '5';
|
||||
keycode_map[Key::KEY_6] = '6';
|
||||
keycode_map[Key::KEY_7] = '7';
|
||||
keycode_map[Key::KEY_8] = '8';
|
||||
keycode_map[Key::KEY_9] = '9';
|
||||
keycode_map[Key::COLON] = ':';
|
||||
keycode_map[Key::SEMICOLON] = ';';
|
||||
keycode_map[Key::LESS] = '<';
|
||||
keycode_map[Key::EQUAL] = '=';
|
||||
keycode_map[Key::GREATER] = '>';
|
||||
keycode_map[Key::QUESTION] = '?';
|
||||
keycode_map[Key::AT] = '@';
|
||||
keycode_map[Key::A] = 'a';
|
||||
keycode_map[Key::B] = 'b';
|
||||
keycode_map[Key::C] = 'c';
|
||||
keycode_map[Key::D] = 'd';
|
||||
keycode_map[Key::E] = 'e';
|
||||
keycode_map[Key::F] = 'f';
|
||||
keycode_map[Key::G] = 'g';
|
||||
keycode_map[Key::H] = 'h';
|
||||
keycode_map[Key::I] = 'i';
|
||||
keycode_map[Key::J] = 'j';
|
||||
keycode_map[Key::K] = 'k';
|
||||
keycode_map[Key::L] = 'l';
|
||||
keycode_map[Key::M] = 'm';
|
||||
keycode_map[Key::N] = 'n';
|
||||
keycode_map[Key::O] = 'o';
|
||||
keycode_map[Key::P] = 'p';
|
||||
keycode_map[Key::Q] = 'q';
|
||||
keycode_map[Key::R] = 'r';
|
||||
keycode_map[Key::S] = 's';
|
||||
keycode_map[Key::T] = 't';
|
||||
keycode_map[Key::U] = 'u';
|
||||
keycode_map[Key::V] = 'v';
|
||||
keycode_map[Key::W] = 'w';
|
||||
keycode_map[Key::X] = 'x';
|
||||
keycode_map[Key::Y] = 'y';
|
||||
keycode_map[Key::Z] = 'z';
|
||||
keycode_map[Key::BRACKETLEFT] = '[';
|
||||
keycode_map[Key::BACKSLASH] = '\\';
|
||||
keycode_map[Key::BRACKETRIGHT] = ']';
|
||||
keycode_map[Key::ASCIICIRCUM] = '^';
|
||||
keycode_map[Key::UNDERSCORE] = '_';
|
||||
keycode_map[Key::QUOTELEFT] = '`';
|
||||
keycode_map[Key::BRACELEFT] = '{';
|
||||
keycode_map[Key::BAR] = '|';
|
||||
keycode_map[Key::BRACERIGHT] = '}';
|
||||
keycode_map[Key::ASCIITILDE] = '~';
|
||||
|
||||
// Keysym -> physical location.
|
||||
// Ctrl.
|
||||
location_map[0x3b] = KeyLocation::LEFT;
|
||||
location_map[0x3e] = KeyLocation::RIGHT;
|
||||
// Shift.
|
||||
location_map[0x38] = KeyLocation::LEFT;
|
||||
location_map[0x3c] = KeyLocation::RIGHT;
|
||||
// Alt/Option.
|
||||
location_map[0x3a] = KeyLocation::LEFT;
|
||||
location_map[0x3d] = KeyLocation::RIGHT;
|
||||
// Meta/Command (yes, right < left).
|
||||
location_map[0x36] = KeyLocation::RIGHT;
|
||||
location_map[0x37] = KeyLocation::LEFT;
|
||||
}
|
||||
|
||||
bool KeyMappingMacOS::is_numpad_key(unsigned int p_key) {
|
||||
return numpad_keys.has(p_key);
|
||||
}
|
||||
|
||||
// Translates a macOS keycode to a Godot keycode.
|
||||
Key KeyMappingMacOS::translate_key(unsigned int p_key) {
|
||||
const Key *key = keysym_map.getptr(p_key);
|
||||
if (key) {
|
||||
return *key;
|
||||
}
|
||||
return Key::NONE;
|
||||
}
|
||||
|
||||
// Translates a Godot keycode back to a macOS keycode.
|
||||
unsigned int KeyMappingMacOS::unmap_key(Key p_key) {
|
||||
const unsigned int *key = keysym_map_inv.getptr(p_key);
|
||||
if (key) {
|
||||
return *key;
|
||||
}
|
||||
return 127;
|
||||
}
|
||||
|
||||
// Remap key according to current keyboard layout.
|
||||
Key KeyMappingMacOS::remap_key(unsigned int p_key, unsigned int p_state, bool p_unicode) {
|
||||
if (is_numpad_key(p_key)) {
|
||||
return translate_key(p_key);
|
||||
}
|
||||
|
||||
TISInputSourceRef current_keyboard = TISCopyCurrentKeyboardInputSource();
|
||||
if (!current_keyboard) {
|
||||
return translate_key(p_key);
|
||||
}
|
||||
|
||||
CFDataRef layout_data = (CFDataRef)TISGetInputSourceProperty(current_keyboard, kTISPropertyUnicodeKeyLayoutData);
|
||||
if (!layout_data) {
|
||||
return translate_key(p_key);
|
||||
}
|
||||
|
||||
const UCKeyboardLayout *keyboard_layout = (const UCKeyboardLayout *)CFDataGetBytePtr(layout_data);
|
||||
|
||||
String keysym;
|
||||
UInt32 keys_down = 0;
|
||||
UniChar chars[256] = {};
|
||||
UniCharCount real_length = 0;
|
||||
|
||||
OSStatus err = UCKeyTranslate(keyboard_layout,
|
||||
p_key,
|
||||
kUCKeyActionDisplay,
|
||||
(p_unicode) ? 0 : (p_state >> 8) & 0xFF,
|
||||
LMGetKbdType(),
|
||||
kUCKeyTranslateNoDeadKeysBit,
|
||||
&keys_down,
|
||||
std::size(chars),
|
||||
&real_length,
|
||||
chars);
|
||||
|
||||
if (err != noErr) {
|
||||
return translate_key(p_key);
|
||||
}
|
||||
|
||||
keysym = String::utf16((char16_t *)chars, real_length);
|
||||
if (keysym.is_empty()) {
|
||||
return translate_key(p_key);
|
||||
}
|
||||
|
||||
char32_t c = keysym[0];
|
||||
if (p_unicode) {
|
||||
return fix_key_label(c, translate_key(p_key));
|
||||
} else {
|
||||
return fix_keycode(c, translate_key(p_key));
|
||||
}
|
||||
}
|
||||
|
||||
// Translates a macOS keycode to a Godot key location.
|
||||
KeyLocation KeyMappingMacOS::translate_location(unsigned int p_key) {
|
||||
const KeyLocation *location = location_map.getptr(p_key);
|
||||
if (location) {
|
||||
return *location;
|
||||
}
|
||||
return KeyLocation::UNSPECIFIED;
|
||||
}
|
||||
|
||||
String KeyMappingMacOS::keycode_get_native_string(Key p_keycode) {
|
||||
const char32_t *key = keycode_map.getptr(p_keycode);
|
||||
if (key) {
|
||||
return String::chr(*key);
|
||||
}
|
||||
return String();
|
||||
}
|
||||
|
||||
unsigned int KeyMappingMacOS::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;
|
||||
}
|
94
platform/macos/macos_quartz_core_spi.h
Normal file
94
platform/macos/macos_quartz_core_spi.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/**************************************************************************/
|
||||
/* macos_quartz_core_spi.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the OpenEmu Team nor the
|
||||
// names of its contributors may be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY OpenEmu Team ''AS IS'' AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL OpenEmu Team BE LIABLE FOR ANY
|
||||
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Quartz/Quartz.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// https://chromium.googlesource.com/chromium/src/+/refs/heads/main/ui/base/cocoa/remote_layer_api.h
|
||||
|
||||
// The CAContextID type identifies a CAContext across processes. This is the
|
||||
// token that is passed from the process that is sharing the CALayer that it is
|
||||
// rendering to the process that will be displaying that CALayer.
|
||||
typedef uint32_t CAContextID;
|
||||
|
||||
// The CAContext has a static CAContextID which can be sent to another process.
|
||||
// When a CALayerHost is created using that CAContextID in another process, the
|
||||
// content displayed by that CALayerHost will be the content of the CALayer
|
||||
// that is set as the |layer| property on the CAContext.
|
||||
@interface CAContext : NSObject
|
||||
+ (instancetype)contextWithCGSConnection:(CAContextID)contextId options:(NSDictionary *)optionsDict;
|
||||
@property(readonly) CAContextID contextId;
|
||||
@property(retain) CALayer *layer;
|
||||
@end
|
||||
|
||||
// The CALayerHost is created in the process that will display the content
|
||||
// being rendered by another process. Setting the |contextId| property on
|
||||
// an object of this class will make this layer display the content of the
|
||||
// CALayer that is set to the CAContext with that CAContextID in the layer
|
||||
// sharing process.
|
||||
@interface CALayerHost : CALayer
|
||||
@property CAContextID contextId;
|
||||
@end
|
||||
|
||||
// The CGSConnectionID is used to create the CAContext in the process that is
|
||||
// going to share the CALayers that it is rendering to another process to
|
||||
// display.
|
||||
typedef uint32_t CGSConnectionID;
|
||||
extern "C" CGSConnectionID CGSMainConnectionID(void);
|
||||
|
||||
extern "C" NSString *const kCAContextCIFilterBehavior;
|
11
platform/macos/msvs.py
Normal file
11
platform/macos/msvs.py
Normal file
@@ -0,0 +1,11 @@
|
||||
# Tuples with the name of the arch
|
||||
def get_platforms():
|
||||
return [("arm64", "arm64"), ("x64", "x86_64")]
|
||||
|
||||
|
||||
def get_configurations():
|
||||
return ["editor", "template_debug", "template_release"]
|
||||
|
||||
|
||||
def get_build_prefix(env):
|
||||
return []
|
161
platform/macos/native_menu_macos.h
Normal file
161
platform/macos/native_menu_macos.h
Normal file
@@ -0,0 +1,161 @@
|
||||
/**************************************************************************/
|
||||
/* native_menu_macos.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/templates/hash_map.h"
|
||||
#include "core/templates/rid_owner.h"
|
||||
#include "servers/display/native_menu.h"
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <ApplicationServices/ApplicationServices.h>
|
||||
|
||||
class NativeMenuMacOS : public NativeMenu {
|
||||
GDCLASS(NativeMenuMacOS, NativeMenu)
|
||||
|
||||
struct MenuData {
|
||||
NSMenu *menu = nullptr;
|
||||
|
||||
Callable open_cb;
|
||||
Callable close_cb;
|
||||
bool is_open = false;
|
||||
bool is_system = false;
|
||||
};
|
||||
|
||||
mutable RID_PtrOwner<MenuData> menus;
|
||||
HashMap<NSMenu *, RID> menu_lookup;
|
||||
|
||||
NSMenu *main_menu_ns = nullptr;
|
||||
NSMenu *application_menu_ns = nullptr;
|
||||
NSMenu *window_menu_ns = nullptr;
|
||||
NSMenu *help_menu_ns = nullptr;
|
||||
NSMenu *dock_menu_ns = nullptr;
|
||||
|
||||
RID main_menu;
|
||||
RID application_menu;
|
||||
RID window_menu;
|
||||
RID help_menu;
|
||||
RID dock_menu;
|
||||
|
||||
int _get_system_menu_start(const NSMenu *p_menu) const;
|
||||
int _get_system_menu_count(const NSMenu *p_menu) const;
|
||||
bool _is_menu_opened(NSMenu *p_menu) const;
|
||||
NSMenuItem *_menu_add_item(NSMenu *p_menu, const String &p_label, Key p_accel, int p_index, int *r_out);
|
||||
|
||||
public:
|
||||
void _register_system_menus(NSMenu *p_main_menu, NSMenu *p_application_menu, NSMenu *p_window_menu, NSMenu *p_help_menu, NSMenu *p_dock_menu);
|
||||
NSMenu *_get_dock_menu();
|
||||
|
||||
void _menu_need_update(NSMenu *p_menu);
|
||||
void _menu_open(NSMenu *p_menu);
|
||||
void _menu_close(NSMenu *p_menu);
|
||||
void _menu_close_cb(const RID &p_rid);
|
||||
|
||||
virtual bool has_feature(Feature p_feature) const override;
|
||||
|
||||
virtual bool has_system_menu(SystemMenus p_menu_id) const override;
|
||||
virtual RID get_system_menu(SystemMenus p_menu_id) const override;
|
||||
|
||||
virtual RID create_menu() override;
|
||||
virtual bool has_menu(const RID &p_rid) const override;
|
||||
virtual void free_menu(const RID &p_rid) override;
|
||||
|
||||
NSMenu *get_native_menu_handle(const RID &p_rid);
|
||||
|
||||
virtual Size2 get_size(const RID &p_rid) const override;
|
||||
virtual void popup(const RID &p_rid, const Vector2i &p_position) override;
|
||||
|
||||
virtual void set_interface_direction(const RID &p_rid, bool p_is_rtl) override;
|
||||
virtual void set_popup_open_callback(const RID &p_rid, const Callable &p_callback) override;
|
||||
virtual Callable get_popup_open_callback(const RID &p_rid) const override;
|
||||
virtual void set_popup_close_callback(const RID &p_rid, const Callable &p_callback) override;
|
||||
virtual Callable get_popup_close_callback(const RID &p_rid) const override;
|
||||
virtual void set_minimum_width(const RID &p_rid, float p_width) override;
|
||||
virtual float get_minimum_width(const RID &p_rid) const override;
|
||||
|
||||
virtual bool is_opened(const RID &p_rid) const override;
|
||||
|
||||
virtual int add_submenu_item(const RID &p_rid, const String &p_label, const RID &p_submenu_rid, const Variant &p_tag = Variant(), int p_index = -1) override;
|
||||
virtual int add_item(const RID &p_rid, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override;
|
||||
virtual int add_check_item(const RID &p_rid, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override;
|
||||
virtual int add_icon_item(const RID &p_rid, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override;
|
||||
virtual int add_icon_check_item(const RID &p_rid, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override;
|
||||
virtual int add_radio_check_item(const RID &p_rid, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override;
|
||||
virtual int add_icon_radio_check_item(const RID &p_rid, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override;
|
||||
virtual int add_multistate_item(const RID &p_rid, const String &p_label, int p_max_states, int p_default_state, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override;
|
||||
virtual int add_separator(const RID &p_rid, int p_index = -1) override;
|
||||
|
||||
virtual int find_item_index_with_text(const RID &p_rid, const String &p_text) const override;
|
||||
virtual int find_item_index_with_tag(const RID &p_rid, const Variant &p_tag) const override;
|
||||
|
||||
virtual bool is_item_checked(const RID &p_rid, int p_idx) const override;
|
||||
virtual bool is_item_checkable(const RID &p_rid, int p_idx) const override;
|
||||
virtual bool is_item_radio_checkable(const RID &p_rid, int p_idx) const override;
|
||||
virtual Callable get_item_callback(const RID &p_rid, int p_idx) const override;
|
||||
virtual Callable get_item_key_callback(const RID &p_rid, int p_idx) const override;
|
||||
virtual Variant get_item_tag(const RID &p_rid, int p_idx) const override;
|
||||
virtual String get_item_text(const RID &p_rid, int p_idx) const override;
|
||||
virtual RID get_item_submenu(const RID &p_rid, int p_idx) const override;
|
||||
virtual Key get_item_accelerator(const RID &p_rid, int p_idx) const override;
|
||||
virtual bool is_item_disabled(const RID &p_rid, int p_idx) const override;
|
||||
virtual bool is_item_hidden(const RID &p_rid, int p_idx) const override;
|
||||
virtual String get_item_tooltip(const RID &p_rid, int p_idx) const override;
|
||||
virtual int get_item_state(const RID &p_rid, int p_idx) const override;
|
||||
virtual int get_item_max_states(const RID &p_rid, int p_idx) const override;
|
||||
virtual Ref<Texture2D> get_item_icon(const RID &p_rid, int p_idx) const override;
|
||||
virtual int get_item_indentation_level(const RID &p_rid, int p_idx) const override;
|
||||
|
||||
virtual void set_item_checked(const RID &p_rid, int p_idx, bool p_checked) override;
|
||||
virtual void set_item_checkable(const RID &p_rid, int p_idx, bool p_checkable) override;
|
||||
virtual void set_item_radio_checkable(const RID &p_rid, int p_idx, bool p_checkable) override;
|
||||
virtual void set_item_callback(const RID &p_rid, int p_idx, const Callable &p_callback) override;
|
||||
virtual void set_item_key_callback(const RID &p_rid, int p_idx, const Callable &p_key_callback) override;
|
||||
virtual void set_item_hover_callbacks(const RID &p_rid, int p_idx, const Callable &p_callback) override;
|
||||
virtual void set_item_tag(const RID &p_rid, int p_idx, const Variant &p_tag) override;
|
||||
virtual void set_item_text(const RID &p_rid, int p_idx, const String &p_text) override;
|
||||
virtual void set_item_submenu(const RID &p_rid, int p_idx, const RID &p_submenu_rid) override;
|
||||
virtual void set_item_accelerator(const RID &p_rid, int p_idx, Key p_keycode) override;
|
||||
virtual void set_item_disabled(const RID &p_rid, int p_idx, bool p_disabled) override;
|
||||
virtual void set_item_hidden(const RID &p_rid, int p_idx, bool p_hidden) override;
|
||||
virtual void set_item_tooltip(const RID &p_rid, int p_idx, const String &p_tooltip) override;
|
||||
virtual void set_item_state(const RID &p_rid, int p_idx, int p_state) override;
|
||||
virtual void set_item_max_states(const RID &p_rid, int p_idx, int p_max_states) override;
|
||||
virtual void set_item_icon(const RID &p_rid, int p_idx, const Ref<Texture2D> &p_icon) override;
|
||||
virtual void set_item_indentation_level(const RID &p_rid, int p_idx, int p_level) override;
|
||||
|
||||
virtual int get_item_count(const RID &p_rid) const override;
|
||||
virtual bool is_system_menu(const RID &p_rid) const override;
|
||||
|
||||
virtual void remove_item(const RID &p_rid, int p_idx) override;
|
||||
virtual void clear(const RID &p_rid) override;
|
||||
|
||||
NativeMenuMacOS();
|
||||
~NativeMenuMacOS();
|
||||
};
|
1403
platform/macos/native_menu_macos.mm
Normal file
1403
platform/macos/native_menu_macos.mm
Normal file
File diff suppressed because it is too large
Load Diff
190
platform/macos/os_macos.h
Normal file
190
platform/macos/os_macos.h
Normal file
@@ -0,0 +1,190 @@
|
||||
/**************************************************************************/
|
||||
/* os_macos.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "crash_handler_macos.h"
|
||||
|
||||
#include "core/input/input.h"
|
||||
#import "drivers/coreaudio/audio_driver_coreaudio.h"
|
||||
#import "drivers/coremidi/midi_driver_coremidi.h"
|
||||
#include "drivers/unix/os_unix.h"
|
||||
#include "servers/audio_server.h"
|
||||
|
||||
class JoypadSDL;
|
||||
|
||||
class OS_MacOS : public OS_Unix {
|
||||
#ifdef COREAUDIO_ENABLED
|
||||
AudioDriverCoreAudio audio_driver;
|
||||
#endif
|
||||
#ifdef COREMIDI_ENABLED
|
||||
MIDIDriverCoreMidi midi_driver;
|
||||
#endif
|
||||
|
||||
CrashHandler crash_handler;
|
||||
|
||||
List<String> launch_service_args;
|
||||
|
||||
CGFloat _weight_to_ct(int p_weight) const;
|
||||
CGFloat _stretch_to_ct(int p_stretch) const;
|
||||
String _get_default_fontname(const String &p_font_name) const;
|
||||
|
||||
static _FORCE_INLINE_ String get_framework_executable(const String &p_path);
|
||||
|
||||
protected:
|
||||
const char *execpath = nullptr;
|
||||
int argc = 0;
|
||||
char **argv = nullptr;
|
||||
|
||||
#ifdef SDL_ENABLED
|
||||
JoypadSDL *joypad_sdl = nullptr;
|
||||
#endif
|
||||
MainLoop *main_loop = nullptr;
|
||||
CFRunLoopTimerRef wait_timer = nil;
|
||||
|
||||
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 add_frame_delay(bool p_can_draw, bool p_wake_for_events) override;
|
||||
|
||||
virtual void set_cmdline_platform_args(const List<String> &p_args);
|
||||
virtual List<String> get_cmdline_platform_args() const override;
|
||||
|
||||
virtual void load_shell_environment() const override;
|
||||
|
||||
virtual String get_name() const override;
|
||||
virtual String get_distribution_name() const override;
|
||||
virtual String get_version() const override;
|
||||
virtual String get_version_alias() 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, GDExtensionData *p_data = 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_temp_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(const String &p_uri) override;
|
||||
virtual Error shell_show_in_file_manager(String p_path, bool p_open_folder) override;
|
||||
|
||||
virtual String get_locale() const override;
|
||||
|
||||
virtual Vector<String> get_system_fonts() const override;
|
||||
virtual String get_system_font_path(const String &p_font_name, int p_weight = 400, int p_stretch = 100, bool p_italic = false) const override;
|
||||
virtual Vector<String> get_system_font_path_for_text(const String &p_font_name, const String &p_text, const String &p_locale = String(), const String &p_script = String(), int p_weight = 400, int p_stretch = 100, bool p_italic = false) const override;
|
||||
virtual String get_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 Error open_with_program(const String &p_program_path, const List<String> &p_paths) override;
|
||||
virtual bool is_process_running(const ProcessID &p_pid) const override;
|
||||
|
||||
virtual String get_unique_id() const override;
|
||||
virtual String get_processor_name() const override;
|
||||
|
||||
virtual String get_model_name() const override;
|
||||
|
||||
virtual bool is_sandboxed() const override;
|
||||
virtual bool request_permission(const String &p_name) override;
|
||||
virtual Vector<String> get_granted_permissions() const override;
|
||||
virtual void revoke_granted_permissions() override;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
static bool is_debugger_attached();
|
||||
void wait_for_debugger(uint32_t p_msec);
|
||||
#endif
|
||||
|
||||
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;
|
||||
|
||||
virtual String get_system_ca_certificates() override;
|
||||
virtual OS::PreferredTextureFormat get_preferred_texture_format() const override;
|
||||
|
||||
virtual void run() = 0;
|
||||
|
||||
OS_MacOS(const char *p_execpath, int p_argc, char **p_argv);
|
||||
};
|
||||
|
||||
class OS_MacOS_NSApp : public OS_MacOS {
|
||||
id delegate = nullptr;
|
||||
bool should_terminate = false;
|
||||
bool main_started = false;
|
||||
|
||||
CFRunLoopObserverRef pre_wait_observer = nil;
|
||||
|
||||
void terminate();
|
||||
|
||||
public:
|
||||
void start_main(); // Initializes and runs Godot main loop.
|
||||
void cleanup();
|
||||
bool os_should_terminate() const { return should_terminate; }
|
||||
int get_cmd_argc() const { return argc; }
|
||||
|
||||
virtual void run() override;
|
||||
|
||||
OS_MacOS_NSApp(const char *p_execpath, int p_argc, char **p_argv);
|
||||
};
|
||||
|
||||
class OS_MacOS_Headless : public OS_MacOS {
|
||||
public:
|
||||
virtual void run() override;
|
||||
|
||||
OS_MacOS_Headless(const char *p_execpath, int p_argc, char **p_argv);
|
||||
};
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
|
||||
class OS_MacOS_Embedded : public OS_MacOS {
|
||||
public:
|
||||
virtual void run() override;
|
||||
|
||||
OS_MacOS_Embedded(const char *p_execpath, int p_argc, char **p_argv);
|
||||
};
|
||||
|
||||
#endif
|
1293
platform/macos/os_macos.mm
Normal file
1293
platform/macos/os_macos.mm
Normal file
File diff suppressed because it is too large
Load Diff
44
platform/macos/platform_config.h
Normal file
44
platform/macos/platform_config.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/**************************************************************************/
|
||||
/* platform_config.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <alloca.h>
|
||||
|
||||
#define PLATFORM_THREAD_OVERRIDE
|
||||
|
||||
#define PTHREAD_RENAME_SELF
|
||||
|
||||
#define _weakify(var) __weak typeof(var) GDWeak_##var = var;
|
||||
#define _strongify(var) \
|
||||
_Pragma("clang diagnostic push") \
|
||||
_Pragma("clang diagnostic ignored \"-Wshadow\"") \
|
||||
__strong typeof(var) var = GDWeak_##var; \
|
||||
_Pragma("clang diagnostic pop")
|
49
platform/macos/platform_gl.h
Normal file
49
platform/macos/platform_gl.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/**************************************************************************/
|
||||
/* platform_gl.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef GL_API_ENABLED
|
||||
#define GL_API_ENABLED // Allow using desktop GL.
|
||||
#endif
|
||||
|
||||
#ifndef GLES_API_ENABLED
|
||||
#define GLES_API_ENABLED // Allow using GLES (ANGLE).
|
||||
#endif
|
||||
|
||||
#ifdef EGL_STATIC
|
||||
#define KHRONOS_STATIC 1
|
||||
#include "thirdparty/angle/include/EGL/egl.h"
|
||||
#include "thirdparty/angle/include/EGL/eglext.h"
|
||||
#undef KHRONOS_STATIC
|
||||
#else
|
||||
#include "thirdparty/glad/glad/egl.h"
|
||||
#endif
|
||||
#include "thirdparty/glad/glad/gl.h"
|
121
platform/macos/platform_macos_builders.py
Normal file
121
platform/macos/platform_macos_builders.py
Normal file
@@ -0,0 +1,121 @@
|
||||
"""Functions used to generate source files during build time"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
from platform_methods import get_build_version, lipo
|
||||
|
||||
|
||||
def generate_bundle(target, source, env):
|
||||
bin_dir = env.Dir("#bin").abspath
|
||||
|
||||
if env.editor_build:
|
||||
# Editor bundle.
|
||||
prefix = "godot." + env["platform"] + "." + env["target"]
|
||||
if env.dev_build:
|
||||
prefix += ".dev"
|
||||
if env["precision"] == "double":
|
||||
prefix += ".double"
|
||||
|
||||
# Lipo editor executable.
|
||||
target_bin = lipo(bin_dir + "/" + prefix, env.extra_suffix + env.module_version_string)
|
||||
|
||||
# Assemble .app bundle and update version info.
|
||||
app_dir = env.Dir(
|
||||
"#bin/" + (prefix + env.extra_suffix + env.module_version_string).replace(".", "_") + ".app"
|
||||
).abspath
|
||||
templ = env.Dir("#misc/dist/macos_tools.app").abspath
|
||||
if os.path.exists(app_dir):
|
||||
shutil.rmtree(app_dir)
|
||||
|
||||
# Create the .app bundle directory itself from scratch so that the creation
|
||||
# date is accurate, but copy the rest of the template over.
|
||||
os.mkdir(app_dir)
|
||||
shutil.copytree(
|
||||
os.path.join(templ, "Contents"),
|
||||
os.path.join(app_dir, "Contents"),
|
||||
ignore=shutil.ignore_patterns("Info.plist"),
|
||||
)
|
||||
|
||||
if not os.path.isdir(app_dir + "/Contents/MacOS"):
|
||||
os.mkdir(app_dir + "/Contents/MacOS")
|
||||
if target_bin != "":
|
||||
shutil.copy(target_bin, app_dir + "/Contents/MacOS/Godot")
|
||||
if "mono" in env.module_version_string:
|
||||
shutil.copytree(env.Dir("#bin/GodotSharp").abspath, app_dir + "/Contents/Resources/GodotSharp")
|
||||
version = get_build_version(False)
|
||||
short_version = get_build_version(True)
|
||||
with open(env.Dir("#misc/dist/macos").abspath + "/editor_info_plist.template", "rt", encoding="utf-8") as fin:
|
||||
with open(app_dir + "/Contents/Info.plist", "wt", encoding="utf-8", newline="\n") as fout:
|
||||
for line in fin:
|
||||
line = line.replace("$version", version)
|
||||
line = line.replace("$short_version", short_version)
|
||||
fout.write(line)
|
||||
|
||||
# Sign .app bundle.
|
||||
if env["bundle_sign_identity"] != "":
|
||||
sign_command = [
|
||||
"codesign",
|
||||
"-s",
|
||||
env["bundle_sign_identity"],
|
||||
"--deep",
|
||||
"--force",
|
||||
"--options=runtime",
|
||||
"--entitlements",
|
||||
]
|
||||
if env.dev_build:
|
||||
sign_command += [env.Dir("#misc/dist/macos").abspath + "/editor_debug.entitlements"]
|
||||
else:
|
||||
sign_command += [env.Dir("#misc/dist/macos").abspath + "/editor.entitlements"]
|
||||
sign_command += [app_dir]
|
||||
subprocess.run(sign_command)
|
||||
|
||||
else:
|
||||
# Template bundle.
|
||||
app_prefix = "godot." + env["platform"]
|
||||
rel_prefix = "godot." + env["platform"] + "." + "template_release"
|
||||
dbg_prefix = "godot." + env["platform"] + "." + "template_debug"
|
||||
if env.dev_build:
|
||||
app_prefix += ".dev"
|
||||
rel_prefix += ".dev"
|
||||
dbg_prefix += ".dev"
|
||||
if env["precision"] == "double":
|
||||
app_prefix += ".double"
|
||||
rel_prefix += ".double"
|
||||
dbg_prefix += ".double"
|
||||
|
||||
# Lipo template executables.
|
||||
rel_target_bin = lipo(bin_dir + "/" + rel_prefix, env.extra_suffix + env.module_version_string)
|
||||
dbg_target_bin = lipo(bin_dir + "/" + dbg_prefix, env.extra_suffix + env.module_version_string)
|
||||
|
||||
# Assemble .app bundle.
|
||||
app_dir = env.Dir("#bin/macos_template.app").abspath
|
||||
templ = env.Dir("#misc/dist/macos_template.app").abspath
|
||||
if os.path.exists(app_dir):
|
||||
shutil.rmtree(app_dir)
|
||||
shutil.copytree(templ, app_dir)
|
||||
if not os.path.isdir(app_dir + "/Contents/MacOS"):
|
||||
os.mkdir(app_dir + "/Contents/MacOS")
|
||||
if rel_target_bin != "":
|
||||
shutil.copy(rel_target_bin, app_dir + "/Contents/MacOS/godot_macos_release.universal")
|
||||
if dbg_target_bin != "":
|
||||
shutil.copy(dbg_target_bin, app_dir + "/Contents/MacOS/godot_macos_debug.universal")
|
||||
|
||||
# ZIP .app bundle.
|
||||
zip_dir = env.Dir(
|
||||
"#bin/" + (app_prefix + env.extra_suffix + env.module_version_string).replace(".", "_")
|
||||
).abspath
|
||||
shutil.make_archive(zip_dir, "zip", root_dir=bin_dir, base_dir="macos_template.app")
|
||||
shutil.rmtree(app_dir)
|
||||
|
||||
|
||||
def make_debug_macos(target, source, env):
|
||||
dst = str(target[0])
|
||||
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(dst))
|
||||
else:
|
||||
os.system("dsymutil {0} -o {0}.dSYM".format(dst))
|
||||
os.system("strip -u -r {0}".format(dst))
|
33
platform/macos/platform_thread.h
Normal file
33
platform/macos/platform_thread.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/**************************************************************************/
|
||||
/* platform_thread.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "drivers/apple/thread_apple.h"
|
55
platform/macos/rendering_context_driver_vulkan_macos.h
Normal file
55
platform/macos/rendering_context_driver_vulkan_macos.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/**************************************************************************/
|
||||
/* rendering_context_driver_vulkan_macos.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef VULKAN_ENABLED
|
||||
|
||||
#include "drivers/vulkan/rendering_context_driver_vulkan.h"
|
||||
|
||||
#import <QuartzCore/CAMetalLayer.h>
|
||||
|
||||
class RenderingContextDriverVulkanMacOS : public RenderingContextDriverVulkan {
|
||||
private:
|
||||
virtual const char *_get_platform_surface_extension() const override final;
|
||||
|
||||
protected:
|
||||
SurfaceID surface_create(const void *p_platform_data) override final;
|
||||
|
||||
public:
|
||||
struct WindowPlatformData {
|
||||
CAMetalLayer *const *layer_ptr;
|
||||
};
|
||||
|
||||
RenderingContextDriverVulkanMacOS();
|
||||
~RenderingContextDriverVulkanMacOS();
|
||||
};
|
||||
|
||||
#endif // VULKAN_ENABLED
|
69
platform/macos/rendering_context_driver_vulkan_macos.mm
Normal file
69
platform/macos/rendering_context_driver_vulkan_macos.mm
Normal file
@@ -0,0 +1,69 @@
|
||||
/**************************************************************************/
|
||||
/* rendering_context_driver_vulkan_macos.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "rendering_context_driver_vulkan_macos.h"
|
||||
|
||||
#ifdef VULKAN_ENABLED
|
||||
|
||||
#ifdef USE_VOLK
|
||||
#include <volk.h>
|
||||
#else
|
||||
#include <vulkan/vulkan_metal.h>
|
||||
#endif
|
||||
|
||||
const char *RenderingContextDriverVulkanMacOS::_get_platform_surface_extension() const {
|
||||
return VK_EXT_METAL_SURFACE_EXTENSION_NAME;
|
||||
}
|
||||
|
||||
RenderingContextDriver::SurfaceID RenderingContextDriverVulkanMacOS::surface_create(const void *p_platform_data) {
|
||||
const WindowPlatformData *wpd = (const WindowPlatformData *)(p_platform_data);
|
||||
|
||||
VkMetalSurfaceCreateInfoEXT create_info = {};
|
||||
create_info.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT;
|
||||
create_info.pLayer = *wpd->layer_ptr;
|
||||
|
||||
VkSurfaceKHR vk_surface = VK_NULL_HANDLE;
|
||||
VkResult err = vkCreateMetalSurfaceEXT(instance_get(), &create_info, get_allocation_callbacks(VK_OBJECT_TYPE_SURFACE_KHR), &vk_surface);
|
||||
ERR_FAIL_COND_V(err != VK_SUCCESS, SurfaceID());
|
||||
|
||||
Surface *surface = memnew(Surface);
|
||||
surface->vk_surface = vk_surface;
|
||||
return SurfaceID(surface);
|
||||
}
|
||||
|
||||
RenderingContextDriverVulkanMacOS::RenderingContextDriverVulkanMacOS() {
|
||||
// Does nothing.
|
||||
}
|
||||
|
||||
RenderingContextDriverVulkanMacOS::~RenderingContextDriverVulkanMacOS() {
|
||||
// Does nothing.
|
||||
}
|
||||
|
||||
#endif // VULKAN_ENABLED
|
68
platform/macos/tts_macos.h
Normal file
68
platform/macos/tts_macos.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/**************************************************************************/
|
||||
/* tts_macos.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/string/ustring.h"
|
||||
#include "core/templates/hash_map.h"
|
||||
#include "core/templates/list.h"
|
||||
#include "core/variant/array.h"
|
||||
#include "servers/display_server.h"
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
#if __has_include(<AVFAudio/AVSpeechSynthesis.h>)
|
||||
#import <AVFAudio/AVSpeechSynthesis.h>
|
||||
#else
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#endif
|
||||
|
||||
@interface TTS_MacOS : 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
|
266
platform/macos/tts_macos.mm
Normal file
266
platform/macos/tts_macos.mm
Normal file
@@ -0,0 +1,266 @@
|
||||
/**************************************************************************/
|
||||
/* tts_macos.mm */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#import "tts_macos.h"
|
||||
|
||||
@implementation TTS_MacOS
|
||||
|
||||
- (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::remap(message.rate, 1.f, 10.f, AVSpeechUtteranceDefaultSpeechRate, AVSpeechUtteranceMaximumSpeechRate)];
|
||||
} else if (message.rate < 1.f) {
|
||||
[new_utterance setRate:Math::remap(message.rate, 0.1f, 1.f, AVSpeechUtteranceMinimumSpeechRate, AVSpeechUtteranceDefaultSpeechRate)];
|
||||
}
|
||||
[new_utterance setPitchMultiplier:message.pitch];
|
||||
[new_utterance setVolume:(Math::remap(message.volume, 0.f, 100.f, 0.f, 1.f))];
|
||||
|
||||
ids[new_utterance] = message.id;
|
||||
[av_synth speakUtterance:new_utterance];
|
||||
} 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::remap(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
|
Reference in New Issue
Block a user