feat: updated engine version to 4.4-rc1
This commit is contained in:
parent
ee00efde1f
commit
21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
|
|
@ -26,11 +27,22 @@ def generate_bundle(target, source, env):
|
|||
target_bin = lipo(bin_dir + "/" + prefix, env.extra_suffix + env.module_version_string)
|
||||
|
||||
# Assemble .app bundle and update version info.
|
||||
app_dir = Dir("#bin/" + (prefix + env.extra_suffix + env.module_version_string).replace(".", "_") + ".app").abspath
|
||||
app_dir = Dir(
|
||||
"#bin/" + (prefix + env.extra_suffix + env.module_version_string).replace(".", "_") + ".app"
|
||||
).abspath
|
||||
templ = Dir("#misc/dist/macos_tools.app").abspath
|
||||
if os.path.exists(app_dir):
|
||||
shutil.rmtree(app_dir)
|
||||
shutil.copytree(templ, app_dir, ignore=shutil.ignore_patterns("Contents/Info.plist"))
|
||||
|
||||
# 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 != "":
|
||||
|
|
@ -63,6 +75,7 @@ def generate_bundle(target, source, env):
|
|||
sign_command += [Dir("#misc/dist/macos").abspath + "/editor.entitlements"]
|
||||
sign_command += [app_dir]
|
||||
subprocess.run(sign_command)
|
||||
|
||||
else:
|
||||
# Template bundle.
|
||||
app_prefix = "godot." + env["platform"]
|
||||
|
|
@ -120,7 +133,6 @@ files = [
|
|||
"native_menu_macos.mm",
|
||||
"dir_access_macos.mm",
|
||||
"tts_macos.mm",
|
||||
"joypad_macos.mm",
|
||||
"rendering_context_driver_vulkan_macos.mm",
|
||||
"gl_manager_macos_angle.mm",
|
||||
"gl_manager_macos_legacy.mm",
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ public:
|
|||
void initialize();
|
||||
|
||||
void disable();
|
||||
bool is_disabled() const { return disabled; };
|
||||
bool is_disabled() const { return disabled; }
|
||||
|
||||
CrashHandler();
|
||||
~CrashHandler();
|
||||
|
|
|
|||
|
|
@ -149,6 +149,7 @@ static void handle_crash(int sig) {
|
|||
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);
|
||||
|
|
|
|||
|
|
@ -2,12 +2,16 @@ import os
|
|||
import sys
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from methods import detect_darwin_sdk_path, get_compiler_version, is_vanilla_clang, print_error
|
||||
from platform_methods import detect_arch, detect_mvk
|
||||
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"
|
||||
|
|
@ -56,27 +60,15 @@ def get_flags():
|
|||
return {
|
||||
"arch": detect_arch(),
|
||||
"use_volk": False,
|
||||
"supported": ["mono"],
|
||||
"metal": True,
|
||||
"supported": ["metal", "mono"],
|
||||
}
|
||||
|
||||
|
||||
def configure(env: "SConsEnvironment"):
|
||||
# Validate arch.
|
||||
supported_arches = ["x86_64", "arm64"]
|
||||
if env["arch"] not in supported_arches:
|
||||
print_error(
|
||||
'Unsupported CPU architecture "%s" for macOS. Supported architectures are: %s.'
|
||||
% (env["arch"], ", ".join(supported_arches))
|
||||
)
|
||||
sys.exit(255)
|
||||
|
||||
## Build type
|
||||
|
||||
if env["target"] == "template_release":
|
||||
if env["arch"] != "arm64":
|
||||
env.Prepend(CCFLAGS=["-msse2"])
|
||||
elif env.dev_build:
|
||||
env.Prepend(LINKFLAGS=["-Xlinker", "-no_deduplicate"])
|
||||
validate_arch(env["arch"], get_name(), supported_arches)
|
||||
|
||||
## Compiler configuration
|
||||
|
||||
|
|
@ -97,17 +89,22 @@ def configure(env: "SConsEnvironment"):
|
|||
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"]
|
||||
vanilla = is_vanilla_clang(env)
|
||||
|
||||
# Workaround for Xcode 15 linker bug.
|
||||
if not vanilla and cc_version_major == 1500 and cc_version_minor == 0:
|
||||
if is_apple_clang(env) and cc_version_major == 1500 and cc_version_minor == 0:
|
||||
env.Prepend(LINKFLAGS=["-ld_classic"])
|
||||
|
||||
env.Append(CCFLAGS=["-fobjc-arc"])
|
||||
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":
|
||||
|
|
@ -119,8 +116,8 @@ def configure(env: "SConsEnvironment"):
|
|||
env["RANLIB"] = mpprefix + "/libexec/llvm-" + mpclangver + "/bin/llvm-ranlib"
|
||||
env["AS"] = mpprefix + "/libexec/llvm-" + mpclangver + "/bin/llvm-as"
|
||||
else:
|
||||
env["CC"] = "clang"
|
||||
env["CXX"] = "clang++"
|
||||
env["CC"] = ccache_path + "clang"
|
||||
env["CXX"] = ccache_path + "clang++"
|
||||
|
||||
detect_darwin_sdk_path("macos", env)
|
||||
env.Append(CCFLAGS=["-isysroot", "$MACOS_SDK_PATH"])
|
||||
|
|
@ -133,15 +130,8 @@ def configure(env: "SConsEnvironment"):
|
|||
else:
|
||||
basecmd = root + "/target/bin/x86_64-apple-" + env["osxcross_sdk"] + "-"
|
||||
|
||||
ccache_path = os.environ.get("CCACHE")
|
||||
if ccache_path is None:
|
||||
env["CC"] = basecmd + "cc"
|
||||
env["CXX"] = basecmd + "c++"
|
||||
else:
|
||||
# there aren't any ccache wrappers available for macOS cross-compile,
|
||||
# to enable caching we need to prepend the path to the ccache binary
|
||||
env["CC"] = ccache_path + " " + basecmd + "cc"
|
||||
env["CXX"] = ccache_path + " " + basecmd + "c++"
|
||||
env["CC"] = ccache_path + basecmd + "cc"
|
||||
env["CXX"] = ccache_path + basecmd + "c++"
|
||||
env["AR"] = basecmd + "ar"
|
||||
env["RANLIB"] = basecmd + "ranlib"
|
||||
env["AS"] = basecmd + "as"
|
||||
|
|
@ -182,6 +172,10 @@ def configure(env: "SConsEnvironment"):
|
|||
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"])
|
||||
|
|
@ -223,6 +217,8 @@ def configure(env: "SConsEnvironment"):
|
|||
"QuartzCore",
|
||||
"-framework",
|
||||
"Security",
|
||||
"-framework",
|
||||
"UniformTypeIdentifiers",
|
||||
]
|
||||
)
|
||||
env.Append(LIBS=["pthread", "z"])
|
||||
|
|
@ -239,9 +235,23 @@ def configure(env: "SConsEnvironment"):
|
|||
|
||||
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
|
||||
|
||||
extra_frameworks = set()
|
||||
|
||||
if env["metal"]:
|
||||
env.AppendUnique(CPPDEFINES=["METAL_ENABLED", "RD_ENABLED"])
|
||||
extra_frameworks.add("Metal")
|
||||
extra_frameworks.add("MetalKit")
|
||||
extra_frameworks.add("MetalFX")
|
||||
env.Prepend(CPPPATH=["#thirdparty/spirv-cross"])
|
||||
|
||||
if env["vulkan"]:
|
||||
env.Append(CPPDEFINES=["VULKAN_ENABLED", "RD_ENABLED"])
|
||||
env.Append(LINKFLAGS=["-framework", "Metal", "-framework", "IOSurface"])
|
||||
env.AppendUnique(CPPDEFINES=["VULKAN_ENABLED", "RD_ENABLED"])
|
||||
extra_frameworks.add("Metal")
|
||||
extra_frameworks.add("IOSurface")
|
||||
if not env["use_volk"]:
|
||||
env.Append(LINKFLAGS=["-lMoltenVK"])
|
||||
|
||||
|
|
@ -260,3 +270,7 @@ def configure(env: "SConsEnvironment"):
|
|||
"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)
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@ protected:
|
|||
|
||||
virtual bool is_hidden(const String &p_name) override;
|
||||
virtual bool is_case_sensitive(const String &p_path) const override;
|
||||
|
||||
virtual bool is_bundle(const String &p_file) const override;
|
||||
};
|
||||
|
||||
#endif // UNIX ENABLED
|
||||
|
|
|
|||
|
|
@ -96,4 +96,14 @@ bool DirAccessMacOS::is_case_sensitive(const String &p_path) const {
|
|||
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
|
||||
|
|
|
|||
|
|
@ -47,6 +47,9 @@
|
|||
#if defined(VULKAN_ENABLED)
|
||||
#include "rendering_context_driver_vulkan_macos.h"
|
||||
#endif // VULKAN_ENABLED
|
||||
#if defined(METAL_ENABLED)
|
||||
#include "drivers/metal/rendering_context_driver_metal.h"
|
||||
#endif
|
||||
#endif // RD_ENABLED
|
||||
|
||||
#define BitMap _QDBitMap // Suppress deprecated QuickDraw definition.
|
||||
|
|
@ -89,6 +92,7 @@ public:
|
|||
Vector<Vector2> mpath;
|
||||
|
||||
Point2i mouse_pos;
|
||||
WindowResizeEdge edge = WINDOW_EDGE_MAX;
|
||||
|
||||
Size2i min_size;
|
||||
Size2i max_size;
|
||||
|
|
@ -126,6 +130,7 @@ public:
|
|||
bool focused = false;
|
||||
bool is_visible = true;
|
||||
bool extend_to_title = false;
|
||||
bool hide_from_capture = false;
|
||||
|
||||
Rect2i parent_safe_rect;
|
||||
};
|
||||
|
|
@ -164,6 +169,10 @@ private:
|
|||
|
||||
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;
|
||||
|
|
@ -172,16 +181,16 @@ private:
|
|||
String name;
|
||||
String code;
|
||||
};
|
||||
Vector<LayoutInfo> kbd_layouts;
|
||||
int current_layout = 0;
|
||||
bool keyboard_layout_dirty = true;
|
||||
mutable Vector<LayoutInfo> kbd_layouts;
|
||||
mutable int current_layout = 0;
|
||||
mutable bool keyboard_layout_dirty = true;
|
||||
|
||||
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;
|
||||
Point2i origin;
|
||||
bool displays_arrangement_dirty = true;
|
||||
mutable Point2i origin;
|
||||
mutable bool displays_arrangement_dirty = true;
|
||||
bool is_resizing = false;
|
||||
|
||||
CursorShape cursor_shape = CURSOR_ARROW;
|
||||
|
|
@ -212,9 +221,9 @@ private:
|
|||
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);
|
||||
void _update_window_style(WindowData p_wd, WindowID p_window);
|
||||
|
||||
void _update_displays_arrangement();
|
||||
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);
|
||||
|
||||
|
|
@ -222,7 +231,7 @@ private:
|
|||
void _dispatch_input_event(const Ref<InputEvent> &p_event);
|
||||
void _push_input(const Ref<InputEvent> &p_event);
|
||||
void _process_key_events();
|
||||
void _update_keyboard_layouts();
|
||||
void _update_keyboard_layouts() const;
|
||||
static void _keyboard_layout_changed(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef user_info);
|
||||
|
||||
static NSCursor *_cursor_from_selector(SEL p_selector, SEL p_fallback = nil);
|
||||
|
|
@ -262,6 +271,8 @@ public:
|
|||
void mouse_exit_window(WindowID p_window);
|
||||
void update_presentation_mode();
|
||||
|
||||
bool is_always_on_top_recursive(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);
|
||||
|
|
@ -295,8 +306,14 @@ public:
|
|||
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) 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) 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;
|
||||
|
|
@ -321,6 +338,7 @@ public:
|
|||
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;
|
||||
|
||||
|
|
@ -399,6 +417,9 @@ public:
|
|||
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;
|
||||
|
||||
|
|
@ -419,6 +440,7 @@ public:
|
|||
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 void process_events() override;
|
||||
virtual void force_process_and_drop_events() override;
|
||||
|
|
@ -439,12 +461,12 @@ public:
|
|||
|
||||
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, Error &r_error);
|
||||
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, Error &r_error);
|
||||
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();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@
|
|||
#include "main/main.h"
|
||||
#include "scene/resources/image_texture.h"
|
||||
|
||||
#include <AppKit/AppKit.h>
|
||||
|
||||
#if defined(GLES3_ENABLED)
|
||||
#include "drivers/gles3/rasterizer_gles3.h"
|
||||
#endif
|
||||
|
|
@ -138,12 +140,20 @@ DisplayServerMacOS::WindowID DisplayServerMacOS::_create_window(WindowMode p_mod
|
|||
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_V_MSG(err != OK, INVALID_WINDOW_ID, vformat("Can't create a %s context", rendering_driver));
|
||||
|
|
@ -203,7 +213,7 @@ DisplayServerMacOS::WindowID DisplayServerMacOS::_create_window(WindowMode p_mod
|
|||
return id;
|
||||
}
|
||||
|
||||
void DisplayServerMacOS::_update_window_style(WindowData p_wd) {
|
||||
void DisplayServerMacOS::_update_window_style(WindowData p_wd, WindowID p_window) {
|
||||
bool borderless_full = false;
|
||||
|
||||
if (p_wd.borderless) {
|
||||
|
|
@ -223,7 +233,7 @@ void DisplayServerMacOS::_update_window_style(WindowData p_wd) {
|
|||
[(NSWindow *)p_wd.window_object setHidesOnDeactivate:YES];
|
||||
} else {
|
||||
// Reset these when our window is not a borderless window that covers up the screen.
|
||||
if (p_wd.on_top && !p_wd.fullscreen) {
|
||||
if (is_always_on_top_recursive(p_window) && !p_wd.fullscreen) {
|
||||
[(NSWindow *)p_wd.window_object setLevel:NSFloatingWindowLevel];
|
||||
} else {
|
||||
[(NSWindow *)p_wd.window_object setLevel:NSNormalWindowLevel];
|
||||
|
|
@ -232,6 +242,21 @@ void DisplayServerMacOS::_update_window_style(WindowData p_wd) {
|
|||
}
|
||||
}
|
||||
|
||||
bool DisplayServerMacOS::is_always_on_top_recursive(WindowID p_window) const {
|
||||
ERR_FAIL_COND_V(!windows.has(p_window), false);
|
||||
|
||||
const WindowData &wd = windows[p_window];
|
||||
if (wd.on_top) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (wd.transient_parent != INVALID_WINDOW_ID) {
|
||||
return is_always_on_top_recursive(wd.transient_parent);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void DisplayServerMacOS::set_window_per_pixel_transparency_enabled(bool p_enabled, WindowID p_window) {
|
||||
ERR_FAIL_COND(!windows.has(p_window));
|
||||
WindowData &wd = windows[p_window];
|
||||
|
|
@ -275,7 +300,7 @@ void DisplayServerMacOS::set_window_per_pixel_transparency_enabled(bool p_enable
|
|||
}
|
||||
}
|
||||
|
||||
void DisplayServerMacOS::_update_displays_arrangement() {
|
||||
void DisplayServerMacOS::_update_displays_arrangement() const {
|
||||
origin = Point2i();
|
||||
|
||||
for (int i = 0; i < get_screen_count(); i++) {
|
||||
|
|
@ -301,7 +326,7 @@ Point2i DisplayServerMacOS::_get_screens_origin() const {
|
|||
// to convert between macOS native screen coordinates and the ones expected by Godot.
|
||||
|
||||
if (displays_arrangement_dirty) {
|
||||
const_cast<DisplayServerMacOS *>(this)->_update_displays_arrangement();
|
||||
_update_displays_arrangement();
|
||||
}
|
||||
|
||||
return origin;
|
||||
|
|
@ -386,13 +411,17 @@ void DisplayServerMacOS::_dispatch_input_event(const Ref<InputEvent> &p_event) {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
// Send to all windows.
|
||||
// Send to all windows. Copy all pending callbacks, since callback can erase window.
|
||||
Vector<Callable> cbs;
|
||||
for (KeyValue<WindowID, WindowData> &E : windows) {
|
||||
Callable callable = E.value.input_event_callback;
|
||||
if (callable.is_valid()) {
|
||||
callable.call(p_event);
|
||||
cbs.push_back(callable);
|
||||
}
|
||||
}
|
||||
for (const Callable &cb : cbs) {
|
||||
cb.call(p_event);
|
||||
}
|
||||
}
|
||||
in_dispatch_input_event = false;
|
||||
}
|
||||
|
|
@ -462,7 +491,7 @@ void DisplayServerMacOS::_process_key_events() {
|
|||
key_event_pos = 0;
|
||||
}
|
||||
|
||||
void DisplayServerMacOS::_update_keyboard_layouts() {
|
||||
void DisplayServerMacOS::_update_keyboard_layouts() const {
|
||||
kbd_layouts.clear();
|
||||
current_layout = 0;
|
||||
|
||||
|
|
@ -708,6 +737,10 @@ void DisplayServerMacOS::window_destroy(WindowID p_window) {
|
|||
}
|
||||
#endif
|
||||
windows.erase(p_window);
|
||||
|
||||
if (last_focused_window == p_window) {
|
||||
last_focused_window = INVALID_WINDOW_ID;
|
||||
}
|
||||
update_presentation_mode();
|
||||
}
|
||||
|
||||
|
|
@ -744,6 +777,8 @@ bool DisplayServerMacOS::has_feature(Feature p_feature) const {
|
|||
case FEATURE_NATIVE_DIALOG:
|
||||
case FEATURE_NATIVE_DIALOG_INPUT:
|
||||
case FEATURE_NATIVE_DIALOG_FILE:
|
||||
case FEATURE_NATIVE_DIALOG_FILE_EXTRA:
|
||||
case FEATURE_NATIVE_DIALOG_FILE_MIME:
|
||||
case FEATURE_IME:
|
||||
case FEATURE_WINDOW_TRANSPARENCY:
|
||||
case FEATURE_HIDPI:
|
||||
|
|
@ -756,6 +791,9 @@ bool DisplayServerMacOS::has_feature(Feature p_feature) const {
|
|||
case FEATURE_SCREEN_CAPTURE:
|
||||
case FEATURE_STATUS_INDICATOR:
|
||||
case FEATURE_NATIVE_HELP:
|
||||
case FEATURE_WINDOW_DRAG:
|
||||
case FEATURE_SCREEN_EXCLUDE_FROM_CAPTURE:
|
||||
case FEATURE_EMOJI_AND_SYMBOL_PICKER:
|
||||
return true;
|
||||
default: {
|
||||
}
|
||||
|
|
@ -1190,6 +1228,10 @@ Error DisplayServerMacOS::_file_dialog_with_options_show(const String &p_title,
|
|||
return OK;
|
||||
}
|
||||
|
||||
void DisplayServerMacOS::beep() const {
|
||||
NSBeep();
|
||||
}
|
||||
|
||||
Error DisplayServerMacOS::dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback) {
|
||||
_THREAD_SAFE_METHOD_
|
||||
|
||||
|
|
@ -1226,10 +1268,14 @@ Error DisplayServerMacOS::dialog_input_text(String p_title, String p_description
|
|||
return OK;
|
||||
}
|
||||
|
||||
void DisplayServerMacOS::mouse_set_mode(MouseMode p_mode) {
|
||||
void DisplayServerMacOS::_mouse_update_mode() {
|
||||
_THREAD_SAFE_METHOD_
|
||||
|
||||
if (p_mode == mouse_mode) {
|
||||
MouseMode wanted_mouse_mode = mouse_mode_override_enabled
|
||||
? mouse_mode_override
|
||||
: mouse_mode_base;
|
||||
|
||||
if (wanted_mouse_mode == mouse_mode) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1239,7 +1285,7 @@ void DisplayServerMacOS::mouse_set_mode(MouseMode p_mode) {
|
|||
}
|
||||
WindowData &wd = windows[window_id];
|
||||
|
||||
bool show_cursor = (p_mode == MOUSE_MODE_VISIBLE || p_mode == MOUSE_MODE_CONFINED);
|
||||
bool show_cursor = (wanted_mouse_mode == MOUSE_MODE_VISIBLE || wanted_mouse_mode == MOUSE_MODE_CONFINED);
|
||||
bool previously_shown = (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED);
|
||||
|
||||
if (show_cursor && !previously_shown) {
|
||||
|
|
@ -1247,7 +1293,7 @@ void DisplayServerMacOS::mouse_set_mode(MouseMode p_mode) {
|
|||
mouse_enter_window(window_id);
|
||||
}
|
||||
|
||||
if (p_mode == MOUSE_MODE_CAPTURED) {
|
||||
if (wanted_mouse_mode == MOUSE_MODE_CAPTURED) {
|
||||
// Apple Docs state that the display parameter is not used.
|
||||
// "This parameter is not used. By default, you may pass kCGDirectMainDisplay."
|
||||
// https://developer.apple.com/library/mac/documentation/graphicsimaging/reference/Quartz_Services_Ref/Reference/reference.html
|
||||
|
|
@ -1261,17 +1307,17 @@ void DisplayServerMacOS::mouse_set_mode(MouseMode p_mode) {
|
|||
NSPoint pointOnScreen = [[wd.window_view window] convertRectToScreen:pointInWindowRect].origin;
|
||||
CGPoint lMouseWarpPos = { pointOnScreen.x, CGDisplayBounds(CGMainDisplayID()).size.height - pointOnScreen.y };
|
||||
CGWarpMouseCursorPosition(lMouseWarpPos);
|
||||
} else if (p_mode == MOUSE_MODE_HIDDEN) {
|
||||
} else if (wanted_mouse_mode == MOUSE_MODE_HIDDEN) {
|
||||
if (previously_shown) {
|
||||
CGDisplayHideCursor(kCGDirectMainDisplay);
|
||||
}
|
||||
[wd.window_object setMovable:YES];
|
||||
CGAssociateMouseAndMouseCursorPosition(true);
|
||||
} else if (p_mode == MOUSE_MODE_CONFINED) {
|
||||
} else if (wanted_mouse_mode == MOUSE_MODE_CONFINED) {
|
||||
CGDisplayShowCursor(kCGDirectMainDisplay);
|
||||
[wd.window_object setMovable:NO];
|
||||
CGAssociateMouseAndMouseCursorPosition(false);
|
||||
} else if (p_mode == MOUSE_MODE_CONFINED_HIDDEN) {
|
||||
} else if (wanted_mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) {
|
||||
if (previously_shown) {
|
||||
CGDisplayHideCursor(kCGDirectMainDisplay);
|
||||
}
|
||||
|
|
@ -1286,17 +1332,51 @@ void DisplayServerMacOS::mouse_set_mode(MouseMode p_mode) {
|
|||
last_warp = [[NSProcessInfo processInfo] systemUptime];
|
||||
ignore_warp = true;
|
||||
warp_events.clear();
|
||||
mouse_mode = p_mode;
|
||||
mouse_mode = wanted_mouse_mode;
|
||||
|
||||
if (show_cursor) {
|
||||
cursor_update_shape();
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayServerMacOS::mouse_set_mode(MouseMode p_mode) {
|
||||
ERR_FAIL_INDEX(p_mode, MouseMode::MOUSE_MODE_MAX);
|
||||
if (p_mode == mouse_mode_base) {
|
||||
return;
|
||||
}
|
||||
mouse_mode_base = p_mode;
|
||||
_mouse_update_mode();
|
||||
}
|
||||
|
||||
DisplayServer::MouseMode DisplayServerMacOS::mouse_get_mode() const {
|
||||
return mouse_mode;
|
||||
}
|
||||
|
||||
void DisplayServerMacOS::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 DisplayServerMacOS::mouse_get_mode_override() const {
|
||||
return mouse_mode_override;
|
||||
}
|
||||
|
||||
void DisplayServerMacOS::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 DisplayServerMacOS::mouse_is_mode_override_enabled() const {
|
||||
return mouse_mode_override_enabled;
|
||||
}
|
||||
|
||||
bool DisplayServerMacOS::update_mouse_wrap(WindowData &p_wd, NSPoint &r_delta, NSPoint &r_mpos, NSTimeInterval p_timestamp) {
|
||||
_THREAD_SAFE_METHOD_
|
||||
|
||||
|
|
@ -1595,35 +1675,41 @@ Rect2i DisplayServerMacOS::screen_get_usable_rect(int p_screen) const {
|
|||
}
|
||||
|
||||
Color DisplayServerMacOS::screen_get_pixel(const Point2i &p_position) const {
|
||||
Point2i position = p_position;
|
||||
// macOS native y-coordinate relative to _get_screens_origin() is negative,
|
||||
// Godot passes a positive value.
|
||||
position.y *= -1;
|
||||
position += _get_screens_origin();
|
||||
HashSet<CGWindowID> exclude_windows;
|
||||
for (HashMap<WindowID, WindowData>::ConstIterator E = windows.begin(); E; ++E) {
|
||||
if (E->value.hide_from_capture) {
|
||||
exclude_windows.insert([E->value.window_object windowNumber]);
|
||||
}
|
||||
}
|
||||
|
||||
CFArrayRef on_screen_windows = CGWindowListCreate(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
|
||||
CFMutableArrayRef capture_windows = CFArrayCreateMutableCopy(nullptr, 0, on_screen_windows);
|
||||
for (long i = CFArrayGetCount(on_screen_windows) - 1; i >= 0; i--) {
|
||||
CGWindowID window = (CGWindowID)(uintptr_t)CFArrayGetValueAtIndex(capture_windows, i);
|
||||
if (exclude_windows.has(window)) {
|
||||
CFArrayRemoveValueAtIndex(capture_windows, i);
|
||||
}
|
||||
}
|
||||
|
||||
Point2i position = p_position - Vector2i(1, 1);
|
||||
position -= screen_get_position(0); // Note: coordinates where the screen origin is in the upper-left corner of the main display and y-axis values increase downward.
|
||||
position /= screen_get_max_scale();
|
||||
|
||||
Color color;
|
||||
for (NSScreen *screen in [NSScreen screens]) {
|
||||
NSRect frame = [screen frame];
|
||||
if (NSMouseInRect(NSMakePoint(position.x, position.y), frame, NO)) {
|
||||
NSDictionary *screenDescription = [screen deviceDescription];
|
||||
CGDirectDisplayID display_id = [[screenDescription objectForKey:@"NSScreenNumber"] unsignedIntValue];
|
||||
CGImageRef image = CGDisplayCreateImageForRect(display_id, CGRectMake(position.x - frame.origin.x, frame.size.height - (position.y - frame.origin.y), 1, 1));
|
||||
if (image) {
|
||||
CGColorSpaceRef color_space = CGColorSpaceCreateDeviceRGB();
|
||||
if (color_space) {
|
||||
uint8_t img_data[4];
|
||||
CGContextRef context = CGBitmapContextCreate(img_data, 1, 1, 8, 4, color_space, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
|
||||
if (context) {
|
||||
CGContextDrawImage(context, CGRectMake(0, 0, 1, 1), image);
|
||||
color = Color(img_data[0] / 255.0f, img_data[1] / 255.0f, img_data[2] / 255.0f, img_data[3] / 255.0f);
|
||||
CGContextRelease(context);
|
||||
}
|
||||
CGColorSpaceRelease(color_space);
|
||||
}
|
||||
CGImageRelease(image);
|
||||
CGImageRef image = CGWindowListCreateImageFromArray(CGRectMake(position.x, position.y, 1, 1), capture_windows, kCGWindowListOptionAll);
|
||||
if (image) {
|
||||
CGColorSpaceRef color_space = CGColorSpaceCreateDeviceRGB();
|
||||
if (color_space) {
|
||||
uint8_t img_data[4];
|
||||
CGContextRef context = CGBitmapContextCreate(img_data, 1, 1, 8, 4, color_space, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
|
||||
if (context) {
|
||||
CGContextDrawImage(context, CGRectMake(0, 0, 1, 1), image);
|
||||
color = Color(img_data[0] / 255.0f, img_data[1] / 255.0f, img_data[2] / 255.0f, img_data[3] / 255.0f);
|
||||
CGContextRelease(context);
|
||||
}
|
||||
CGColorSpaceRelease(color_space);
|
||||
}
|
||||
CGImageRelease(image);
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
|
@ -1631,43 +1717,98 @@ Color DisplayServerMacOS::screen_get_pixel(const Point2i &p_position) const {
|
|||
Ref<Image> DisplayServerMacOS::screen_get_image(int p_screen) const {
|
||||
ERR_FAIL_INDEX_V(p_screen, get_screen_count(), Ref<Image>());
|
||||
|
||||
switch (p_screen) {
|
||||
case SCREEN_PRIMARY: {
|
||||
p_screen = get_primary_screen();
|
||||
} break;
|
||||
case SCREEN_OF_MAIN_WINDOW: {
|
||||
p_screen = window_get_current_screen(MAIN_WINDOW_ID);
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
HashSet<CGWindowID> exclude_windows;
|
||||
for (HashMap<WindowID, WindowData>::ConstIterator E = windows.begin(); E; ++E) {
|
||||
if (E->value.hide_from_capture) {
|
||||
exclude_windows.insert([E->value.window_object windowNumber]);
|
||||
}
|
||||
}
|
||||
|
||||
Ref<Image> img;
|
||||
NSArray *screenArray = [NSScreen screens];
|
||||
if ((NSUInteger)p_screen < [screenArray count]) {
|
||||
NSRect nsrect = [[screenArray objectAtIndex:p_screen] frame];
|
||||
NSDictionary *screenDescription = [[screenArray objectAtIndex:p_screen] deviceDescription];
|
||||
CGDirectDisplayID display_id = [[screenDescription objectForKey:@"NSScreenNumber"] unsignedIntValue];
|
||||
CGImageRef image = CGDisplayCreateImageForRect(display_id, CGRectMake(0, 0, nsrect.size.width, nsrect.size.height));
|
||||
if (image) {
|
||||
CGColorSpaceRef color_space = CGColorSpaceCreateDeviceRGB();
|
||||
if (color_space) {
|
||||
NSUInteger width = CGImageGetWidth(image);
|
||||
NSUInteger height = CGImageGetHeight(image);
|
||||
|
||||
Vector<uint8_t> img_data;
|
||||
img_data.resize(height * width * 4);
|
||||
CGContextRef context = CGBitmapContextCreate(img_data.ptrw(), width, height, 8, 4 * width, color_space, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
|
||||
if (context) {
|
||||
CGContextDrawImage(context, CGRectMake(0, 0, width, height), image);
|
||||
img = Image::create_from_data(width, height, false, Image::FORMAT_RGBA8, img_data);
|
||||
CGContextRelease(context);
|
||||
}
|
||||
CGColorSpaceRelease(color_space);
|
||||
}
|
||||
CGImageRelease(image);
|
||||
CFArrayRef on_screen_windows = CGWindowListCreate(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
|
||||
CFMutableArrayRef capture_windows = CFArrayCreateMutableCopy(nullptr, 0, on_screen_windows);
|
||||
for (long i = CFArrayGetCount(on_screen_windows) - 1; i >= 0; i--) {
|
||||
CGWindowID window = (CGWindowID)(uintptr_t)CFArrayGetValueAtIndex(capture_windows, i);
|
||||
if (exclude_windows.has(window)) {
|
||||
CFArrayRemoveValueAtIndex(capture_windows, i);
|
||||
}
|
||||
}
|
||||
|
||||
Point2i position = screen_get_position(p_screen);
|
||||
position -= screen_get_position(0); // Note: coordinates where the screen origin is in the upper-left corner of the main display and y-axis values increase downward.
|
||||
position /= screen_get_max_scale();
|
||||
|
||||
Size2i size = screen_get_size(p_screen);
|
||||
size /= screen_get_max_scale();
|
||||
|
||||
Ref<Image> img;
|
||||
CGImageRef image = CGWindowListCreateImageFromArray(CGRectMake(position.x, position.y, size.width, size.height), capture_windows, kCGWindowListOptionAll);
|
||||
if (image) {
|
||||
CGColorSpaceRef color_space = CGColorSpaceCreateDeviceRGB();
|
||||
if (color_space) {
|
||||
NSUInteger width = CGImageGetWidth(image);
|
||||
NSUInteger height = CGImageGetHeight(image);
|
||||
|
||||
Vector<uint8_t> img_data;
|
||||
img_data.resize(height * width * 4);
|
||||
CGContextRef context = CGBitmapContextCreate(img_data.ptrw(), width, height, 8, 4 * width, color_space, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
|
||||
if (context) {
|
||||
CGContextDrawImage(context, CGRectMake(0, 0, width, height), image);
|
||||
img = Image::create_from_data(width, height, false, Image::FORMAT_RGBA8, img_data);
|
||||
CGContextRelease(context);
|
||||
img->resize(screen_get_size(p_screen).x, screen_get_size(p_screen).y, Image::INTERPOLATE_NEAREST);
|
||||
}
|
||||
CGColorSpaceRelease(color_space);
|
||||
}
|
||||
CGImageRelease(image);
|
||||
}
|
||||
return img;
|
||||
}
|
||||
|
||||
Ref<Image> DisplayServerMacOS::screen_get_image_rect(const Rect2i &p_rect) const {
|
||||
HashSet<CGWindowID> exclude_windows;
|
||||
for (HashMap<WindowID, WindowData>::ConstIterator E = windows.begin(); E; ++E) {
|
||||
if (E->value.hide_from_capture) {
|
||||
exclude_windows.insert([E->value.window_object windowNumber]);
|
||||
}
|
||||
}
|
||||
|
||||
CFArrayRef on_screen_windows = CGWindowListCreate(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
|
||||
CFMutableArrayRef capture_windows = CFArrayCreateMutableCopy(nullptr, 0, on_screen_windows);
|
||||
for (long i = CFArrayGetCount(on_screen_windows) - 1; i >= 0; i--) {
|
||||
CGWindowID window = (CGWindowID)(uintptr_t)CFArrayGetValueAtIndex(capture_windows, i);
|
||||
if (exclude_windows.has(window)) {
|
||||
CFArrayRemoveValueAtIndex(capture_windows, i);
|
||||
}
|
||||
}
|
||||
|
||||
Point2i position = p_rect.position;
|
||||
position -= screen_get_position(0); // Note: coordinates where the screen origin is in the upper-left corner of the main display and y-axis values increase downward.
|
||||
position /= screen_get_max_scale();
|
||||
|
||||
Size2i size = p_rect.size;
|
||||
size /= screen_get_max_scale();
|
||||
|
||||
Ref<Image> img;
|
||||
CGImageRef image = CGWindowListCreateImageFromArray(CGRectMake(position.x, position.y, size.width, size.height), capture_windows, kCGWindowListOptionAll);
|
||||
if (image) {
|
||||
CGColorSpaceRef color_space = CGColorSpaceCreateDeviceRGB();
|
||||
if (color_space) {
|
||||
NSUInteger width = CGImageGetWidth(image);
|
||||
NSUInteger height = CGImageGetHeight(image);
|
||||
|
||||
Vector<uint8_t> img_data;
|
||||
img_data.resize_zeroed(height * width * 4);
|
||||
CGContextRef context = CGBitmapContextCreate(img_data.ptrw(), width, height, 8, 4 * width, color_space, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
|
||||
if (context) {
|
||||
CGContextDrawImage(context, CGRectMake(0, 0, width, height), image);
|
||||
img = Image::create_from_data(width, height, false, Image::FORMAT_RGBA8, img_data);
|
||||
CGContextRelease(context);
|
||||
img->resize(p_rect.size.x, p_rect.size.y, Image::INTERPOLATE_NEAREST);
|
||||
}
|
||||
CGColorSpaceRelease(color_space);
|
||||
}
|
||||
CGImageRelease(image);
|
||||
}
|
||||
return img;
|
||||
}
|
||||
|
||||
|
|
@ -1885,6 +2026,12 @@ void DisplayServerMacOS::window_set_current_screen(int p_screen, WindowID p_wind
|
|||
was_fullscreen = true;
|
||||
}
|
||||
|
||||
bool was_maximized = false;
|
||||
if (!was_fullscreen && NSEqualRects([wd.window_object frame], [[wd.window_object screen] visibleFrame])) {
|
||||
[wd.window_object zoom:nil];
|
||||
was_maximized = true;
|
||||
}
|
||||
|
||||
Rect2i srect = screen_get_usable_rect(p_screen);
|
||||
Point2i wpos = window_get_position(p_window) - screen_get_position(window_get_current_screen(p_window));
|
||||
Size2i wsize = window_get_size(p_window);
|
||||
|
|
@ -1893,6 +2040,10 @@ void DisplayServerMacOS::window_set_current_screen(int p_screen, WindowID p_wind
|
|||
wpos = wpos.clamp(srect.position, srect.position + srect.size - wsize / 3);
|
||||
window_set_position(wpos, p_window);
|
||||
|
||||
if (was_maximized) {
|
||||
[wd.window_object zoom:nil];
|
||||
}
|
||||
|
||||
if (was_fullscreen) {
|
||||
// Re-enter fullscreen mode.
|
||||
[wd.window_object toggleFullScreen:nil];
|
||||
|
|
@ -2025,7 +2176,7 @@ void DisplayServerMacOS::window_set_position(const Point2i &p_position, WindowID
|
|||
|
||||
[wd.window_object setFrameTopLeftPoint:NSMakePoint(position.x - offset.x, position.y - offset.y)];
|
||||
|
||||
_update_window_style(wd);
|
||||
_update_window_style(wd, p_window);
|
||||
update_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream]);
|
||||
}
|
||||
|
||||
|
|
@ -2163,7 +2314,7 @@ void DisplayServerMacOS::window_set_size(const Size2i p_size, WindowID p_window)
|
|||
|
||||
[wd.window_object setFrame:new_frame display:YES];
|
||||
|
||||
_update_window_style(wd);
|
||||
_update_window_style(wd, p_window);
|
||||
}
|
||||
|
||||
Size2i DisplayServerMacOS::window_get_size(WindowID p_window) const {
|
||||
|
|
@ -2323,6 +2474,26 @@ bool DisplayServerMacOS::window_minimize_on_title_dbl_click() const {
|
|||
return false;
|
||||
}
|
||||
|
||||
void DisplayServerMacOS::window_start_drag(WindowID p_window) {
|
||||
_THREAD_SAFE_METHOD_
|
||||
|
||||
ERR_FAIL_COND(!windows.has(p_window));
|
||||
WindowData &wd = windows[p_window];
|
||||
|
||||
NSEvent *event = [NSEvent mouseEventWithType:NSEventTypeLeftMouseDown location:((NSWindow *)wd.window_object).mouseLocationOutsideOfEventStream modifierFlags:0 timestamp:[[NSProcessInfo processInfo] systemUptime] windowNumber:((NSWindow *)wd.window_object).windowNumber context:nil eventNumber:0 clickCount:1 pressure:1.0f];
|
||||
[wd.window_object performWindowDragWithEvent:event];
|
||||
}
|
||||
|
||||
void DisplayServerMacOS::window_start_resize(WindowResizeEdge p_edge, WindowID p_window) {
|
||||
_THREAD_SAFE_METHOD_
|
||||
|
||||
ERR_FAIL_INDEX(int(p_edge), WINDOW_EDGE_MAX);
|
||||
ERR_FAIL_COND(!windows.has(p_window));
|
||||
WindowData &wd = windows[p_window];
|
||||
|
||||
wd.edge = p_edge;
|
||||
}
|
||||
|
||||
void DisplayServerMacOS::window_set_window_buttons_offset(const Vector2i &p_offset, WindowID p_window) {
|
||||
_THREAD_SAFE_METHOD_
|
||||
|
||||
|
|
@ -2332,7 +2503,7 @@ void DisplayServerMacOS::window_set_window_buttons_offset(const Vector2i &p_offs
|
|||
wd.wb_offset = p_offset / scale;
|
||||
wd.wb_offset = wd.wb_offset.maxi(12);
|
||||
if (wd.window_button_view) {
|
||||
[wd.window_button_view setOffset:NSMakePoint(wd.wb_offset.x, wd.wb_offset.y)];
|
||||
[(GodotButtonView *)wd.window_button_view setOffset:NSMakePoint(wd.wb_offset.x, wd.wb_offset.y)];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2450,7 +2621,7 @@ void DisplayServerMacOS::window_set_flag(WindowFlags p_flag, bool p_enabled, Win
|
|||
[wd.window_object setFrame:NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width + 1, frameRect.size.height) display:NO];
|
||||
[wd.window_object setFrame:frameRect display:NO];
|
||||
}
|
||||
_update_window_style(wd);
|
||||
_update_window_style(wd, p_window);
|
||||
if (was_visible || [wd.window_object isVisible]) {
|
||||
if ([wd.window_object isMiniaturized]) {
|
||||
return;
|
||||
|
|
@ -2494,6 +2665,14 @@ void DisplayServerMacOS::window_set_flag(WindowFlags p_flag, bool p_enabled, Win
|
|||
NSWindow *w = wd.window_object;
|
||||
w.excludedFromWindowsMenu = wd.is_popup || wd.no_focus;
|
||||
} break;
|
||||
case WINDOW_FLAG_EXCLUDE_FROM_CAPTURE: {
|
||||
if (p_enabled) {
|
||||
[wd.window_object setSharingType:NSWindowSharingNone];
|
||||
} else {
|
||||
[wd.window_object setSharingType:NSWindowSharingReadWrite];
|
||||
}
|
||||
wd.hide_from_capture = p_enabled;
|
||||
} break;
|
||||
case WINDOW_FLAG_MOUSE_PASSTHROUGH: {
|
||||
wd.mpass = p_enabled;
|
||||
} break;
|
||||
|
|
@ -2527,11 +2706,7 @@ bool DisplayServerMacOS::window_get_flag(WindowFlags p_flag, WindowID p_window)
|
|||
return [wd.window_object styleMask] == NSWindowStyleMaskBorderless;
|
||||
} break;
|
||||
case WINDOW_FLAG_ALWAYS_ON_TOP: {
|
||||
if (wd.fullscreen) {
|
||||
return wd.on_top;
|
||||
} else {
|
||||
return [(NSWindow *)wd.window_object level] == NSFloatingWindowLevel;
|
||||
}
|
||||
return wd.on_top;
|
||||
} break;
|
||||
case WINDOW_FLAG_TRANSPARENT: {
|
||||
return wd.layered_window;
|
||||
|
|
@ -2539,6 +2714,9 @@ bool DisplayServerMacOS::window_get_flag(WindowFlags p_flag, WindowID p_window)
|
|||
case WINDOW_FLAG_NO_FOCUS: {
|
||||
return wd.no_focus;
|
||||
} break;
|
||||
case WINDOW_FLAG_EXCLUDE_FROM_CAPTURE: {
|
||||
return wd.hide_from_capture;
|
||||
} break;
|
||||
case WINDOW_FLAG_MOUSE_PASSTHROUGH: {
|
||||
return wd.mpass;
|
||||
} break;
|
||||
|
|
@ -2658,6 +2836,18 @@ int64_t DisplayServerMacOS::window_get_native_handle(HandleType p_handle_type, W
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
case EGL_DISPLAY: {
|
||||
if (gl_manager_angle) {
|
||||
return (int64_t)gl_manager_angle->get_display(p_window);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
case EGL_CONFIG: {
|
||||
if (gl_manager_angle) {
|
||||
return (int64_t)gl_manager_angle->get_config(p_window);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
default: {
|
||||
return 0;
|
||||
|
|
@ -2700,7 +2890,7 @@ void DisplayServerMacOS::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_
|
|||
gl_manager_legacy->set_use_vsync(p_vsync_mode != DisplayServer::VSYNC_DISABLED);
|
||||
}
|
||||
#endif
|
||||
#if defined(VULKAN_ENABLED)
|
||||
#if defined(RD_ENABLED)
|
||||
if (rendering_context) {
|
||||
rendering_context->window_set_vsync_mode(p_window, p_vsync_mode);
|
||||
}
|
||||
|
|
@ -2717,7 +2907,7 @@ DisplayServer::VSyncMode DisplayServerMacOS::window_get_vsync_mode(WindowID p_wi
|
|||
return (gl_manager_legacy->is_using_vsync() ? DisplayServer::VSyncMode::VSYNC_ENABLED : DisplayServer::VSyncMode::VSYNC_DISABLED);
|
||||
}
|
||||
#endif
|
||||
#if defined(VULKAN_ENABLED)
|
||||
#if defined(RD_ENABLED)
|
||||
if (rendering_context) {
|
||||
return rendering_context->window_get_vsync_mode(p_window);
|
||||
}
|
||||
|
|
@ -2904,14 +3094,14 @@ bool DisplayServerMacOS::get_swap_cancel_ok() {
|
|||
|
||||
int DisplayServerMacOS::keyboard_get_layout_count() const {
|
||||
if (keyboard_layout_dirty) {
|
||||
const_cast<DisplayServerMacOS *>(this)->_update_keyboard_layouts();
|
||||
_update_keyboard_layouts();
|
||||
}
|
||||
return kbd_layouts.size();
|
||||
}
|
||||
|
||||
void DisplayServerMacOS::keyboard_set_current_layout(int p_index) {
|
||||
if (keyboard_layout_dirty) {
|
||||
const_cast<DisplayServerMacOS *>(this)->_update_keyboard_layouts();
|
||||
_update_keyboard_layouts();
|
||||
}
|
||||
|
||||
ERR_FAIL_INDEX(p_index, kbd_layouts.size());
|
||||
|
|
@ -2941,7 +3131,7 @@ void DisplayServerMacOS::keyboard_set_current_layout(int p_index) {
|
|||
|
||||
int DisplayServerMacOS::keyboard_get_current_layout() const {
|
||||
if (keyboard_layout_dirty) {
|
||||
const_cast<DisplayServerMacOS *>(this)->_update_keyboard_layouts();
|
||||
_update_keyboard_layouts();
|
||||
}
|
||||
|
||||
return current_layout;
|
||||
|
|
@ -2949,7 +3139,7 @@ int DisplayServerMacOS::keyboard_get_current_layout() const {
|
|||
|
||||
String DisplayServerMacOS::keyboard_get_layout_language(int p_index) const {
|
||||
if (keyboard_layout_dirty) {
|
||||
const_cast<DisplayServerMacOS *>(this)->_update_keyboard_layouts();
|
||||
_update_keyboard_layouts();
|
||||
}
|
||||
|
||||
ERR_FAIL_INDEX_V(p_index, kbd_layouts.size(), "");
|
||||
|
|
@ -2958,7 +3148,7 @@ String DisplayServerMacOS::keyboard_get_layout_language(int p_index) const {
|
|||
|
||||
String DisplayServerMacOS::keyboard_get_layout_name(int p_index) const {
|
||||
if (keyboard_layout_dirty) {
|
||||
const_cast<DisplayServerMacOS *>(this)->_update_keyboard_layouts();
|
||||
_update_keyboard_layouts();
|
||||
}
|
||||
|
||||
ERR_FAIL_INDEX_V(p_index, kbd_layouts.size(), "");
|
||||
|
|
@ -2987,6 +3177,10 @@ Key DisplayServerMacOS::keyboard_get_label_from_physical(Key p_keycode) const {
|
|||
return (Key)(KeyMappingMacOS::remap_key(macos_keycode, 0, true) | modifiers);
|
||||
}
|
||||
|
||||
void DisplayServerMacOS::show_emoji_and_symbol_picker() const {
|
||||
[[NSApplication sharedApplication] orderFrontCharacterPalette:nil];
|
||||
}
|
||||
|
||||
void DisplayServerMacOS::process_events() {
|
||||
ERR_FAIL_COND(!Thread::is_main_thread());
|
||||
|
||||
|
|
@ -3268,8 +3462,8 @@ bool DisplayServerMacOS::is_window_transparency_available() const {
|
|||
return OS::get_singleton()->is_layered_allowed();
|
||||
}
|
||||
|
||||
DisplayServer *DisplayServerMacOS::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, Error &r_error) {
|
||||
DisplayServer *ds = memnew(DisplayServerMacOS(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, r_error));
|
||||
DisplayServer *DisplayServerMacOS::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) {
|
||||
DisplayServer *ds = memnew(DisplayServerMacOS(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, p_parent_window, r_error));
|
||||
if (r_error != OK) {
|
||||
if (p_rendering_driver == "vulkan") {
|
||||
String executable_command;
|
||||
|
|
@ -3301,6 +3495,9 @@ Vector<String> DisplayServerMacOS::get_rendering_drivers_func() {
|
|||
#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");
|
||||
drivers.push_back("opengl3_angle");
|
||||
|
|
@ -3460,7 +3657,7 @@ bool DisplayServerMacOS::mouse_process_popups(bool p_close) {
|
|||
return closed;
|
||||
}
|
||||
|
||||
DisplayServerMacOS::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, Error &r_error) {
|
||||
DisplayServerMacOS::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) {
|
||||
KeyMappingMacOS::initialize();
|
||||
|
||||
Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events);
|
||||
|
|
@ -3590,6 +3787,47 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM
|
|||
//TODO - do Vulkan and OpenGL support checks, driver selection and fallback
|
||||
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");
|
||||
// Metal rendering driver not available on Intel.
|
||||
if (fallback_to_vulkan && 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 seem not to support MoltenVK or Metal, switching to OpenGL 3.");
|
||||
rendering_driver = "opengl3";
|
||||
OS::get_singleton()->set_current_rendering_method("gl_compatibility");
|
||||
OS::get_singleton()->set_current_rendering_driver_name(rendering_driver);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
r_error = ERR_CANT_CREATE;
|
||||
ERR_FAIL_MSG("Could not initialize " + rendering_driver);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(GLES3_ENABLED)
|
||||
if (rendering_driver == "opengl3_angle") {
|
||||
gl_manager_angle = memnew(GLManagerANGLE_MacOS);
|
||||
|
|
@ -3598,8 +3836,13 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM
|
|||
gl_manager_angle = nullptr;
|
||||
bool fallback = GLOBAL_GET("rendering/gl_compatibility/fallback_to_native");
|
||||
if (fallback) {
|
||||
WARN_PRINT("Your video card drivers seem not to support the required Metal version, switching to native OpenGL.");
|
||||
#ifdef EGL_STATIC
|
||||
WARN_PRINT("Your video card drivers seem not to support GLES3 / ANGLE, switching to native OpenGL.");
|
||||
#else
|
||||
WARN_PRINT("Your video card drivers seem not to support GLES3 / ANGLE or ANGLE dynamic libraries (libEGL.dylib and libGLESv2.dylib) are missing, switching to native OpenGL.");
|
||||
#endif
|
||||
rendering_driver = "opengl3";
|
||||
OS::get_singleton()->set_current_rendering_driver_name(rendering_driver);
|
||||
} else {
|
||||
r_error = ERR_UNAVAILABLE;
|
||||
ERR_FAIL_MSG("Could not initialize ANGLE OpenGL.");
|
||||
|
|
@ -3617,22 +3860,6 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM
|
|||
}
|
||||
}
|
||||
#endif
|
||||
#if defined(RD_ENABLED)
|
||||
#if defined(VULKAN_ENABLED)
|
||||
if (rendering_driver == "vulkan") {
|
||||
rendering_context = memnew(RenderingContextDriverVulkanMacOS);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (rendering_context) {
|
||||
if (rendering_context->initialize() != OK) {
|
||||
memdelete(rendering_context);
|
||||
rendering_context = nullptr;
|
||||
r_error = ERR_CANT_CREATE;
|
||||
ERR_FAIL_MSG("Could not initialize " + rendering_driver);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Point2i window_position;
|
||||
if (p_position != nullptr) {
|
||||
|
|
|
|||
|
|
@ -38,8 +38,11 @@
|
|||
<member name="application/icon_interpolation" type="int" setter="" getter="">
|
||||
Interpolation method used to resize application icon.
|
||||
</member>
|
||||
<member name="application/min_macos_version" type="String" setter="" getter="">
|
||||
Minimum version of macOS required for this application to run in the [code]major.minor.patch[/code] or [code]major.minor[/code] format, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]).
|
||||
<member 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.
|
||||
|
|
@ -72,6 +75,13 @@
|
|||
<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>
|
||||
|
|
|
|||
|
|
@ -37,8 +37,9 @@ void register_macos_exporter_types() {
|
|||
}
|
||||
|
||||
void register_macos_exporter() {
|
||||
// TODO: Move to editor_settings.cpp
|
||||
#ifndef ANDROID_ENABLED
|
||||
EDITOR_DEF("export/macos/rcodesign", "");
|
||||
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
|
||||
|
|
|
|||
|
|
@ -47,10 +47,7 @@
|
|||
#include "editor/themes/editor_scale.h"
|
||||
#include "scene/resources/image_texture.h"
|
||||
|
||||
#include "modules/modules_enabled.gen.h" // For svg and regex.
|
||||
#ifdef MODULE_SVG_ENABLED
|
||||
#include "modules/svg/image_loader_svg.h"
|
||||
#endif
|
||||
|
||||
void EditorExportPlatformMacOS::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const {
|
||||
r_features->push_back(p_preset->get("binary_format/architecture"));
|
||||
|
|
@ -65,6 +62,11 @@ void EditorExportPlatformMacOS::get_preset_features(const Ref<EditorExportPreset
|
|||
} else {
|
||||
ERR_PRINT("Invalid architecture");
|
||||
}
|
||||
|
||||
if (architecture == "universal") {
|
||||
r_features->push_back("x86_64");
|
||||
r_features->push_back("arm64");
|
||||
}
|
||||
}
|
||||
|
||||
String EditorExportPlatformMacOS::get_export_option_warning(const EditorExportPreset *p_preset, const StringName &p_name) const {
|
||||
|
|
@ -327,7 +329,21 @@ bool EditorExportPlatformMacOS::get_export_option_visibility(const EditorExportP
|
|||
}
|
||||
|
||||
bool advanced_options_enabled = p_preset->are_advanced_options_enabled();
|
||||
if (p_option.begins_with("privacy")) {
|
||||
if (p_option.begins_with("privacy") ||
|
||||
p_option == "codesign/entitlements/additional" ||
|
||||
p_option == "custom_template/debug" ||
|
||||
p_option == "custom_template/release" ||
|
||||
p_option == "application/additional_plist_content" ||
|
||||
p_option == "application/export_angle" ||
|
||||
p_option == "application/icon_interpolation" ||
|
||||
p_option == "application/signature" ||
|
||||
p_option == "display/high_res" ||
|
||||
p_option == "xcode/platform_build" ||
|
||||
p_option == "xcode/sdk_build" ||
|
||||
p_option == "xcode/sdk_name" ||
|
||||
p_option == "xcode/sdk_version" ||
|
||||
p_option == "xcode/xcode_build" ||
|
||||
p_option == "xcode/xcode_version") {
|
||||
return advanced_options_enabled;
|
||||
}
|
||||
}
|
||||
|
|
@ -357,17 +373,13 @@ List<String> EditorExportPlatformMacOS::get_binary_extensions(const Ref<EditorEx
|
|||
list.push_back("dmg");
|
||||
#endif
|
||||
list.push_back("zip");
|
||||
#ifndef WINDOWS_ENABLED
|
||||
list.push_back("app");
|
||||
#endif
|
||||
} else if (dist_type == 1) {
|
||||
#ifdef MACOS_ENABLED
|
||||
list.push_back("dmg");
|
||||
#endif
|
||||
list.push_back("zip");
|
||||
#ifndef WINDOWS_ENABLED
|
||||
list.push_back("app");
|
||||
#endif
|
||||
} else if (dist_type == 2) {
|
||||
#ifdef MACOS_ENABLED
|
||||
list.push_back("pkg");
|
||||
|
|
@ -451,13 +463,15 @@ void EditorExportPlatformMacOS::get_export_options(List<ExportOption> *r_options
|
|||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version", PROPERTY_HINT_PLACEHOLDER_TEXT, "Leave empty to use project version"), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::DICTIONARY, "application/copyright_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary()));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/min_macos_version"), "10.12"));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/min_macos_version_x86_64"), "10.12"));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/min_macos_version_arm64"), "11.00"));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_angle", PROPERTY_HINT_ENUM, "Auto,Yes,No"), 0, true));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "display/high_res"), true));
|
||||
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/additional_plist_content", PROPERTY_HINT_MULTILINE_TEXT), ""));
|
||||
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "xcode/platform_build"), "14C18"));
|
||||
// TODO(sgc): Need to set appropriate version when using Metal
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "xcode/sdk_version"), "13.1"));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "xcode/sdk_build"), "22C55"));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "xcode/sdk_name"), "macosx13.1"));
|
||||
|
|
@ -503,6 +517,7 @@ void EditorExportPlatformMacOS::get_export_options(List<ExportOption> *r_options
|
|||
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_movies", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_user_selected", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::ARRAY, "codesign/entitlements/app_sandbox/helper_executables", PROPERTY_HINT_ARRAY_TYPE, itos(Variant::STRING) + "/" + itos(PROPERTY_HINT_GLOBAL_FILE) + ":"), Array()));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/entitlements/additional", PROPERTY_HINT_MULTILINE_TEXT), ""));
|
||||
r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "codesign/custom_options"), PackedStringArray()));
|
||||
|
||||
#ifdef MACOS_ENABLED
|
||||
|
|
@ -822,8 +837,12 @@ void EditorExportPlatformMacOS::_fix_plist(const Ref<EditorExportPreset> &p_pres
|
|||
strnew += lines[i].replace("$app_category", cat.to_lower()) + "\n";
|
||||
} else if (lines[i].contains("$copyright")) {
|
||||
strnew += lines[i].replace("$copyright", p_preset->get("application/copyright")) + "\n";
|
||||
} else if (lines[i].contains("$min_version_arm64")) {
|
||||
strnew += lines[i].replace("$min_version_arm64", p_preset->get("application/min_macos_version_arm64")) + "\n";
|
||||
} else if (lines[i].contains("$min_version_x86_64")) {
|
||||
strnew += lines[i].replace("$min_version_x86_64", p_preset->get("application/min_macos_version_x86_64")) + "\n";
|
||||
} else if (lines[i].contains("$min_version")) {
|
||||
strnew += lines[i].replace("$min_version", p_preset->get("application/min_macos_version")) + "\n";
|
||||
strnew += lines[i].replace("$min_version", p_preset->get("application/min_macos_version_x86_64")) + "\n"; // Old template, use x86-64 version for both.
|
||||
} else if (lines[i].contains("$highres")) {
|
||||
strnew += lines[i].replace("$highres", p_preset->get("display/high_res") ? "\t<true/>" : "\t<false/>") + "\n";
|
||||
} else if (lines[i].contains("$additional_plist_content")) {
|
||||
|
|
@ -962,7 +981,7 @@ Error EditorExportPlatformMacOS::_notarize(const Ref<EditorExportPreset> &p_pres
|
|||
return Error::FAILED;
|
||||
} else {
|
||||
print_verbose("rcodesign (" + p_path + "):\n" + str);
|
||||
int next_nl = str.find("\n", rq_offset);
|
||||
int next_nl = str.find_char('\n', rq_offset);
|
||||
String request_uuid = (next_nl == -1) ? str.substr(rq_offset + 23, -1) : str.substr(rq_offset + 23, next_nl - rq_offset - 23);
|
||||
add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), vformat(TTR("Notarization request UUID: \"%s\""), request_uuid));
|
||||
add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), TTR("The notarization process generally takes less than an hour."));
|
||||
|
|
@ -1046,7 +1065,7 @@ Error EditorExportPlatformMacOS::_notarize(const Ref<EditorExportPreset> &p_pres
|
|||
return Error::FAILED;
|
||||
} else {
|
||||
print_verbose("notarytool (" + p_path + "):\n" + str);
|
||||
int next_nl = str.find("\n", rq_offset);
|
||||
int next_nl = str.find_char('\n', rq_offset);
|
||||
String request_uuid = (next_nl == -1) ? str.substr(rq_offset + 4, -1) : str.substr(rq_offset + 4, next_nl - rq_offset - 4);
|
||||
add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), vformat(TTR("Notarization request UUID: \"%s\""), request_uuid));
|
||||
add_message(EXPORT_MESSAGE_INFO, TTR("Notarization"), TTR("The notarization process generally takes less than an hour."));
|
||||
|
|
@ -1069,16 +1088,12 @@ void EditorExportPlatformMacOS::_code_sign(const Ref<EditorExportPreset> &p_pres
|
|||
switch (codesign_tool) {
|
||||
case 1: { // built-in ad-hoc
|
||||
print_verbose("using built-in codesign...");
|
||||
#ifdef MODULE_REGEX_ENABLED
|
||||
String error_msg;
|
||||
Error err = CodeSign::codesign(false, true, p_path, p_ent_path, error_msg);
|
||||
if (err != OK) {
|
||||
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), vformat(TTR("Built-in CodeSign failed with error \"%s\"."), error_msg));
|
||||
return;
|
||||
}
|
||||
#else
|
||||
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("Built-in CodeSign require regex module."));
|
||||
#endif
|
||||
} break;
|
||||
case 2: { // "rcodesign"
|
||||
print_verbose("using rcodesign codesign...");
|
||||
|
|
@ -1092,7 +1107,7 @@ void EditorExportPlatformMacOS::_code_sign(const Ref<EditorExportPreset> &p_pres
|
|||
List<String> args;
|
||||
args.push_back("sign");
|
||||
|
||||
if (p_path.get_extension() != "dmg") {
|
||||
if (!p_ent_path.is_empty()) {
|
||||
args.push_back("--entitlements-xml-path");
|
||||
args.push_back(p_ent_path);
|
||||
}
|
||||
|
|
@ -1153,7 +1168,7 @@ void EditorExportPlatformMacOS::_code_sign(const Ref<EditorExportPreset> &p_pres
|
|||
args.push_back("runtime");
|
||||
}
|
||||
|
||||
if (p_path.get_extension() != "dmg") {
|
||||
if (!p_ent_path.is_empty()) {
|
||||
args.push_back("--entitlements");
|
||||
args.push_back(p_ent_path);
|
||||
}
|
||||
|
|
@ -1237,7 +1252,7 @@ void EditorExportPlatformMacOS::_code_sign_directory(const Ref<EditorExportPrese
|
|||
}
|
||||
|
||||
if (extensions_to_sign.has(current_file.get_extension())) {
|
||||
String ent_path = p_ent_path;
|
||||
String ent_path;
|
||||
bool set_bundle_id = false;
|
||||
if (sandbox && FileAccess::exists(current_file_path)) {
|
||||
int ftype = MachO::get_filetype(current_file_path);
|
||||
|
|
@ -1281,18 +1296,18 @@ Error EditorExportPlatformMacOS::_copy_and_sign_files(Ref<DirAccess> &dir_access
|
|||
#endif
|
||||
print_verbose("export framework: " + p_src_path + " -> " + p_in_app_path);
|
||||
|
||||
bool plist_misssing = false;
|
||||
bool plist_missing = false;
|
||||
Ref<PList> plist;
|
||||
plist.instantiate();
|
||||
plist->load_file(p_src_path.path_join("Resources").path_join("Info.plist"));
|
||||
|
||||
Ref<PListNode> root_node = plist->get_root();
|
||||
if (root_node.is_null()) {
|
||||
plist_misssing = true;
|
||||
plist_missing = true;
|
||||
} else {
|
||||
Dictionary root = root_node->get_value();
|
||||
if (!root.has("CFBundleExecutable") || !root.has("CFBundleIdentifier") || !root.has("CFBundlePackageType") || !root.has("CFBundleInfoDictionaryVersion") || !root.has("CFBundleName") || !root.has("CFBundleSupportedPlatforms")) {
|
||||
plist_misssing = true;
|
||||
plist_missing = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1300,7 +1315,7 @@ Error EditorExportPlatformMacOS::_copy_and_sign_files(Ref<DirAccess> &dir_access
|
|||
if (err == OK) {
|
||||
err = dir_access->copy_dir(p_src_path, p_in_app_path, -1, true);
|
||||
}
|
||||
if (err == OK && plist_misssing) {
|
||||
if (err == OK && plist_missing) {
|
||||
add_message(EXPORT_MESSAGE_WARNING, TTR("Export"), vformat(TTR("\"%s\": Info.plist missing or invalid, new Info.plist generated."), p_src_path.get_file()));
|
||||
// Generate Info.plist
|
||||
String lib_name = p_src_path.get_basename().get_file();
|
||||
|
|
@ -1357,7 +1372,7 @@ Error EditorExportPlatformMacOS::_copy_and_sign_files(Ref<DirAccess> &dir_access
|
|||
_code_sign_directory(p_preset, p_in_app_path, p_ent_path, p_helper_ent_path, p_should_error_on_non_code_sign);
|
||||
} else {
|
||||
if (extensions_to_sign.has(p_in_app_path.get_extension())) {
|
||||
String ent_path = p_ent_path;
|
||||
String ent_path;
|
||||
bool set_bundle_id = false;
|
||||
if (p_sandbox && FileAccess::exists(p_in_app_path)) {
|
||||
int ftype = MachO::get_filetype(p_in_app_path);
|
||||
|
|
@ -1504,7 +1519,7 @@ Error EditorExportPlatformMacOS::_export_debug_script(const Ref<EditorExportPres
|
|||
return OK;
|
||||
}
|
||||
|
||||
Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
|
||||
Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) {
|
||||
ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
|
||||
|
||||
const String base_dir = p_path.get_base_dir();
|
||||
|
|
@ -1584,7 +1599,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
|
|||
tmp_app_path_name = p_path;
|
||||
scr_path = p_path.get_basename() + ".command";
|
||||
} else {
|
||||
tmp_base_path_name = EditorPaths::get_singleton()->get_cache_dir().path_join(pkg_name);
|
||||
tmp_base_path_name = EditorPaths::get_singleton()->get_temp_dir().path_join(pkg_name);
|
||||
tmp_app_path_name = tmp_base_path_name.path_join(tmp_app_dir_name);
|
||||
scr_path = tmp_base_path_name.path_join(pkg_name + ".command");
|
||||
}
|
||||
|
|
@ -1868,10 +1883,8 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
|
|||
icon->get_buffer(&data.write[0], icon->get_length());
|
||||
}
|
||||
} else {
|
||||
Ref<Image> icon;
|
||||
icon.instantiate();
|
||||
err = ImageLoader::load_image(icon_path, icon);
|
||||
if (err == OK && !icon->is_empty()) {
|
||||
Ref<Image> icon = _load_icon_or_splash_image(icon_path, &err);
|
||||
if (err == OK && icon.is_valid() && !icon->is_empty()) {
|
||||
_make_icon(p_preset, icon, data);
|
||||
}
|
||||
}
|
||||
|
|
@ -1897,6 +1910,11 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
|
|||
if (is_executable(file)) {
|
||||
// chmod with 0755 if the file is executable.
|
||||
FileAccess::set_unix_permissions(file, 0755);
|
||||
#ifndef UNIX_ENABLED
|
||||
if (export_format == "app") {
|
||||
add_message(EXPORT_MESSAGE_INFO, TTR("Export"), vformat(TTR("Unable to set Unix permissions for executable \"%s\". Use \"chmod +x\" to set it after transferring the exported .app to macOS or Linux."), "Contents/MacOS/" + file.get_file()));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Could not open \"%s\"."), file));
|
||||
|
|
@ -1922,6 +1940,11 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
|
|||
if ((con_scr == 1 && p_debug) || (con_scr == 2)) {
|
||||
err = _export_debug_script(p_preset, pkg_name, tmp_app_path_name.get_file() + "/Contents/MacOS/" + pkg_name, scr_path);
|
||||
FileAccess::set_unix_permissions(scr_path, 0755);
|
||||
#ifndef UNIX_ENABLED
|
||||
if (export_format == "app") {
|
||||
add_message(EXPORT_MESSAGE_INFO, TTR("Export"), vformat(TTR("Unable to set Unix permissions for executable \"%s\". Use \"chmod +x\" to set it after transferring the exported .app to macOS or Linux."), scr_path.get_file()));
|
||||
}
|
||||
#endif
|
||||
if (err != OK) {
|
||||
add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Could not create console wrapper."));
|
||||
}
|
||||
|
|
@ -1969,9 +1992,9 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
|
|||
|
||||
bool sandbox = p_preset->get("codesign/entitlements/app_sandbox/enabled");
|
||||
String ent_path = p_preset->get("codesign/entitlements/custom_file");
|
||||
String hlp_ent_path = sandbox ? EditorPaths::get_singleton()->get_cache_dir().path_join(pkg_name + "_helper.entitlements") : ent_path;
|
||||
String hlp_ent_path = sandbox ? EditorPaths::get_singleton()->get_temp_dir().path_join(pkg_name + "_helper.entitlements") : ent_path;
|
||||
if (sign_enabled && (ent_path.is_empty())) {
|
||||
ent_path = EditorPaths::get_singleton()->get_cache_dir().path_join(pkg_name + ".entitlements");
|
||||
ent_path = EditorPaths::get_singleton()->get_temp_dir().path_join(pkg_name + ".entitlements");
|
||||
|
||||
Ref<FileAccess> ent_f = FileAccess::open(ent_path, FileAccess::WRITE);
|
||||
if (ent_f.is_valid()) {
|
||||
|
|
@ -2114,6 +2137,11 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
|
|||
}
|
||||
}
|
||||
|
||||
const String &additional_entitlements = p_preset->get("codesign/entitlements/additional");
|
||||
if (!additional_entitlements.is_empty()) {
|
||||
ent_f->store_line(additional_entitlements);
|
||||
}
|
||||
|
||||
ent_f->store_line("</dict>");
|
||||
ent_f->store_line("</plist>");
|
||||
} else {
|
||||
|
|
@ -2150,6 +2178,11 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
|
|||
_code_sign(p_preset, tmp_app_path_name + "/Contents/Helpers/" + hlp_path.get_file(), hlp_ent_path, false, true);
|
||||
}
|
||||
FileAccess::set_unix_permissions(tmp_app_path_name + "/Contents/Helpers/" + hlp_path.get_file(), 0755);
|
||||
#ifndef UNIX_ENABLED
|
||||
if (export_format == "app") {
|
||||
add_message(EXPORT_MESSAGE_INFO, TTR("Export"), vformat(TTR("Unable to set Unix permissions for executable \"%s\". Use \"chmod +x\" to set it after transferring the exported .app to macOS or Linux."), "Contents/Helpers/" + hlp_path.get_file()));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2241,7 +2274,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
|
|||
} else if (export_format == "app" && noto_enabled) {
|
||||
// Create temporary ZIP.
|
||||
if (err == OK) {
|
||||
noto_path = EditorPaths::get_singleton()->get_cache_dir().path_join(pkg_name + ".zip");
|
||||
noto_path = EditorPaths::get_singleton()->get_temp_dir().path_join(pkg_name + ".zip");
|
||||
|
||||
if (ep.step(TTR("Making ZIP"), 3)) {
|
||||
return ERR_SKIP;
|
||||
|
|
@ -2271,6 +2304,14 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
|
|||
}
|
||||
}
|
||||
|
||||
if (FileAccess::exists(ent_path)) {
|
||||
print_verbose("entitlements:\n" + FileAccess::get_file_as_string(ent_path));
|
||||
}
|
||||
|
||||
if (FileAccess::exists(hlp_ent_path)) {
|
||||
print_verbose("helper entitlements:\n" + FileAccess::get_file_as_string(hlp_ent_path));
|
||||
}
|
||||
|
||||
// Clean up temporary entitlements files.
|
||||
if (FileAccess::exists(hlp_ent_path)) {
|
||||
DirAccess::remove_file_or_error(hlp_ent_path);
|
||||
|
|
@ -2510,7 +2551,7 @@ void EditorExportPlatformMacOS::cleanup() {
|
|||
cleanup_commands.clear();
|
||||
}
|
||||
|
||||
Error EditorExportPlatformMacOS::run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) {
|
||||
Error EditorExportPlatformMacOS::run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) {
|
||||
cleanup();
|
||||
if (p_device) { // Stop command, cleanup only.
|
||||
return OK;
|
||||
|
|
@ -2518,7 +2559,7 @@ Error EditorExportPlatformMacOS::run(const Ref<EditorExportPreset> &p_preset, in
|
|||
|
||||
EditorProgress ep("run", TTR("Running..."), 5);
|
||||
|
||||
const String dest = EditorPaths::get_singleton()->get_cache_dir().path_join("macos");
|
||||
const String dest = EditorPaths::get_singleton()->get_temp_dir().path_join("macos");
|
||||
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
|
||||
if (!da->dir_exists(dest)) {
|
||||
Error err = da->make_dir_recursive(dest);
|
||||
|
|
@ -2572,8 +2613,7 @@ Error EditorExportPlatformMacOS::run(const Ref<EditorExportPreset> &p_preset, in
|
|||
|
||||
String cmd_args;
|
||||
{
|
||||
Vector<String> cmd_args_list;
|
||||
gen_debug_flags(cmd_args_list, p_debug_flags);
|
||||
Vector<String> cmd_args_list = gen_export_flags(p_debug_flags);
|
||||
for (int i = 0; i < cmd_args_list.size(); i++) {
|
||||
if (i != 0) {
|
||||
cmd_args += " ";
|
||||
|
|
@ -2582,7 +2622,7 @@ Error EditorExportPlatformMacOS::run(const Ref<EditorExportPreset> &p_preset, in
|
|||
}
|
||||
}
|
||||
|
||||
const bool use_remote = (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) || (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT);
|
||||
const bool use_remote = p_debug_flags.has_flag(DEBUG_FLAG_REMOTE_DEBUG) || p_debug_flags.has_flag(DEBUG_FLAG_DUMB_CLIENT);
|
||||
int dbg_port = EditorSettings::get_singleton()->get("network/debug/remote_port");
|
||||
|
||||
print_line("Creating temporary directory...");
|
||||
|
|
@ -2667,7 +2707,6 @@ Error EditorExportPlatformMacOS::run(const Ref<EditorExportPreset> &p_preset, in
|
|||
|
||||
EditorExportPlatformMacOS::EditorExportPlatformMacOS() {
|
||||
if (EditorNode::get_singleton()) {
|
||||
#ifdef MODULE_SVG_ENABLED
|
||||
Ref<Image> img = memnew(Image);
|
||||
const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE);
|
||||
|
||||
|
|
@ -2676,7 +2715,6 @@ EditorExportPlatformMacOS::EditorExportPlatformMacOS() {
|
|||
|
||||
ImageLoaderSVG::create_image_from_string(img, _macos_run_icon_svg, EDSCALE, upsample, false);
|
||||
run_icon = ImageTexture::create_from_image(img);
|
||||
#endif
|
||||
|
||||
Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme();
|
||||
if (theme.is_valid()) {
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
#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"
|
||||
|
|
@ -68,14 +69,14 @@ class EditorExportPlatformMacOS : public EditorExportPlatform {
|
|||
String cmd_args;
|
||||
bool wait = false;
|
||||
|
||||
SSHCleanupCommand(){};
|
||||
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;
|
||||
|
|
@ -147,7 +148,7 @@ public:
|
|||
|
||||
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, int p_flags = 0) 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;
|
||||
|
|
@ -167,7 +168,7 @@ public:
|
|||
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, int p_debug_flags) override;
|
||||
virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) override;
|
||||
virtual void cleanup() override;
|
||||
|
||||
EditorExportPlatformMacOS();
|
||||
|
|
|
|||
|
|
@ -394,6 +394,11 @@
|
|||
}
|
||||
|
||||
- (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];
|
||||
|
|
@ -404,10 +409,65 @@
|
|||
}
|
||||
|
||||
- (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 {
|
||||
|
|
|
|||
|
|
@ -102,7 +102,11 @@
|
|||
} else {
|
||||
// Otherwise redirect event to the engine.
|
||||
if (DisplayServer::get_singleton()) {
|
||||
[[[NSApplication sharedApplication] keyWindow] sendEvent:event];
|
||||
if ([[NSApplication sharedApplication] keyWindow].sheet) {
|
||||
[[[[NSApplication sharedApplication] keyWindow] sheetParent] sendEvent:event];
|
||||
} else {
|
||||
[[[NSApplication sharedApplication] keyWindow] sendEvent:event];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
|
||||
|
||||
#include "core/templates/hash_map.h"
|
||||
#include "core/variant/typed_array.h"
|
||||
|
|
|
|||
|
|
@ -119,18 +119,44 @@
|
|||
Vector<String> tokens = p_filters[i].split(";");
|
||||
if (tokens.size() >= 1) {
|
||||
String flt = tokens[0].strip_edges();
|
||||
String mime = (tokens.size() >= 2) ? 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_slice(",", j).strip_edges());
|
||||
if (!str.is_empty()) {
|
||||
[type_filters addObject:[NSString stringWithUTF8String:str.replace("*.", "").strip_edges().utf8().get_data()]];
|
||||
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] : vformat("%s (%s)", tokens[1].strip_edges(), tokens[0].strip_edges())).utf8().get_data()];
|
||||
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];
|
||||
}
|
||||
|
|
@ -147,13 +173,38 @@
|
|||
Vector<String> tokens = p_filters[0].split(";");
|
||||
if (tokens.size() >= 1) {
|
||||
String flt = tokens[0].strip_edges();
|
||||
String mime = (tokens.size() >= 2) ? 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_slice(",", j).strip_edges());
|
||||
if (!str.is_empty()) {
|
||||
[type_filters addObject:[NSString stringWithUTF8String:str.replace("*.", "").strip_edges().utf8().get_data()]];
|
||||
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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -176,15 +227,29 @@
|
|||
}
|
||||
if ([new_allowed_types count] > 0) {
|
||||
NSMutableArray *type_filters = [new_allowed_types objectAtIndex:0];
|
||||
if (type_filters && [type_filters count] == 1 && [[type_filters objectAtIndex:0] isEqualToString:@"*"]) {
|
||||
[p_panel setAllowedFileTypes:nil];
|
||||
[p_panel setAllowsOtherFileTypes:true];
|
||||
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 {
|
||||
[p_panel setAllowsOtherFileTypes:false];
|
||||
[p_panel setAllowedFileTypes:type_filters];
|
||||
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 {
|
||||
[p_panel setAllowedFileTypes:nil];
|
||||
if (@available(macOS 11, *)) {
|
||||
[p_panel setAllowedContentTypes:@[ UTTypeData ]];
|
||||
} else {
|
||||
[p_panel setAllowedFileTypes:nil];
|
||||
}
|
||||
[p_panel setAllowsOtherFileTypes:true];
|
||||
}
|
||||
}
|
||||
|
|
@ -247,16 +312,30 @@
|
|||
NSUInteger index = [btn indexOfSelectedItem];
|
||||
if (allowed_types && index < [allowed_types count]) {
|
||||
NSMutableArray *type_filters = [allowed_types objectAtIndex:index];
|
||||
if (type_filters && [type_filters count] == 1 && [[type_filters objectAtIndex:0] isEqualToString:@"*"]) {
|
||||
[dialog setAllowedFileTypes:nil];
|
||||
[dialog setAllowsOtherFileTypes:true];
|
||||
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 {
|
||||
[dialog setAllowsOtherFileTypes:false];
|
||||
[dialog setAllowedFileTypes:type_filters];
|
||||
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 {
|
||||
[dialog setAllowedFileTypes:nil];
|
||||
if (@available(macOS 11, *)) {
|
||||
[dialog setAllowedContentTypes:@[ UTTypeData ]];
|
||||
} else {
|
||||
[dialog setAllowedFileTypes:nil];
|
||||
}
|
||||
[dialog setAllowsOtherFileTypes:true];
|
||||
cur_index = -1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -194,7 +194,7 @@
|
|||
}
|
||||
|
||||
// Restore on-top state.
|
||||
if (wd.on_top) {
|
||||
if (ds->is_always_on_top_recursive(window_id)) {
|
||||
[wd.window_object setLevel:NSFloatingWindowLevel];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,89 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* joypad_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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "core/input/input.h"
|
||||
|
||||
#define Key _QKey
|
||||
#import <CoreHaptics/CoreHaptics.h>
|
||||
#import <GameController/GameController.h>
|
||||
#undef Key
|
||||
|
||||
@interface JoypadMacOSObserver : NSObject
|
||||
|
||||
- (void)startObserving;
|
||||
- (void)startProcessing;
|
||||
- (void)finishObserving;
|
||||
|
||||
@end
|
||||
|
||||
API_AVAILABLE(macosx(11))
|
||||
@interface RumbleMotor : NSObject
|
||||
@property(strong, nonatomic) CHHapticEngine *engine;
|
||||
@property(strong, nonatomic) id<CHHapticPatternPlayer> player;
|
||||
@end
|
||||
|
||||
API_AVAILABLE(macosx(11))
|
||||
@interface RumbleContext : NSObject
|
||||
// High frequency motor, it's usually the right engine.
|
||||
@property(strong, nonatomic) RumbleMotor *weak_motor;
|
||||
// Low frequency motor, it's usually the left engine.
|
||||
@property(strong, nonatomic) RumbleMotor *strong_motor;
|
||||
@end
|
||||
|
||||
// Controller support for macOS begins with macOS 10.9+,
|
||||
// however haptics (vibrations) are only supported in macOS 11+.
|
||||
@interface Joypad : NSObject
|
||||
|
||||
@property(assign, nonatomic) BOOL force_feedback;
|
||||
@property(assign, nonatomic) NSInteger ff_effect_timestamp;
|
||||
@property(strong, nonatomic) GCController *controller;
|
||||
@property(strong, nonatomic) RumbleContext *rumble_context API_AVAILABLE(macosx(11));
|
||||
|
||||
- (instancetype)init;
|
||||
- (instancetype)init:(GCController *)controller;
|
||||
|
||||
@end
|
||||
|
||||
class JoypadMacOS {
|
||||
private:
|
||||
JoypadMacOSObserver *observer;
|
||||
|
||||
public:
|
||||
JoypadMacOS();
|
||||
~JoypadMacOS();
|
||||
|
||||
API_AVAILABLE(macosx(11))
|
||||
void joypad_vibration_start(Joypad *p_joypad, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp);
|
||||
API_AVAILABLE(macosx(11))
|
||||
void joypad_vibration_stop(Joypad *p_joypad, uint64_t p_timestamp);
|
||||
|
||||
void start_processing();
|
||||
void process_joypads();
|
||||
};
|
||||
|
|
@ -1,611 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* joypad_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 "joypad_macos.h"
|
||||
|
||||
#include <Foundation/Foundation.h>
|
||||
|
||||
#import "os_macos.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/os/keyboard.h"
|
||||
#include "core/string/ustring.h"
|
||||
#include "main/main.h"
|
||||
|
||||
@implementation RumbleMotor
|
||||
|
||||
- (instancetype)initWithController:(GCController *)controller locality:(GCHapticsLocality)locality {
|
||||
self = [super init];
|
||||
self.engine = [controller.haptics createEngineWithLocality:locality];
|
||||
self.player = nil;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)execute_pattern:(CHHapticPattern *)pattern {
|
||||
NSError *error;
|
||||
id<CHHapticPatternPlayer> player = [self.engine createPlayerWithPattern:pattern error:&error];
|
||||
|
||||
// When all players have stopped for an engine, stop the engine.
|
||||
[self.engine notifyWhenPlayersFinished:^CHHapticEngineFinishedAction(NSError *_Nullable error) {
|
||||
return CHHapticEngineFinishedActionStopEngine;
|
||||
}];
|
||||
|
||||
self.player = player;
|
||||
|
||||
// Starts the engine and returns if an error was encountered.
|
||||
if (![self.engine startAndReturnError:&error]) {
|
||||
print_verbose("Couldn't start controller haptic engine");
|
||||
return;
|
||||
}
|
||||
if (![self.player startAtTime:0 error:&error]) {
|
||||
print_verbose("Couldn't execute controller haptic pattern");
|
||||
}
|
||||
}
|
||||
|
||||
- (void)stop {
|
||||
NSError *error;
|
||||
[self.player stopAtTime:0 error:&error];
|
||||
self.player = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation RumbleContext
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
self.weak_motor = nil;
|
||||
self.strong_motor = nil;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (bool)hasMotors {
|
||||
return self.weak_motor != nil && self.strong_motor != nil;
|
||||
}
|
||||
- (bool)hasActivePlayers {
|
||||
if (![self hasMotors]) {
|
||||
return NO;
|
||||
}
|
||||
return self.weak_motor.player != nil && self.strong_motor.player != nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation Joypad
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
return self;
|
||||
}
|
||||
- (instancetype)init:(GCController *)controller {
|
||||
self = [super init];
|
||||
self.controller = controller;
|
||||
|
||||
if (@available(macOS 11, *)) {
|
||||
// Haptics within the controller is only available in macOS 11+.
|
||||
self.rumble_context = [[RumbleContext alloc] init];
|
||||
|
||||
// Create Weak and Strong motors for controller.
|
||||
self.rumble_context.weak_motor = [[RumbleMotor alloc] initWithController:controller locality:GCHapticsLocalityRightHandle];
|
||||
self.rumble_context.strong_motor = [[RumbleMotor alloc] initWithController:controller locality:GCHapticsLocalityLeftHandle];
|
||||
|
||||
// If the rumble motors aren't available, disable force feedback.
|
||||
if (![self.rumble_context hasMotors]) {
|
||||
self.force_feedback = NO;
|
||||
} else {
|
||||
self.force_feedback = YES;
|
||||
}
|
||||
} else {
|
||||
self.force_feedback = NO;
|
||||
}
|
||||
|
||||
self.ff_effect_timestamp = 0;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
JoypadMacOS::JoypadMacOS() {
|
||||
observer = [[JoypadMacOSObserver alloc] init];
|
||||
[observer startObserving];
|
||||
|
||||
if (@available(macOS 11.3, *)) {
|
||||
GCController.shouldMonitorBackgroundEvents = YES;
|
||||
}
|
||||
}
|
||||
|
||||
JoypadMacOS::~JoypadMacOS() {
|
||||
if (observer) {
|
||||
[observer finishObserving];
|
||||
observer = nil;
|
||||
}
|
||||
}
|
||||
|
||||
void JoypadMacOS::start_processing() {
|
||||
if (observer) {
|
||||
[observer startProcessing];
|
||||
}
|
||||
process_joypads();
|
||||
}
|
||||
|
||||
API_AVAILABLE(macosx(10.15))
|
||||
CHHapticPattern *get_vibration_pattern(float p_magnitude, float p_duration) {
|
||||
// Creates a vibration pattern with an intensity and duration.
|
||||
NSDictionary *hapticDict = @{
|
||||
CHHapticPatternKeyPattern : @[
|
||||
@{
|
||||
CHHapticPatternKeyEvent : @{
|
||||
CHHapticPatternKeyEventType : CHHapticEventTypeHapticContinuous,
|
||||
CHHapticPatternKeyTime : @(CHHapticTimeImmediate),
|
||||
CHHapticPatternKeyEventDuration : [NSNumber numberWithFloat:p_duration],
|
||||
|
||||
CHHapticPatternKeyEventParameters : @[
|
||||
@{
|
||||
CHHapticPatternKeyParameterID : CHHapticEventParameterIDHapticIntensity,
|
||||
CHHapticPatternKeyParameterValue : [NSNumber numberWithFloat:p_magnitude]
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
NSError *error;
|
||||
CHHapticPattern *pattern = [[CHHapticPattern alloc] initWithDictionary:hapticDict error:&error];
|
||||
return pattern;
|
||||
}
|
||||
|
||||
void JoypadMacOS::joypad_vibration_start(Joypad *p_joypad, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp) {
|
||||
if (!p_joypad.force_feedback || p_weak_magnitude < 0.f || p_weak_magnitude > 1.f || p_strong_magnitude < 0.f || p_strong_magnitude > 1.f) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If there is active vibration players, stop them.
|
||||
if ([p_joypad.rumble_context hasActivePlayers]) {
|
||||
joypad_vibration_stop(p_joypad, p_timestamp);
|
||||
}
|
||||
|
||||
// Gets the default vibration pattern and creates a player for each motor.
|
||||
CHHapticPattern *weak_pattern = get_vibration_pattern(p_weak_magnitude, p_duration);
|
||||
CHHapticPattern *strong_pattern = get_vibration_pattern(p_strong_magnitude, p_duration);
|
||||
|
||||
RumbleMotor *weak_motor = p_joypad.rumble_context.weak_motor;
|
||||
RumbleMotor *strong_motor = p_joypad.rumble_context.strong_motor;
|
||||
|
||||
[weak_motor execute_pattern:weak_pattern];
|
||||
[strong_motor execute_pattern:strong_pattern];
|
||||
|
||||
p_joypad.ff_effect_timestamp = p_timestamp;
|
||||
}
|
||||
|
||||
void JoypadMacOS::joypad_vibration_stop(Joypad *p_joypad, uint64_t p_timestamp) {
|
||||
if (!p_joypad.force_feedback) {
|
||||
return;
|
||||
}
|
||||
// If there is no active vibration players, exit.
|
||||
if (![p_joypad.rumble_context hasActivePlayers]) {
|
||||
return;
|
||||
}
|
||||
|
||||
RumbleMotor *weak_motor = p_joypad.rumble_context.weak_motor;
|
||||
RumbleMotor *strong_motor = p_joypad.rumble_context.strong_motor;
|
||||
|
||||
[weak_motor stop];
|
||||
[strong_motor stop];
|
||||
|
||||
p_joypad.ff_effect_timestamp = p_timestamp;
|
||||
}
|
||||
|
||||
@interface JoypadMacOSObserver ()
|
||||
|
||||
@property(assign, nonatomic) BOOL isObserving;
|
||||
@property(assign, nonatomic) BOOL isProcessing;
|
||||
@property(strong, nonatomic) NSMutableDictionary<NSNumber *, Joypad *> *connectedJoypads;
|
||||
@property(strong, nonatomic) NSMutableArray<GCController *> *joypadsQueue;
|
||||
|
||||
@end
|
||||
|
||||
@implementation JoypadMacOSObserver
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
[self godot_commonInit];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)godot_commonInit {
|
||||
self.isObserving = NO;
|
||||
self.isProcessing = NO;
|
||||
}
|
||||
|
||||
- (void)startProcessing {
|
||||
self.isProcessing = YES;
|
||||
|
||||
for (GCController *controller in self.joypadsQueue) {
|
||||
[self addMacOSJoypad:controller];
|
||||
}
|
||||
|
||||
[self.joypadsQueue removeAllObjects];
|
||||
}
|
||||
|
||||
- (void)startObserving {
|
||||
if (self.isObserving) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.isObserving = YES;
|
||||
|
||||
self.connectedJoypads = [NSMutableDictionary dictionary];
|
||||
self.joypadsQueue = [NSMutableArray array];
|
||||
|
||||
// Get told when controllers connect, this will be called right away for
|
||||
// already connected controllers.
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
addObserver:self
|
||||
selector:@selector(controllerWasConnected:)
|
||||
name:GCControllerDidConnectNotification
|
||||
object:nil];
|
||||
|
||||
// Get told when controllers disconnect.
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
addObserver:self
|
||||
selector:@selector(controllerWasDisconnected:)
|
||||
name:GCControllerDidDisconnectNotification
|
||||
object:nil];
|
||||
}
|
||||
|
||||
- (void)finishObserving {
|
||||
if (self.isObserving) {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
self.isObserving = NO;
|
||||
self.isProcessing = NO;
|
||||
|
||||
self.connectedJoypads = nil;
|
||||
self.joypadsQueue = nil;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self finishObserving];
|
||||
}
|
||||
|
||||
- (NSArray<NSNumber *> *)getAllKeysForController:(GCController *)controller {
|
||||
NSArray *keys = [self.connectedJoypads allKeys];
|
||||
NSMutableArray *final_keys = [NSMutableArray array];
|
||||
|
||||
for (NSNumber *key in keys) {
|
||||
Joypad *joypad = [self.connectedJoypads objectForKey:key];
|
||||
if (joypad.controller == controller) {
|
||||
[final_keys addObject:key];
|
||||
}
|
||||
}
|
||||
|
||||
return final_keys;
|
||||
}
|
||||
|
||||
- (int)getJoyIdForController:(GCController *)controller {
|
||||
NSArray *keys = [self getAllKeysForController:controller];
|
||||
|
||||
for (NSNumber *key in keys) {
|
||||
int joy_id = [key intValue];
|
||||
return joy_id;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
- (void)addMacOSJoypad:(GCController *)controller {
|
||||
// Get a new id for our controller.
|
||||
int joy_id = Input::get_singleton()->get_unused_joy_id();
|
||||
|
||||
if (joy_id == -1) {
|
||||
print_verbose("Couldn't retrieve new joy ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Assign our player index.
|
||||
if (controller.playerIndex == GCControllerPlayerIndexUnset) {
|
||||
controller.playerIndex = [self getFreePlayerIndex];
|
||||
}
|
||||
|
||||
// Tell Godot about our new controller.
|
||||
Input::get_singleton()->joy_connection_changed(joy_id, true, String::utf8([controller.vendorName UTF8String]));
|
||||
|
||||
Joypad *joypad = [[Joypad alloc] init:controller];
|
||||
|
||||
// Add it to our dictionary, this will retain our controllers.
|
||||
[self.connectedJoypads setObject:joypad forKey:[NSNumber numberWithInt:joy_id]];
|
||||
|
||||
// Set our input handler.
|
||||
[self setControllerInputHandler:controller];
|
||||
}
|
||||
|
||||
- (void)controllerWasConnected:(NSNotification *)notification {
|
||||
// Get our controller.
|
||||
GCController *controller = (GCController *)notification.object;
|
||||
|
||||
if (!controller) {
|
||||
print_verbose("Couldn't retrieve new controller.");
|
||||
return;
|
||||
}
|
||||
|
||||
if ([[self getAllKeysForController:controller] count] > 0) {
|
||||
print_verbose("Controller is already registered.");
|
||||
} else if (!self.isProcessing) {
|
||||
[self.joypadsQueue addObject:controller];
|
||||
} else {
|
||||
[self addMacOSJoypad:controller];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)controllerWasDisconnected:(NSNotification *)notification {
|
||||
// Find our joystick, there should be only one in our dictionary.
|
||||
GCController *controller = (GCController *)notification.object;
|
||||
|
||||
if (!controller) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSArray *keys = [self getAllKeysForController:controller];
|
||||
for (NSNumber *key in keys) {
|
||||
// Tell Godot this joystick is no longer there.
|
||||
int joy_id = [key intValue];
|
||||
Input::get_singleton()->joy_connection_changed(joy_id, false, "");
|
||||
|
||||
// And remove it from our dictionary.
|
||||
[self.connectedJoypads removeObjectForKey:key];
|
||||
}
|
||||
}
|
||||
|
||||
- (GCControllerPlayerIndex)getFreePlayerIndex {
|
||||
bool have_player_1 = false;
|
||||
bool have_player_2 = false;
|
||||
bool have_player_3 = false;
|
||||
bool have_player_4 = false;
|
||||
|
||||
if (self.connectedJoypads == nil) {
|
||||
NSArray *keys = [self.connectedJoypads allKeys];
|
||||
for (NSNumber *key in keys) {
|
||||
Joypad *joypad = [self.connectedJoypads objectForKey:key];
|
||||
GCController *controller = joypad.controller;
|
||||
if (controller.playerIndex == GCControllerPlayerIndex1) {
|
||||
have_player_1 = true;
|
||||
} else if (controller.playerIndex == GCControllerPlayerIndex2) {
|
||||
have_player_2 = true;
|
||||
} else if (controller.playerIndex == GCControllerPlayerIndex3) {
|
||||
have_player_3 = true;
|
||||
} else if (controller.playerIndex == GCControllerPlayerIndex4) {
|
||||
have_player_4 = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!have_player_1) {
|
||||
return GCControllerPlayerIndex1;
|
||||
} else if (!have_player_2) {
|
||||
return GCControllerPlayerIndex2;
|
||||
} else if (!have_player_3) {
|
||||
return GCControllerPlayerIndex3;
|
||||
} else if (!have_player_4) {
|
||||
return GCControllerPlayerIndex4;
|
||||
} else {
|
||||
return GCControllerPlayerIndexUnset;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setControllerInputHandler:(GCController *)controller {
|
||||
// Hook in the callback handler for the correct gamepad profile.
|
||||
// This is a bit of a weird design choice on Apples part.
|
||||
// You need to select the most capable gamepad profile for the
|
||||
// gamepad attached.
|
||||
if (controller.extendedGamepad != nil) {
|
||||
// The extended gamepad profile has all the input you could possibly find on
|
||||
// a gamepad but will only be active if your gamepad actually has all of
|
||||
// these...
|
||||
_weakify(self);
|
||||
_weakify(controller);
|
||||
|
||||
controller.extendedGamepad.valueChangedHandler = ^(GCExtendedGamepad *gamepad, GCControllerElement *element) {
|
||||
_strongify(self);
|
||||
_strongify(controller);
|
||||
|
||||
int joy_id = [self getJoyIdForController:controller];
|
||||
|
||||
if (element == gamepad.buttonA) {
|
||||
Input::get_singleton()->joy_button(joy_id, JoyButton::A,
|
||||
gamepad.buttonA.isPressed);
|
||||
} else if (element == gamepad.buttonB) {
|
||||
Input::get_singleton()->joy_button(joy_id, JoyButton::B,
|
||||
gamepad.buttonB.isPressed);
|
||||
} else if (element == gamepad.buttonX) {
|
||||
Input::get_singleton()->joy_button(joy_id, JoyButton::X,
|
||||
gamepad.buttonX.isPressed);
|
||||
} else if (element == gamepad.buttonY) {
|
||||
Input::get_singleton()->joy_button(joy_id, JoyButton::Y,
|
||||
gamepad.buttonY.isPressed);
|
||||
} else if (element == gamepad.leftShoulder) {
|
||||
Input::get_singleton()->joy_button(joy_id, JoyButton::LEFT_SHOULDER,
|
||||
gamepad.leftShoulder.isPressed);
|
||||
} else if (element == gamepad.rightShoulder) {
|
||||
Input::get_singleton()->joy_button(joy_id, JoyButton::RIGHT_SHOULDER,
|
||||
gamepad.rightShoulder.isPressed);
|
||||
} else if (element == gamepad.dpad) {
|
||||
Input::get_singleton()->joy_button(joy_id, JoyButton::DPAD_UP,
|
||||
gamepad.dpad.up.isPressed);
|
||||
Input::get_singleton()->joy_button(joy_id, JoyButton::DPAD_DOWN,
|
||||
gamepad.dpad.down.isPressed);
|
||||
Input::get_singleton()->joy_button(joy_id, JoyButton::DPAD_LEFT,
|
||||
gamepad.dpad.left.isPressed);
|
||||
Input::get_singleton()->joy_button(joy_id, JoyButton::DPAD_RIGHT,
|
||||
gamepad.dpad.right.isPressed);
|
||||
}
|
||||
|
||||
if (element == gamepad.leftThumbstick) {
|
||||
float value = gamepad.leftThumbstick.xAxis.value;
|
||||
Input::get_singleton()->joy_axis(joy_id, JoyAxis::LEFT_X, value);
|
||||
value = -gamepad.leftThumbstick.yAxis.value;
|
||||
Input::get_singleton()->joy_axis(joy_id, JoyAxis::LEFT_Y, value);
|
||||
} else if (element == gamepad.rightThumbstick) {
|
||||
float value = gamepad.rightThumbstick.xAxis.value;
|
||||
Input::get_singleton()->joy_axis(joy_id, JoyAxis::RIGHT_X, value);
|
||||
value = -gamepad.rightThumbstick.yAxis.value;
|
||||
Input::get_singleton()->joy_axis(joy_id, JoyAxis::RIGHT_Y, value);
|
||||
} else if (element == gamepad.leftTrigger) {
|
||||
float value = gamepad.leftTrigger.value;
|
||||
Input::get_singleton()->joy_axis(joy_id, JoyAxis::TRIGGER_LEFT, value);
|
||||
} else if (element == gamepad.rightTrigger) {
|
||||
float value = gamepad.rightTrigger.value;
|
||||
Input::get_singleton()->joy_axis(joy_id, JoyAxis::TRIGGER_RIGHT, value);
|
||||
}
|
||||
|
||||
if (@available(macOS 10.14.1, *)) {
|
||||
if (element == gamepad.leftThumbstickButton) {
|
||||
Input::get_singleton()->joy_button(joy_id, JoyButton::LEFT_STICK,
|
||||
gamepad.leftThumbstickButton.isPressed);
|
||||
} else if (element == gamepad.rightThumbstickButton) {
|
||||
Input::get_singleton()->joy_button(joy_id, JoyButton::RIGHT_STICK,
|
||||
gamepad.rightThumbstickButton.isPressed);
|
||||
}
|
||||
}
|
||||
|
||||
if (@available(macOS 10.15, *)) {
|
||||
if (element == gamepad.buttonOptions) {
|
||||
Input::get_singleton()->joy_button(joy_id, JoyButton::BACK,
|
||||
gamepad.buttonOptions.isPressed);
|
||||
} else if (element == gamepad.buttonMenu) {
|
||||
Input::get_singleton()->joy_button(joy_id, JoyButton::START,
|
||||
gamepad.buttonMenu.isPressed);
|
||||
}
|
||||
}
|
||||
|
||||
if (@available(macOS 11, *)) {
|
||||
if (element == gamepad.buttonHome) {
|
||||
Input::get_singleton()->joy_button(joy_id, JoyButton::GUIDE,
|
||||
gamepad.buttonHome.isPressed);
|
||||
}
|
||||
if ([gamepad isKindOfClass:[GCXboxGamepad class]]) {
|
||||
GCXboxGamepad *xboxGamepad = (GCXboxGamepad *)gamepad;
|
||||
if (element == xboxGamepad.paddleButton1) {
|
||||
Input::get_singleton()->joy_button(joy_id, JoyButton::PADDLE1,
|
||||
xboxGamepad.paddleButton1.isPressed);
|
||||
} else if (element == xboxGamepad.paddleButton2) {
|
||||
Input::get_singleton()->joy_button(joy_id, JoyButton::PADDLE2,
|
||||
xboxGamepad.paddleButton2.isPressed);
|
||||
} else if (element == xboxGamepad.paddleButton3) {
|
||||
Input::get_singleton()->joy_button(joy_id, JoyButton::PADDLE3,
|
||||
xboxGamepad.paddleButton3.isPressed);
|
||||
} else if (element == xboxGamepad.paddleButton4) {
|
||||
Input::get_singleton()->joy_button(joy_id, JoyButton::PADDLE4,
|
||||
xboxGamepad.paddleButton4.isPressed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (@available(macOS 12, *)) {
|
||||
if ([gamepad isKindOfClass:[GCXboxGamepad class]]) {
|
||||
GCXboxGamepad *xboxGamepad = (GCXboxGamepad *)gamepad;
|
||||
if (element == xboxGamepad.buttonShare) {
|
||||
Input::get_singleton()->joy_button(joy_id, JoyButton::MISC1,
|
||||
xboxGamepad.buttonShare.isPressed);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
} else if (controller.microGamepad != nil) {
|
||||
// Micro gamepads were added in macOS 10.11 and feature just 2 buttons and a d-pad.
|
||||
_weakify(self);
|
||||
_weakify(controller);
|
||||
|
||||
controller.microGamepad.valueChangedHandler = ^(GCMicroGamepad *gamepad, GCControllerElement *element) {
|
||||
_strongify(self);
|
||||
_strongify(controller);
|
||||
|
||||
int joy_id = [self getJoyIdForController:controller];
|
||||
|
||||
if (element == gamepad.buttonA) {
|
||||
Input::get_singleton()->joy_button(joy_id, JoyButton::A,
|
||||
gamepad.buttonA.isPressed);
|
||||
} else if (element == gamepad.buttonX) {
|
||||
Input::get_singleton()->joy_button(joy_id, JoyButton::X,
|
||||
gamepad.buttonX.isPressed);
|
||||
} else if (element == gamepad.dpad) {
|
||||
Input::get_singleton()->joy_button(joy_id, JoyButton::DPAD_UP,
|
||||
gamepad.dpad.up.isPressed);
|
||||
Input::get_singleton()->joy_button(joy_id, JoyButton::DPAD_DOWN,
|
||||
gamepad.dpad.down.isPressed);
|
||||
Input::get_singleton()->joy_button(joy_id, JoyButton::DPAD_LEFT,
|
||||
gamepad.dpad.left.isPressed);
|
||||
Input::get_singleton()->joy_button(joy_id, JoyButton::DPAD_RIGHT,
|
||||
gamepad.dpad.right.isPressed);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: Need to add support for controller.motion which gives us access to
|
||||
// the orientation of the device (if supported).
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
void JoypadMacOS::process_joypads() {
|
||||
if (@available(macOS 11, *)) {
|
||||
// Process vibrations in macOS 11+.
|
||||
NSArray *keys = [observer.connectedJoypads allKeys];
|
||||
|
||||
for (NSNumber *key in keys) {
|
||||
int id = key.intValue;
|
||||
Joypad *joypad = [observer.connectedJoypads objectForKey:key];
|
||||
|
||||
if (joypad.force_feedback) {
|
||||
Input *input = Input::get_singleton();
|
||||
uint64_t timestamp = input->get_joy_vibration_timestamp(id);
|
||||
|
||||
if (timestamp > (unsigned)joypad.ff_effect_timestamp) {
|
||||
Vector2 strength = input->get_joy_vibration_strength(id);
|
||||
float duration = input->get_joy_vibration_duration(id);
|
||||
if (duration == 0) {
|
||||
duration = GCHapticDurationInfinite;
|
||||
}
|
||||
|
||||
if (strength.x == 0 && strength.y == 0) {
|
||||
joypad_vibration_stop(joypad, timestamp);
|
||||
} else {
|
||||
joypad_vibration_start(joypad, strength.x, strength.y, duration, timestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -293,7 +293,7 @@ void NativeMenuMacOS::popup(const RID &p_rid, const Vector2i &p_position) {
|
|||
position += ds->_get_screens_origin();
|
||||
position /= ds->screen_get_max_scale();
|
||||
|
||||
[md->menu popUpMenuPositioningItem:nil atLocation:NSMakePoint(position.x, position.y) inView:nil];
|
||||
[md->menu popUpMenuPositioningItem:nil atLocation:NSMakePoint(position.x, position.y - 5) inView:nil]; // Menu vertical position doesn't include rounded corners, add `5` display pixels to better align it with Godot buttons.
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,16 +32,16 @@
|
|||
#define OS_MACOS_H
|
||||
|
||||
#include "crash_handler_macos.h"
|
||||
#import "joypad_macos.h"
|
||||
|
||||
#include "core/input/input.h"
|
||||
#import "drivers/apple/joypad_apple.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 OS_MacOS : public OS_Unix {
|
||||
JoypadMacOS *joypad_macos = nullptr;
|
||||
JoypadApple *joypad_apple = nullptr;
|
||||
|
||||
#ifdef COREAUDIO_ENABLED
|
||||
AudioDriverCoreAudio audio_driver;
|
||||
|
|
@ -82,6 +82,7 @@ public:
|
|||
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;
|
||||
|
||||
|
|
@ -92,6 +93,7 @@ public:
|
|||
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;
|
||||
|
|
@ -114,6 +116,8 @@ public:
|
|||
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 Vector<String> get_granted_permissions() const override;
|
||||
virtual void revoke_granted_permissions() override;
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ void OS_MacOS::pre_wait_observer_cb(CFRunLoopObserverRef p_observer, CFRunLoopAc
|
|||
// Do not redraw when rendering is done from the separate thread, it will conflict with the OpenGL context updates.
|
||||
|
||||
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
|
||||
if (get_singleton()->get_main_loop() && ds && (get_singleton()->get_render_thread_mode() != RENDER_SEPARATE_THREAD) && !ds->get_is_resizing()) {
|
||||
if (get_singleton()->get_main_loop() && ds && !get_singleton()->is_separate_thread_rendering_enabled() && !ds->get_is_resizing()) {
|
||||
Main::force_redraw();
|
||||
if (!Main::is_iterating()) { // Avoid cyclic loop.
|
||||
Main::iteration();
|
||||
|
|
@ -67,6 +67,15 @@ void OS_MacOS::initialize() {
|
|||
initialize_core();
|
||||
}
|
||||
|
||||
String OS_MacOS::get_model_name() const {
|
||||
char buffer[256];
|
||||
size_t buffer_len = 256;
|
||||
if (sysctlbyname("hw.model", &buffer, &buffer_len, nullptr, 0) == 0 && buffer_len != 0) {
|
||||
return String::utf8(buffer, buffer_len);
|
||||
}
|
||||
return OS_Unix::get_model_name();
|
||||
}
|
||||
|
||||
String OS_MacOS::get_processor_name() const {
|
||||
char buffer[256];
|
||||
size_t buffer_len = 256;
|
||||
|
|
@ -133,13 +142,13 @@ void OS_MacOS::finalize() {
|
|||
|
||||
delete_main_loop();
|
||||
|
||||
if (joypad_macos) {
|
||||
memdelete(joypad_macos);
|
||||
if (joypad_apple) {
|
||||
memdelete(joypad_apple);
|
||||
}
|
||||
}
|
||||
|
||||
void OS_MacOS::initialize_joypads() {
|
||||
joypad_macos = memnew(JoypadMacOS());
|
||||
joypad_apple = memnew(JoypadApple());
|
||||
}
|
||||
|
||||
void OS_MacOS::set_main_loop(MainLoop *p_main_loop) {
|
||||
|
|
@ -176,6 +185,33 @@ String OS_MacOS::get_version() const {
|
|||
return vformat("%d.%d.%d", (int64_t)ver.majorVersion, (int64_t)ver.minorVersion, (int64_t)ver.patchVersion);
|
||||
}
|
||||
|
||||
String OS_MacOS::get_version_alias() const {
|
||||
NSOperatingSystemVersion ver = [NSProcessInfo processInfo].operatingSystemVersion;
|
||||
String macos_string;
|
||||
if (ver.majorVersion == 15) {
|
||||
macos_string += "Sequoia";
|
||||
} else if (ver.majorVersion == 14) {
|
||||
macos_string += "Sonoma";
|
||||
} else if (ver.majorVersion == 13) {
|
||||
macos_string += "Ventura";
|
||||
} else if (ver.majorVersion == 12) {
|
||||
macos_string += "Monterey";
|
||||
} else if (ver.majorVersion == 11 || (ver.majorVersion == 10 && ver.minorVersion == 16)) {
|
||||
// Big Sur was 10.16 during beta, but it became 11 for the stable version.
|
||||
macos_string += "Big Sur";
|
||||
} else if (ver.majorVersion == 10 && ver.minorVersion == 15) {
|
||||
macos_string += "Catalina";
|
||||
} else if (ver.majorVersion == 10 && ver.minorVersion == 14) {
|
||||
macos_string += "Mojave";
|
||||
} else if (ver.majorVersion == 10 && ver.minorVersion == 13) {
|
||||
macos_string += "High Sierra";
|
||||
} else {
|
||||
macos_string += "Unknown";
|
||||
}
|
||||
// macOS versions older than 10.13 cannot run Godot.
|
||||
return vformat("%s (%s)", macos_string, get_version());
|
||||
}
|
||||
|
||||
void OS_MacOS::alert(const String &p_alert, const String &p_title) {
|
||||
NSAlert *window = [[NSAlert alloc] init];
|
||||
NSString *ns_title = [NSString stringWithUTF8String:p_title.utf8().get_data()];
|
||||
|
|
@ -230,7 +266,10 @@ Error OS_MacOS::open_dynamic_library(const String &p_path, void *&p_library_hand
|
|||
path = get_framework_executable(get_executable_path().get_base_dir().path_join("../Frameworks").path_join(p_path.get_file()));
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V(!FileAccess::exists(path), ERR_FILE_NOT_FOUND);
|
||||
if (!FileAccess::exists(path)) {
|
||||
// Try using path as is. macOS system libraries with `/usr/lib/*` path do not exist as physical files and are loaded from shared cache.
|
||||
path = p_path;
|
||||
}
|
||||
|
||||
p_library_handle = dlopen(path.utf8().get_data(), RTLD_NOW);
|
||||
ERR_FAIL_NULL_V_MSG(p_library_handle, ERR_CANT_OPEN, vformat("Can't open dynamic library: %s. Error: %s.", p_path, dlerror()));
|
||||
|
|
@ -264,6 +303,19 @@ String OS_MacOS::get_cache_path() const {
|
|||
return get_config_path();
|
||||
}
|
||||
|
||||
String OS_MacOS::get_temp_path() const {
|
||||
static String ret;
|
||||
if (ret.is_empty()) {
|
||||
NSURL *url = [NSURL fileURLWithPath:NSTemporaryDirectory()
|
||||
isDirectory:YES];
|
||||
if (url) {
|
||||
ret = String::utf8([url.path UTF8String]);
|
||||
ret = ret.trim_prefix("file://");
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
String OS_MacOS::get_bundle_resource_dir() const {
|
||||
String ret;
|
||||
|
||||
|
|
@ -782,7 +834,7 @@ void OS_MacOS::run() {
|
|||
if (DisplayServer::get_singleton()) {
|
||||
DisplayServer::get_singleton()->process_events(); // Get rid of pending events.
|
||||
}
|
||||
joypad_macos->start_processing();
|
||||
joypad_apple->process_joypads();
|
||||
|
||||
if (Main::iteration()) {
|
||||
quit = true;
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ RenderingContextDriver::SurfaceID RenderingContextDriverVulkanMacOS::surface_cre
|
|||
create_info.pLayer = *wpd->layer_ptr;
|
||||
|
||||
VkSurfaceKHR vk_surface = VK_NULL_HANDLE;
|
||||
VkResult err = vkCreateMetalSurfaceEXT(instance_get(), &create_info, nullptr, &vk_surface);
|
||||
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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue