feat: updated engine version to 4.4-rc1

This commit is contained in:
Sara 2025-02-23 14:38:14 +01:00
parent ee00efde1f
commit 21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions

View file

@ -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",

View file

@ -38,7 +38,7 @@ public:
void initialize();
void disable();
bool is_disabled() const { return disabled; };
bool is_disabled() const { return disabled; }
CrashHandler();
~CrashHandler();

View file

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

View file

@ -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)

View file

@ -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

View file

@ -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

View file

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

View file

@ -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) {

View file

@ -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]&lt;dict&gt;[/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]
&lt;key&gt;key_name&lt;/key&gt;
&lt;string&gt;value&lt;/string&gt;
[/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>

View file

@ -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

View file

@ -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()) {

View file

@ -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();

View file

@ -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 {

View file

@ -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];
}
}
}

View file

@ -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"

View file

@ -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;
}

View file

@ -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];
}

View file

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

View file

@ -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);
}
}
}
}
}
}

View file

@ -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.
}
}

View file

@ -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;

View file

@ -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;

View file

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