diff options
Diffstat (limited to 'platform/osx')
| -rw-r--r-- | platform/osx/audio_driver_osx.cpp | 175 | ||||
| -rw-r--r-- | platform/osx/audio_driver_osx.h | 8 | ||||
| -rw-r--r-- | platform/osx/detect.py | 70 | ||||
| -rw-r--r-- | platform/osx/export/export.cpp | 363 | ||||
| -rw-r--r-- | platform/osx/os_osx.h | 8 | ||||
| -rw-r--r-- | platform/osx/os_osx.mm | 362 |
6 files changed, 719 insertions, 267 deletions
diff --git a/platform/osx/audio_driver_osx.cpp b/platform/osx/audio_driver_osx.cpp index 7469d52976..d7a91b1653 100644 --- a/platform/osx/audio_driver_osx.cpp +++ b/platform/osx/audio_driver_osx.cpp @@ -31,11 +31,15 @@ #include "audio_driver_osx.h" -Error AudioDriverOSX::init() { +static OSStatus outputDeviceAddressCB(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *__nullable inClientData) { + AudioDriverOSX *driver = (AudioDriverOSX *)inClientData; - active = false; - channels = 2; + driver->reopen(); + return noErr; +} + +Error AudioDriverOSX::initDevice() { AudioStreamBasicDescription strdesc; strdesc.mFormatID = kAudioFormatLinearPCM; strdesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; @@ -43,12 +47,10 @@ Error AudioDriverOSX::init() { strdesc.mSampleRate = 44100; strdesc.mFramesPerPacket = 1; strdesc.mBitsPerChannel = 16; - strdesc.mBytesPerFrame = - strdesc.mBitsPerChannel * strdesc.mChannelsPerFrame / 8; - strdesc.mBytesPerPacket = - strdesc.mBytesPerFrame * strdesc.mFramesPerPacket; + strdesc.mBytesPerFrame = strdesc.mBitsPerChannel * strdesc.mChannelsPerFrame / 8; + strdesc.mBytesPerPacket = strdesc.mBytesPerFrame * strdesc.mFramesPerPacket; - OSStatus result = noErr; + OSStatus result; AURenderCallbackStruct callback; AudioComponentDescription desc; AudioComponent comp = NULL; @@ -58,83 +60,130 @@ Error AudioDriverOSX::init() { zeromem(&desc, sizeof(desc)); desc.componentType = kAudioUnitType_Output; - desc.componentSubType = 0; /* !!! FIXME: ? */ - comp = AudioComponentFindNext(NULL, &desc); + desc.componentSubType = kAudioUnitSubType_HALOutput; desc.componentManufacturer = kAudioUnitManufacturer_Apple; + comp = AudioComponentFindNext(NULL, &desc); + ERR_FAIL_COND_V(comp == NULL, FAILED); + result = AudioComponentInstanceNew(comp, &audio_unit); ERR_FAIL_COND_V(result != noErr, FAILED); - ERR_FAIL_COND_V(comp == NULL, FAILED); - result = AudioUnitSetProperty(audio_unit, - kAudioUnitProperty_StreamFormat, - scope, bus, &strdesc, sizeof(strdesc)); + result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_StreamFormat, scope, bus, &strdesc, sizeof(strdesc)); ERR_FAIL_COND_V(result != noErr, FAILED); zeromem(&callback, sizeof(AURenderCallbackStruct)); callback.inputProc = &AudioDriverOSX::output_callback; callback.inputProcRefCon = this; - result = AudioUnitSetProperty(audio_unit, - kAudioUnitProperty_SetRenderCallback, - scope, bus, &callback, sizeof(callback)); + result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_SetRenderCallback, scope, bus, &callback, sizeof(callback)); ERR_FAIL_COND_V(result != noErr, FAILED); result = AudioUnitInitialize(audio_unit); ERR_FAIL_COND_V(result != noErr, FAILED); - result = AudioOutputUnitStart(audio_unit); + return OK; +} + +Error AudioDriverOSX::finishDevice() { + OSStatus result; + + if (active) { + result = AudioOutputUnitStop(audio_unit); + ERR_FAIL_COND_V(result != noErr, FAILED); + + active = false; + } + + result = AudioUnitUninitialize(audio_unit); + ERR_FAIL_COND_V(result != noErr, FAILED); + + return OK; +} + +Error AudioDriverOSX::init() { + OSStatus result; + + mutex = Mutex::create(); + active = false; + channels = 2; + + outputDeviceAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; + outputDeviceAddress.mScope = kAudioObjectPropertyScopeGlobal; + outputDeviceAddress.mElement = kAudioObjectPropertyElementMaster; + + result = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &outputDeviceAddress, &outputDeviceAddressCB, this); ERR_FAIL_COND_V(result != noErr, FAILED); const int samples = 1024; samples_in = memnew_arr(int32_t, samples); // whatever buffer_frames = samples / channels; - return OK; + return initDevice(); }; +Error AudioDriverOSX::reopen() { + Error err; + bool restart = false; + + lock(); + + if (active) { + restart = true; + } + + err = finishDevice(); + if (err != OK) { + ERR_PRINT("finishDevice failed"); + unlock(); + return err; + } + + err = initDevice(); + if (err != OK) { + ERR_PRINT("initDevice failed"); + unlock(); + return err; + } + + if (restart) { + start(); + } + + unlock(); + + return OK; +} + OSStatus AudioDriverOSX::output_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) { - AudioBuffer *abuf; AudioDriverOSX *ad = (AudioDriverOSX *)inRefCon; - bool mix = true; - - if (!ad->active) - mix = false; - else if (ad->mutex) { - mix = ad->mutex->try_lock() == OK; - }; - - if (!mix) { + if (!ad->active || !ad->try_lock()) { for (unsigned int i = 0; i < ioData->mNumberBuffers; i++) { - abuf = &ioData->mBuffers[i]; + AudioBuffer *abuf = &ioData->mBuffers[i]; zeromem(abuf->mData, abuf->mDataByteSize); }; return 0; }; - int frames_left; - for (unsigned int i = 0; i < ioData->mNumberBuffers; i++) { - abuf = &ioData->mBuffers[i]; - frames_left = inNumberFrames; + AudioBuffer *abuf = &ioData->mBuffers[i]; + int frames_left = inNumberFrames; int16_t *out = (int16_t *)abuf->mData; while (frames_left) { int frames = MIN(frames_left, ad->buffer_frames); - //ad->lock(); ad->audio_server_process(frames, ad->samples_in); - //ad->unlock(); - for (int i = 0; i < frames * ad->channels; i++) { + for (int j = 0; j < frames * ad->channels; j++) { - out[i] = ad->samples_in[i] >> 16; + out[j] = ad->samples_in[j] >> 16; } frames_left -= frames; @@ -142,14 +191,20 @@ OSStatus AudioDriverOSX::output_callback(void *inRefCon, }; }; - if (ad->mutex) - ad->mutex->unlock(); + ad->unlock(); return 0; }; void AudioDriverOSX::start() { - active = true; + if (!active) { + OSStatus result = AudioOutputUnitStart(audio_unit); + if (result != noErr) { + ERR_PRINT("AudioOutputUnitStart failed"); + } else { + active = true; + } + } }; int AudioDriverOSX::get_mix_rate() const { @@ -161,29 +216,47 @@ AudioDriver::SpeakerMode AudioDriverOSX::get_speaker_mode() const { }; void AudioDriverOSX::lock() { - if (active && mutex) + if (mutex) mutex->lock(); }; + void AudioDriverOSX::unlock() { - if (active && mutex) + if (mutex) mutex->unlock(); }; +bool AudioDriverOSX::try_lock() { + if (mutex) + return mutex->try_lock() == OK; + return true; +} + void AudioDriverOSX::finish() { + OSStatus result; - if (active) - AudioOutputUnitStop(audio_unit); + finishDevice(); - memdelete_arr(samples_in); -}; + result = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &outputDeviceAddress, &outputDeviceAddressCB, this); + if (result != noErr) { + ERR_PRINT("AudioObjectRemovePropertyListener failed"); + } -AudioDriverOSX::AudioDriverOSX() { + if (mutex) { + memdelete(mutex); + mutex = NULL; + } - mutex = Mutex::create(); //NULL; + if (samples_in) { + memdelete_arr(samples_in); + samples_in = NULL; + } }; -AudioDriverOSX::~AudioDriverOSX(){ - +AudioDriverOSX::AudioDriverOSX() { + mutex = NULL; + samples_in = NULL; }; +AudioDriverOSX::~AudioDriverOSX(){}; + #endif diff --git a/platform/osx/audio_driver_osx.h b/platform/osx/audio_driver_osx.h index 9b48dab405..287c9d6cbf 100644 --- a/platform/osx/audio_driver_osx.h +++ b/platform/osx/audio_driver_osx.h @@ -35,10 +35,12 @@ #include "servers/audio_server.h" #include <AudioUnit/AudioUnit.h> +#include <CoreAudio/AudioHardware.h> class AudioDriverOSX : public AudioDriver { AudioComponentInstance audio_unit; + AudioObjectPropertyAddress outputDeviceAddress; bool active; Mutex *mutex; @@ -52,6 +54,9 @@ class AudioDriverOSX : public AudioDriver { UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData); + Error initDevice(); + Error finishDevice(); + public: const char *get_name() const { return "AudioUnit"; @@ -65,6 +70,9 @@ public: virtual void unlock(); virtual void finish(); + bool try_lock(); + Error reopen(); + AudioDriverOSX(); ~AudioDriverOSX(); }; diff --git a/platform/osx/detect.py b/platform/osx/detect.py index 39ee33ae82..d9891dda61 100644 --- a/platform/osx/detect.py +++ b/platform/osx/detect.py @@ -1,4 +1,3 @@ - import os import sys @@ -22,9 +21,7 @@ def can_build(): def get_opts(): return [ - ('force_64_bits', 'Force 64 bits binary', 'no'), ('osxcross_sdk', 'OSXCross SDK version', 'darwin14'), - ] @@ -36,36 +33,37 @@ def get_flags(): def configure(env): - env.Append(CPPPATH=['#platform/osx']) - - if (env["bits"] == "default"): - env["bits"] = "32" + ## Build type if (env["target"] == "release"): - - env.Append(CCFLAGS=['-O2', '-ffast-math', '-fomit-frame-pointer', '-ftree-vectorize', '-msse2']) + env.Prepend(CCFLAGS=['-O2', '-ffast-math', '-fomit-frame-pointer', '-ftree-vectorize', '-msse2']) elif (env["target"] == "release_debug"): - - env.Append(CCFLAGS=['-O2', '-DDEBUG_ENABLED']) + env.Prepend(CCFLAGS=['-O2', '-DDEBUG_ENABLED']) elif (env["target"] == "debug"): + env.Prepend(CCFLAGS=['-g3', '-DDEBUG_ENABLED', '-DDEBUG_MEMORY_ENABLED']) - env.Append(CCFLAGS=['-g3', '-DDEBUG_ENABLED', '-DDEBUG_MEMORY_ENABLED']) + ## Architecture - if (not os.environ.has_key("OSXCROSS_ROOT")): - # regular native build - if (env["bits"] == "64"): - env.Append(CCFLAGS=['-arch', 'x86_64']) - env.Append(LINKFLAGS=['-arch', 'x86_64']) + is64 = sys.maxsize > 2**32 + if (env["bits"] == "default"): + env["bits"] = "64" if is64 else "32" + + ## Compiler configuration + + if (not os.environ.has_key("OSXCROSS_ROOT")): # regular native build + if (env["bits"] == "fat"): + env.Append(CCFLAGS=['-arch', 'i386', '-arch', 'x86_64']) + env.Append(LINKFLAGS=['-arch', 'i386', '-arch', 'x86_64']) elif (env["bits"] == "32"): env.Append(CCFLAGS=['-arch', 'i386']) env.Append(LINKFLAGS=['-arch', 'i386']) - else: - env.Append(CCFLAGS=['-arch', 'i386', '-arch', 'x86_64']) - env.Append(LINKFLAGS=['-arch', 'i386', '-arch', 'x86_64']) - else: - # osxcross build + else: # 64-bit, default + env.Append(CCFLAGS=['-arch', 'x86_64']) + env.Append(LINKFLAGS=['-arch', 'x86_64']) + + else: # osxcross build root = os.environ.get("OSXCROSS_ROOT", 0) if env["bits"] == "64": basecmd = root + "/target/bin/x86_64-apple-" + env["osxcross_sdk"] + "-" @@ -78,26 +76,22 @@ def configure(env): env['RANLIB'] = basecmd + "ranlib" env['AS'] = basecmd + "as" - env.Append(CPPFLAGS=["-DAPPLE_STYLE_KEYS"]) - env.Append(CPPFLAGS=['-DUNIX_ENABLED', '-DGLES2_ENABLED', '-DOSX_ENABLED']) - env.Append(CPPFLAGS=["-mmacosx-version-min=10.9"]) - env.Append(LIBS=['pthread']) - #env.Append(CPPFLAGS=['-F/Developer/SDKs/MacOSX10.4u.sdk/System/Library/Frameworks', '-isysroot', '/Developer/SDKs/MacOSX10.4u.sdk', '-mmacosx-version-min=10.4']) - #env.Append(LINKFLAGS=['-mmacosx-version-min=10.4', '-isysroot', '/Developer/SDKs/MacOSX10.4u.sdk', '-Wl,-syslibroot,/Developer/SDKs/MacOSX10.4u.sdk']) - env.Append(LINKFLAGS=['-framework', 'Cocoa', '-framework', 'Carbon', '-framework', 'OpenGL', '-framework', 'AGL', '-framework', 'AudioUnit', '-lz', '-framework', 'IOKit', '-framework', 'ForceFeedback']) - env.Append(LINKFLAGS=["-mmacosx-version-min=10.9"]) - if (env["CXX"] == "clang++"): env.Append(CPPFLAGS=['-DTYPED_METHOD_BIND']) env["CC"] = "clang" env["LD"] = "clang++" - import methods + ## Dependencies + + if (env['builtin_libtheora'] != 'no'): + env["x86_libtheora_opt_gcc"] = True - env.Append(BUILDERS={'GLSL120': env.Builder(action=methods.build_legacygl_headers, suffix='glsl.h', src_suffix='.glsl')}) - env.Append(BUILDERS={'GLSL': env.Builder(action=methods.build_glsl_headers, suffix='glsl.h', src_suffix='.glsl')}) - env.Append(BUILDERS={'GLSL120GLES': env.Builder(action=methods.build_gles2_headers, suffix='glsl.h', src_suffix='.glsl')}) - #env.Append( BUILDERS = { 'HLSL9' : env.Builder(action = methods.build_hlsl_dx9_headers, suffix = 'hlsl.h',src_suffix = '.hlsl') } ) + ## Flags + + env.Append(CPPPATH=['#platform/osx']) + env.Append(CPPFLAGS=['-DOSX_ENABLED', '-DUNIX_ENABLED', '-DGLES2_ENABLED', '-DAPPLE_STYLE_KEYS']) + env.Append(LINKFLAGS=['-framework', 'Cocoa', '-framework', 'Carbon', '-framework', 'OpenGL', '-framework', 'AGL', '-framework', 'AudioUnit', '-framework', 'CoreAudio', '-lz', '-framework', 'IOKit', '-framework', 'ForceFeedback']) + env.Append(LIBS=['pthread']) - env["x86_libtheora_opt_gcc"] = True - + env.Append(CPPFLAGS=['-mmacosx-version-min=10.9']) + env.Append(LINKFLAGS=['-mmacosx-version-min=10.9']) diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp index 066adde780..03f424de8d 100644 --- a/platform/osx/export/export.cpp +++ b/platform/osx/export/export.cpp @@ -32,15 +32,16 @@ #include "editor/editor_export.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" -#include "global_config.h" #include "io/marshalls.h" #include "io/resource_saver.h" #include "io/zip_io.h" #include "os/file_access.h" #include "os/os.h" #include "platform/osx/logo.gen.h" +#include "project_settings.h" #include "string.h" #include "version.h" +#include <sys/stat.h> class EditorExportPlatformOSX : public EditorExportPlatform { @@ -52,6 +53,10 @@ class EditorExportPlatformOSX : public EditorExportPlatform { void _fix_plist(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &plist, const String &p_binary); void _make_icon(const Ref<Image> &p_icon, Vector<uint8_t> &p_data); +#ifdef OSX_ENABLED + Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path); + Error _create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name); +#endif protected: virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features); @@ -59,13 +64,25 @@ protected: public: virtual String get_name() const { return "Mac OSX"; } + virtual String get_os_name() const { return "OSX"; } virtual Ref<Texture> get_logo() const { return logo; } +#ifdef OSX_ENABLED + virtual String get_binary_extension() const { return "dmg"; } +#else virtual String get_binary_extension() const { return "zip"; } +#endif virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0); virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const; + virtual void get_platform_features(List<String> *r_features) { + + r_features->push_back("pc"); + r_features->push_back("s3tc"); + r_features->push_back("OSX"); + } + EditorExportPlatformOSX(); ~EditorExportPlatformOSX(); }; @@ -90,6 +107,11 @@ void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options) r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/bits_mode", PROPERTY_HINT_ENUM, "Fat (32 & 64 bits),64 bits,32 bits"), 0)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "display/high_res"), false)); + +#ifdef OSX_ENABLED + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/entitlements"), "")); +#endif } void EditorExportPlatformOSX::_make_icon(const Ref<Image> &p_icon, Vector<uint8_t> &p_data) { @@ -171,27 +193,68 @@ void EditorExportPlatformOSX::_fix_plist(const Ref<EditorExportPreset> &p_preset } CharString cs = strnew.utf8(); - plist.resize(cs.size()); - for (int i = 9; i < cs.size(); i++) { + plist.resize(cs.size() - 1); + for (int i = 0; i < cs.size() - 1; i++) { plist[i] = cs[i]; } } +#ifdef OSX_ENABLED +/** + If we're running the OSX version of the Godot editor we'll: + - export our application bundle to a temporary folder + - attempt to code sign it + - and then wrap it up in a DMG +**/ + +Error EditorExportPlatformOSX::_code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path) { + List<String> args; + if (p_preset->get("codesign/entitlements") != "") { + /* this should point to our entitlements.plist file that sandboxes our application, I don't know if this should also be placed in our app bundle */ + args.push_back("-entitlements"); + args.push_back(p_preset->get("codesign/entitlements")); + } + args.push_back("-s"); + args.push_back(p_preset->get("codesign/identity")); + args.push_back("-v"); /* provide some more feedback */ + args.push_back(p_path); + Error err = OS::get_singleton()->execute("/usr/bin/codesign", args, true); + ERR_FAIL_COND_V(err, err); + + return OK; +} + +Error EditorExportPlatformOSX::_create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name) { + List<String> args; + args.push_back("create"); + args.push_back(p_dmg_path); + args.push_back("-volname"); + args.push_back(p_pkg_name); + args.push_back("-fs"); + args.push_back("HFS+"); + args.push_back("-srcfolder"); + args.push_back(p_app_path_name); + Error err = OS::get_singleton()->execute("/usr/bin/hdiutil", args, true); + ERR_FAIL_COND_V(err, err); + + return OK; +} + Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { - String src_pkg; + String src_pkg_name; - EditorProgress ep("export", "Exporting for OSX", 104); + EditorProgress ep("export", "Exporting for OSX", 3); if (p_debug) - src_pkg = p_preset->get("custom_package/debug"); + src_pkg_name = p_preset->get("custom_package/debug"); else - src_pkg = p_preset->get("custom_package/release"); + src_pkg_name = p_preset->get("custom_package/release"); - if (src_pkg == "") { + if (src_pkg_name == "") { String err; - src_pkg = find_export_template("osx.zip", &err); - if (src_pkg == "") { + src_pkg_name = find_export_template("osx.zip", &err); + if (src_pkg_name == "") { EditorNode::add_io_error(err); return ERR_FILE_NOT_FOUND; } @@ -202,20 +265,15 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p ep.step("Creating app", 0); - unzFile pkg = unzOpen2(src_pkg.utf8().get_data(), &io); - if (!pkg) { + unzFile src_pkg_zip = unzOpen2(src_pkg_name.utf8().get_data(), &io); + if (!src_pkg_zip) { - EditorNode::add_io_error("Could not find template app to export:\n" + src_pkg); + EditorNode::add_io_error("Could not find template app to export:\n" + src_pkg_name); return ERR_FILE_NOT_FOUND; } - ERR_FAIL_COND_V(!pkg, ERR_CANT_OPEN); - int ret = unzGoToFirstFile(pkg); - - zlib_filefunc_def io2 = io; - FileAccess *dst_f = NULL; - io2.opaque = &dst_f; - zipFile dpkg = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, NULL, &io2); + ERR_FAIL_COND_V(!src_pkg_zip, ERR_CANT_OPEN); + int ret = unzGoToFirstFile(src_pkg_zip); String binary_to_use = "godot_osx_" + String(p_debug ? "debug" : "release") + "."; int bits_mode = p_preset->get("application/bits_mode"); @@ -225,19 +283,39 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p String pkg_name; if (p_preset->get("application/name") != "") pkg_name = p_preset->get("application/name"); // app_name - else if (String(GlobalConfig::get_singleton()->get("application/name")) != "") - pkg_name = String(GlobalConfig::get_singleton()->get("application/name")); + else if (String(ProjectSettings::get_singleton()->get("application/config/name")) != "") + pkg_name = String(ProjectSettings::get_singleton()->get("application/config/name")); else pkg_name = "Unnamed"; + // We're on OSX so we can export to DMG, but first we create our application bundle + String tmp_app_path_name = p_path.get_base_dir() + "/" + pkg_name + ".app"; + print_line("Exporting to " + tmp_app_path_name); + DirAccess *tmp_app_path = DirAccess::create_for_path(tmp_app_path_name); + ERR_FAIL_COND_V(!tmp_app_path, ERR_CANT_CREATE) + + ///@TODO We should delete the existing application bundle especially if we attempt to code sign it, but what is a safe way to do this? Maybe call system function so it moves to trash? + // tmp_app_path->erase_contents_recursive(); + + // Create our folder structure or rely on unzip? + print_line("Creating " + tmp_app_path_name + "/Contents/MacOS"); + Error dir_err = tmp_app_path->make_dir_recursive(tmp_app_path_name + "/Contents/MacOS"); + ERR_FAIL_COND_V(dir_err, ERR_CANT_CREATE) + print_line("Creating " + tmp_app_path_name + "/Contents/Resources"); + dir_err = tmp_app_path->make_dir_recursive(tmp_app_path_name + "/Contents/Resources"); + ERR_FAIL_COND_V(dir_err, ERR_CANT_CREATE) + + /* Now process our template */ bool found_binary = false; + int total_size = 0; while (ret == UNZ_OK) { + bool is_execute = false; //get filename unz_file_info info; char fname[16384]; - ret = unzGetCurrentFileInfo(pkg, &info, fname, 16384, NULL, 0, NULL, 0); + ret = unzGetCurrentFileInfo(src_pkg_zip, &info, fname, 16384, NULL, 0, NULL, 0); String file = fname; @@ -246,9 +324,9 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p data.resize(info.uncompressed_size); //read - unzOpenCurrentFile(pkg); - unzReadCurrentFile(pkg, data.ptr(), data.size()); - unzCloseCurrentFile(pkg); + unzOpenCurrentFile(src_pkg_zip); + unzReadCurrentFile(src_pkg_zip, data.ptr(), data.size()); + unzCloseCurrentFile(src_pkg_zip); //write @@ -261,10 +339,11 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p if (file.begins_with("Contents/MacOS/godot_")) { if (file != "Contents/MacOS/" + binary_to_use) { - ret = unzGoToNextFile(pkg); + ret = unzGoToNextFile(src_pkg_zip); continue; //ignore! } found_binary = true; + is_execute = true; file = "Contents/MacOS/" + pkg_name; } @@ -274,7 +353,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p if (p_preset->get("application/icon") != "") iconpath = p_preset->get("application/icon"); else - iconpath = GlobalConfig::get_singleton()->get("application/icon"); + iconpath = ProjectSettings::get_singleton()->get("application/config/icon"); print_line("icon? " + iconpath); if (iconpath != "") { Ref<Image> icon; @@ -288,11 +367,206 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p //bleh? } - file = pkg_name + ".app/" + file; + if (data.size() > 0) { + print_line("ADDING: " + file + " size: " + itos(data.size())); + total_size += data.size(); + + /* write it into our application bundle */ + file = tmp_app_path_name + "/" + file; + + /* write the file, need to add chmod */ + FileAccess *f = FileAccess::open(file, FileAccess::WRITE); + ERR_FAIL_COND_V(!f, ERR_CANT_CREATE) + f->store_buffer(data.ptr(), data.size()); + f->close(); + memdelete(f); + + if (is_execute) { + // we need execute rights on this file + chmod(file.utf8().get_data(), 0755); + } else { + // seems to already be set correctly + // chmod(file.utf8().get_data(), 0644); + } + } + + ret = unzGoToNextFile(src_pkg_zip); + } + + /* we're done with our source zip */ + unzClose(src_pkg_zip); + + if (!found_binary) { + ERR_PRINTS("Requested template binary '" + binary_to_use + "' not found. It might be missing from your template archive."); + unzClose(src_pkg_zip); + return ERR_FILE_NOT_FOUND; + } + + ep.step("Making PKG", 1); + + String pack_path = tmp_app_path_name + "/Contents/Resources/" + pkg_name + ".pck"; + Error err = save_pack(p_preset, pack_path); + // chmod(pack_path.utf8().get_data(), 0644); + + if (err) { + return err; + } + + /* see if we can code sign our new package */ + if (p_preset->get("codesign/identity") != "") { + ep.step("Code signing bundle", 2); + + /* the order in which we code sign is important, this is a bit of a shame or we could do this in our loop that extracts the files from our ZIP */ + + // start with our application + err = _code_sign(p_preset, tmp_app_path_name + "/Contents/MacOS/" + pkg_name); + ERR_FAIL_COND_V(err, err); + + ///@TODO we should check the contents of /Contents/Frameworks for frameworks to sign + + // we should probably loop through all resources and sign them? + err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Resources/icon.icns"); + ERR_FAIL_COND_V(err, err); + err = _code_sign(p_preset, pack_path); + ERR_FAIL_COND_V(err, err); + err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Info.plist"); + ERR_FAIL_COND_V(err, err); + } + + /* and finally create a DMG */ + ep.step("Making DMG", 3); + err = _create_dmg(p_path, pkg_name, tmp_app_path_name); + ERR_FAIL_COND_V(err, err); + + return OK; +} + +#else + +/** + When exporting for OSX from any other platform we don't have access to code signing or creating DMGs so we'll wrap the bundle into a zip file. + + Should probably find a nicer way to have just one export method instead of duplicating the method like this but I would the code got very + messy with switches inside of it. +**/ +Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { + + String src_pkg_name; + + EditorProgress ep("export", "Exporting for OSX", 104); + + if (p_debug) + src_pkg_name = p_preset->get("custom_package/debug"); + else + src_pkg_name = p_preset->get("custom_package/release"); + + if (src_pkg_name == "") { + String err; + src_pkg_name = find_export_template("osx.zip", &err); + if (src_pkg_name == "") { + EditorNode::add_io_error(err); + return ERR_FILE_NOT_FOUND; + } + } + + FileAccess *src_f = NULL; + zlib_filefunc_def io = zipio_create_io_from_file(&src_f); + + ep.step("Creating app", 0); + + unzFile src_pkg_zip = unzOpen2(src_pkg_name.utf8().get_data(), &io); + if (!src_pkg_zip) { + + EditorNode::add_io_error("Could not find template app to export:\n" + src_pkg_name); + return ERR_FILE_NOT_FOUND; + } + + ERR_FAIL_COND_V(!src_pkg_zip, ERR_CANT_OPEN); + int ret = unzGoToFirstFile(src_pkg_zip); + + String binary_to_use = "godot_osx_" + String(p_debug ? "debug" : "release") + "."; + int bits_mode = p_preset->get("application/bits_mode"); + binary_to_use += String(bits_mode == 0 ? "fat" : bits_mode == 1 ? "64" : "32"); + + print_line("binary: " + binary_to_use); + String pkg_name; + if (p_preset->get("application/name") != "") + pkg_name = p_preset->get("application/name"); // app_name + else if (String(ProjectSettings::get_singleton()->get("application/config/name")) != "") + pkg_name = String(ProjectSettings::get_singleton()->get("application/config/name")); + else + pkg_name = "Unnamed"; + + /* Open our destination zip file */ + zlib_filefunc_def io2 = io; + FileAccess *dst_f = NULL; + io2.opaque = &dst_f; + zipFile dst_pkg_zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, NULL, &io2); + + bool found_binary = false; + + while (ret == UNZ_OK) { + + //get filename + unz_file_info info; + char fname[16384]; + ret = unzGetCurrentFileInfo(src_pkg_zip, &info, fname, 16384, NULL, 0, NULL, 0); + + String file = fname; + + print_line("READ: " + file); + Vector<uint8_t> data; + data.resize(info.uncompressed_size); + + //read + unzOpenCurrentFile(src_pkg_zip); + unzReadCurrentFile(src_pkg_zip, data.ptr(), data.size()); + unzCloseCurrentFile(src_pkg_zip); + + //write + + file = file.replace_first("osx_template.app/", ""); + + if (file == "Contents/Info.plist") { + print_line("parse plist"); + _fix_plist(p_preset, data, pkg_name); + } + + if (file.begins_with("Contents/MacOS/godot_")) { + if (file != "Contents/MacOS/" + binary_to_use) { + ret = unzGoToNextFile(src_pkg_zip); + continue; //ignore! + } + found_binary = true; + file = "Contents/MacOS/" + pkg_name; + } + + if (file == "Contents/Resources/icon.icns") { + //see if there is an icon + String iconpath; + if (p_preset->get("application/icon") != "") + iconpath = p_preset->get("application/icon"); + else + iconpath = ProjectSettings::get_singleton()->get("application/config/icon"); + print_line("icon? " + iconpath); + if (iconpath != "") { + Ref<Image> icon; + icon.instance(); + icon->load(iconpath); + if (!icon->empty()) { + print_line("loaded?"); + _make_icon(icon, data); + } + } + //bleh? + } if (data.size() > 0) { print_line("ADDING: " + file + " size: " + itos(data.size())); + /* add it to our zip file */ + file = pkg_name + ".app/" + file; + zip_fileinfo fi; fi.tmz_date.tm_hour = info.tmu_date.tm_hour; fi.tmz_date.tm_min = info.tmu_date.tm_min; @@ -304,7 +578,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p fi.internal_fa = info.internal_fa; fi.external_fa = info.external_fa; - int err = zipOpenNewFileInZip(dpkg, + int err = zipOpenNewFileInZip(dst_pkg_zip, file.utf8().get_data(), &fi, NULL, @@ -316,37 +590,37 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p Z_DEFAULT_COMPRESSION); print_line("OPEN ERR: " + itos(err)); - err = zipWriteInFileInZip(dpkg, data.ptr(), data.size()); + err = zipWriteInFileInZip(dst_pkg_zip, data.ptr(), data.size()); print_line("WRITE ERR: " + itos(err)); - zipCloseFileInZip(dpkg); + zipCloseFileInZip(dst_pkg_zip); } - ret = unzGoToNextFile(pkg); + ret = unzGoToNextFile(src_pkg_zip); } if (!found_binary) { ERR_PRINTS("Requested template binary '" + binary_to_use + "' not found. It might be missing from your template archive."); - zipClose(dpkg, NULL); - unzClose(pkg); + zipClose(dst_pkg_zip, NULL); + unzClose(src_pkg_zip); return ERR_FILE_NOT_FOUND; } ep.step("Making PKG", 1); - String pack_path = EditorSettings::get_singleton()->get_settings_path() + "/tmp/data.pck"; + String pack_path = EditorSettings::get_singleton()->get_settings_path() + "/tmp/" + pkg_name + ".pck"; Error err = save_pack(p_preset, pack_path); if (err) { - zipClose(dpkg, NULL); - unzClose(pkg); + zipClose(dst_pkg_zip, NULL); + unzClose(src_pkg_zip); return err; } { //write datapack - zipOpenNewFileInZip(dpkg, - (pkg_name + ".app/Contents/Resources/data.pck").utf8().get_data(), + zipOpenNewFileInZip(dst_pkg_zip, + (pkg_name + ".app/Contents/Resources/" + pkg_name + ".pck").utf8().get_data(), NULL, NULL, 0, @@ -366,17 +640,18 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p int r = pf->get_buffer(buf, BSIZE); if (r <= 0) break; - zipWriteInFileInZip(dpkg, buf, r); + zipWriteInFileInZip(dst_pkg_zip, buf, r); } - zipCloseFileInZip(dpkg); + zipCloseFileInZip(dst_pkg_zip); memdelete(pf); } - zipClose(dpkg, NULL); - unzClose(pkg); + zipClose(dst_pkg_zip, NULL); + unzClose(src_pkg_zip); return OK; } +#endif bool EditorExportPlatformOSX::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h index dc3e88df2c..cb9dd1dd8e 100644 --- a/platform/osx/os_osx.h +++ b/platform/osx/os_osx.h @@ -101,11 +101,7 @@ public: bool maximized; bool zoomed; - Vector<Rect2> screens; - Vector<int> screen_dpi; - Size2 window_size; - int current_screen; Rect2 restore_rect; power_osx *power_manager; @@ -117,6 +113,8 @@ public: return 1.0; } + void _update_window(); + float display_scale; protected: @@ -208,6 +206,8 @@ public: virtual int get_power_seconds_left(); virtual int get_power_percent_left(); + virtual bool _check_internal_feature_support(const String &p_feature); + void run(); void set_mouse_mode(MouseMode p_mode); diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index ad1e308ae0..4a01532d89 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -80,6 +80,7 @@ static int mouse_y = 0; static int prev_mouse_x = 0; static int prev_mouse_y = 0; static int button_mask = 0; +static bool mouse_down_control = false; @interface GodotApplication : NSApplication @end @@ -151,6 +152,46 @@ static int button_mask = 0; return NO; } +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 +- (void)windowDidEnterFullScreen:(NSNotification *)notification { + OS_OSX::singleton->zoomed = true; +} + +- (void)windowDidExitFullScreen:(NSNotification *)notification { + OS_OSX::singleton->zoomed = false; +} +#endif // MAC_OS_X_VERSION_MAX_ALLOWED + +- (void)windowDidChangeBackingProperties:(NSNotification *)notification { + if (!OS_OSX::singleton) + return; + + NSWindow *window = (NSWindow *)[notification object]; + CGFloat newBackingScaleFactor = [window backingScaleFactor]; + CGFloat oldBackingScaleFactor = [[[notification userInfo] objectForKey:@"NSBackingPropertyOldScaleFactorKey"] doubleValue]; + + if (newBackingScaleFactor != oldBackingScaleFactor) { + //Set new display scale and window size + OS_OSX::singleton->display_scale = newBackingScaleFactor; + + const NSRect contentRect = [OS_OSX::singleton->window_view frame]; + const NSRect fbRect = contentRect; //convertRectToBacking(contentRect); + + OS_OSX::singleton->window_size.width = fbRect.size.width * OS_OSX::singleton->display_scale; + OS_OSX::singleton->window_size.height = fbRect.size.height * OS_OSX::singleton->display_scale; + + //Update context + if (OS_OSX::singleton->main_loop) { + [OS_OSX::singleton->context update]; + + //Force window resize ??? + NSRect frame = [OS_OSX::singleton->window_object frame]; + [OS_OSX::singleton->window_object setFrame:NSMakeRect(frame.origin.x, frame.origin.y, 1, 1) display:YES]; + [OS_OSX::singleton->window_object setFrame:frame display:YES]; + } + } +} + - (void)windowDidResize:(NSNotification *)notification { [OS_OSX::singleton->context update]; @@ -160,6 +201,12 @@ static int button_mask = 0; OS_OSX::singleton->window_size.width = fbRect.size.width * OS_OSX::singleton->display_scale; OS_OSX::singleton->window_size.height = fbRect.size.height * OS_OSX::singleton->display_scale; + if (OS_OSX::singleton->main_loop) { + Main::force_redraw(); + //Event retrieval blocks until resize is over. Call Main::iteration() directly. + Main::iteration(); + } + /* _GodotInputFramebufferSize(window, fbRect.size.width, fbRect.size.height); _GodotInputWindowSize(window, contentRect.size.width, contentRect.size.height); @@ -285,41 +332,48 @@ static int button_mask = 0; //setModeCursor(window, window->cursorMode); } -- (void)mouseDown:(NSEvent *)event { - - button_mask |= BUTTON_MASK_LEFT; +static void _mouseDownEvent(NSEvent *event, int index, int mask, bool pressed) { + if (pressed) { + button_mask |= mask; + } else { + button_mask &= ~mask; + } Ref<InputEventMouseButton> mb; mb.instance(); get_key_modifier_state([event modifierFlags], mb); - mb->set_button_index(BUTTON_LEFT); - mb->set_pressed(true); + mb->set_button_index(index); + mb->set_pressed(pressed); mb->set_position(Vector2(mouse_x, mouse_y)); mb->set_global_position(Vector2(mouse_x, mouse_y)); mb->set_button_mask(button_mask); - mb->set_doubleclick([event clickCount] == 2); + if (index == BUTTON_LEFT && pressed) { + mb->set_doubleclick([event clickCount] == 2); + } OS_OSX::singleton->push_input(mb); } +- (void)mouseDown:(NSEvent *)event { + if (([event modifierFlags] & NSControlKeyMask)) { + mouse_down_control = true; + _mouseDownEvent(event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, true); + } else { + mouse_down_control = false; + _mouseDownEvent(event, BUTTON_LEFT, BUTTON_MASK_LEFT, true); + } +} + - (void)mouseDragged:(NSEvent *)event { [self mouseMoved:event]; } - (void)mouseUp:(NSEvent *)event { - - button_mask &= ~BUTTON_MASK_LEFT; - Ref<InputEventMouseButton> mb; - mb.instance(); - - get_key_modifier_state([event modifierFlags], mb); - mb->set_button_index(BUTTON_LEFT); - mb->set_pressed(false); - mb->set_position(Vector2(mouse_x, mouse_y)); - mb->set_global_position(Vector2(mouse_x, mouse_y)); - mb->set_button_mask(button_mask); - mb->set_doubleclick([event clickCount] == 2); - OS_OSX::singleton->push_input(mb); + if (mouse_down_control) { + _mouseDownEvent(event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, false); + } else { + _mouseDownEvent(event, BUTTON_LEFT, BUTTON_MASK_LEFT, false); + } } - (void)mouseMoved:(NSEvent *)event { @@ -347,20 +401,7 @@ static int button_mask = 0; } - (void)rightMouseDown:(NSEvent *)event { - - button_mask |= BUTTON_MASK_RIGHT; - - Ref<InputEventMouseButton> mb; - mb.instance(); - - get_key_modifier_state([event modifierFlags], mb); - mb->set_button_index(BUTTON_RIGHT); - mb->set_pressed(true); - mb->set_position(Vector2(mouse_x, mouse_y)); - mb->set_global_position(Vector2(mouse_x, mouse_y)); - mb->set_button_mask(button_mask); - mb->set_doubleclick([event clickCount] == 2); - OS_OSX::singleton->push_input(mb); + _mouseDownEvent(event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, true); } - (void)rightMouseDragged:(NSEvent *)event { @@ -368,20 +409,7 @@ static int button_mask = 0; } - (void)rightMouseUp:(NSEvent *)event { - - button_mask &= ~BUTTON_MASK_RIGHT; - - Ref<InputEventMouseButton> mb; - mb.instance(); - - get_key_modifier_state([event modifierFlags], mb); - mb->set_button_index(BUTTON_RIGHT); - mb->set_pressed(false); - mb->set_position(Vector2(mouse_x, mouse_y)); - mb->set_global_position(Vector2(mouse_x, mouse_y)); - mb->set_button_mask(button_mask); - mb->set_doubleclick([event clickCount] == 2); - OS_OSX::singleton->push_input(mb); + _mouseDownEvent(event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, false); } - (void)otherMouseDown:(NSEvent *)event { @@ -389,19 +417,7 @@ static int button_mask = 0; if ((int)[event buttonNumber] != 2) return; - button_mask |= BUTTON_MASK_MIDDLE; - - Ref<InputEventMouseButton> mb; - mb.instance(); - - get_key_modifier_state([event modifierFlags], mb); - mb->set_button_index(BUTTON_MIDDLE); - mb->set_pressed(true); - mb->set_position(Vector2(mouse_x, mouse_y)); - mb->set_global_position(Vector2(mouse_x, mouse_y)); - mb->set_button_mask(button_mask); - mb->set_doubleclick([event clickCount] == 2); - OS_OSX::singleton->push_input(mb); + _mouseDownEvent(event, BUTTON_MIDDLE, BUTTON_MASK_MIDDLE, true); } - (void)otherMouseDragged:(NSEvent *)event { @@ -413,19 +429,7 @@ static int button_mask = 0; if ((int)[event buttonNumber] != 2) return; - button_mask &= ~BUTTON_MASK_MIDDLE; - - Ref<InputEventMouseButton> mb; - mb.instance(); - - get_key_modifier_state([event modifierFlags], mb); - mb->set_button_index(BUTTON_MIDDLE); - mb->set_pressed(false); - mb->set_position(Vector2(mouse_x, mouse_y)); - mb->set_global_position(Vector2(mouse_x, mouse_y)); - mb->set_button_mask(button_mask); - mb->set_doubleclick([event clickCount] == 2); - OS_OSX::singleton->push_input(mb); + _mouseDownEvent(event, BUTTON_MIDDLE, BUTTON_MASK_MIDDLE, false); } - (void)mouseExited:(NSEvent *)event { @@ -554,7 +558,7 @@ static int translateKey(unsigned int key) { /* 49 */ KEY_UNKNOWN, /* VolumeDown */ /* 4a */ KEY_UNKNOWN, /* Mute */ /* 4b */ KEY_KP_DIVIDE, - /* 4c */ KEY_KP_ENTER, + /* 4c */ KEY_ENTER, /* 4d */ KEY_UNKNOWN, /* 4e */ KEY_KP_SUBTRACT, /* 4f */ KEY_UNKNOWN, @@ -921,6 +925,8 @@ void OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_au [NSApp activateIgnoringOtherApps:YES]; + _update_window(); + [window_object makeKeyAndOrderFront:nil]; if (p_desired.fullscreen) @@ -970,32 +976,6 @@ void OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_au _ensure_data_dir(); - NSArray *screenArray = [NSScreen screens]; - printf("nscreen count %i\n", (int)[screenArray count]); - for (int i = 0; i < [screenArray count]; i++) { - - float displayScale = 1.0; - - if (display_scale > 1.0 && [[screenArray objectAtIndex:i] respondsToSelector:@selector(backingScaleFactor)]) { - displayScale = [[screenArray objectAtIndex:i] backingScaleFactor]; - } - - NSRect nsrect = [[screenArray objectAtIndex:i] visibleFrame]; - Rect2 rect = Rect2(nsrect.origin.x, nsrect.origin.y, nsrect.size.width, nsrect.size.height); - rect.position *= displayScale; - rect.size *= displayScale; - screens.push_back(rect); - - NSDictionary *description = [[screenArray objectAtIndex:i] deviceDescription]; - NSSize displayPixelSize = [[description objectForKey:NSDeviceSize] sizeValue]; - CGSize displayPhysicalSize = CGDisplayScreenSize( - [[description objectForKey:@"NSScreenNumber"] unsignedIntValue]); - - //printf("width: %i pwidth %i rect width %i\n",int(displayPixelSize.width*displayScale),int(displayPhysicalSize.width*displayScale),int(nsrect.size.width)); - int dpi = (displayPixelSize.width * 25.4f / displayPhysicalSize.width) * displayScale; - - screen_dpi.push_back(dpi); - }; restore_rect = Rect2(get_window_position(), get_window_size()); } @@ -1016,8 +996,6 @@ void OS_OSX::finalize() { physics_2d_server->finish(); memdelete(physics_2d_server); - - screens.clear(); } void OS_OSX::set_main_loop(MainLoop *p_main_loop) { @@ -1267,37 +1245,105 @@ void OS_OSX::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) con } int OS_OSX::get_screen_count() const { - - return screens.size(); + NSArray *screenArray = [NSScreen screens]; + return [screenArray count]; }; int OS_OSX::get_current_screen() const { - - return current_screen; + Vector2 wpos = get_window_position(); + + int count = get_screen_count(); + for (int i = 0; i < count; i++) { + Point2 pos = get_screen_position(i); + Size2 size = get_screen_size(i); + if ((wpos.x >= pos.x && wpos.x < pos.x + size.width) && (wpos.y >= pos.y && wpos.y < pos.y + size.height)) + return i; + } + return 0; }; void OS_OSX::set_current_screen(int p_screen) { - - current_screen = p_screen; + Vector2 wpos = get_window_position() - get_screen_position(get_current_screen()); + set_window_position(wpos + get_screen_position(p_screen)); }; Point2 OS_OSX::get_screen_position(int p_screen) const { + NSArray *screenArray = [NSScreen screens]; + if (p_screen < [screenArray count]) { + float displayScale = 1.0; - ERR_FAIL_INDEX_V(p_screen, screens.size(), Point2()); - return screens[p_screen].position; -}; + if (display_scale > 1.0 && [[screenArray objectAtIndex:p_screen] respondsToSelector:@selector(backingScaleFactor)]) { + displayScale = [[screenArray objectAtIndex:p_screen] backingScaleFactor]; + } + + NSRect nsrect = [[screenArray objectAtIndex:p_screen] frame]; + return Point2(nsrect.origin.x, nsrect.origin.y) * displayScale; + } + + return Point2(); +} int OS_OSX::get_screen_dpi(int p_screen) const { + NSArray *screenArray = [NSScreen screens]; + if (p_screen < [screenArray count]) { + float displayScale = 1.0; + + if (display_scale > 1.0 && [[screenArray objectAtIndex:p_screen] respondsToSelector:@selector(backingScaleFactor)]) { + displayScale = [[screenArray objectAtIndex:p_screen] backingScaleFactor]; + } + + NSDictionary *description = [[screenArray objectAtIndex:p_screen] deviceDescription]; + NSSize displayPixelSize = [[description objectForKey:NSDeviceSize] sizeValue]; + CGSize displayPhysicalSize = CGDisplayScreenSize( + [[description objectForKey:@"NSScreenNumber"] unsignedIntValue]); + + return (displayPixelSize.width * 25.4f / displayPhysicalSize.width) * displayScale; + } - ERR_FAIL_INDEX_V(p_screen, screens.size(), 72); - return screen_dpi[p_screen]; + return 72; } Size2 OS_OSX::get_screen_size(int p_screen) const { + NSArray *screenArray = [NSScreen screens]; + if (p_screen < [screenArray count]) { + float displayScale = 1.0; - ERR_FAIL_INDEX_V(p_screen, screens.size(), Point2()); - return screens[p_screen].size; -}; + if (display_scale > 1.0 && [[screenArray objectAtIndex:p_screen] respondsToSelector:@selector(backingScaleFactor)]) { + displayScale = [[screenArray objectAtIndex:p_screen] backingScaleFactor]; + } + + // Note: Use frame to get the whole screen size + NSRect nsrect = [[screenArray objectAtIndex:p_screen] frame]; + return Size2(nsrect.size.width, nsrect.size.height) * displayScale; + } + + return Size2(); +} + +void OS_OSX::_update_window() { + bool borderless_full = false; + + if (get_borderless_window()) { + NSRect frameRect = [window_object frame]; + NSRect screenRect = [[window_object screen] frame]; + + // Check if our window covers up the screen + if (frameRect.origin.x <= screenRect.origin.x && frameRect.origin.y <= frameRect.origin.y && + frameRect.size.width >= screenRect.size.width && frameRect.size.height >= screenRect.size.height) { + borderless_full = true; + } + } + + if (borderless_full) { + // If the window covers up the screen set the level to above the main menu and hide on deactivate + [window_object setLevel:NSMainMenuWindowLevel + 1]; + [window_object setHidesOnDeactivate:YES]; + } else { + // Reset these when our window is not a borderless window that covers up the screen + [window_object setLevel:NSNormalWindowLevel]; + [window_object setHidesOnDeactivate:NO]; + } +} Point2 OS_OSX::get_window_position() const { @@ -1311,6 +1357,8 @@ void OS_OSX::set_window_position(const Point2 &p_position) { Point2 size = p_position; size /= display_scale; [window_object setFrame:NSMakeRect(size.x, size.y, [window_object frame].size.width, [window_object frame].size.height) display:YES]; + + _update_window(); }; Size2 OS_OSX::get_window_size() const { @@ -1322,17 +1370,22 @@ void OS_OSX::set_window_size(const Size2 p_size) { Size2 size = p_size; - // NSRect used by setFrame includes the title bar, so add it to our size.y - CGFloat menuBarHeight = [[[NSApplication sharedApplication] mainMenu] menuBarHeight]; - if (menuBarHeight != 0.f) { - size.y += menuBarHeight; + if (get_borderless_window() == false) { + // NSRect used by setFrame includes the title bar, so add it to our size.y + CGFloat menuBarHeight = [[[NSApplication sharedApplication] mainMenu] menuBarHeight]; + if (menuBarHeight != 0.f) { + size.y += menuBarHeight; #if MAC_OS_X_VERSION_MAX_ALLOWED <= 101104 - } else { - size.y += [[NSStatusBar systemStatusBar] thickness]; + } else { + size.y += [[NSStatusBar systemStatusBar] thickness]; #endif + } } + NSRect frame = [window_object frame]; [window_object setFrame:NSMakeRect(frame.origin.x, frame.origin.y, size.x, size.y) display:YES]; + + _update_window(); }; void OS_OSX::set_window_fullscreen(bool p_enabled) { @@ -1390,7 +1443,7 @@ void OS_OSX::set_window_maximized(bool p_enabled) { if (p_enabled) { restore_rect = Rect2(get_window_position(), get_window_size()); - [window_object setFrame:[[[NSScreen screens] objectAtIndex:current_screen] visibleFrame] display:YES]; + [window_object setFrame:[[[NSScreen screens] objectAtIndex:get_current_screen()] visibleFrame] display:YES]; } else { set_window_size(restore_rect.size); set_window_position(restore_rect.position); @@ -1416,9 +1469,12 @@ void OS_OSX::request_attention() { void OS_OSX::set_borderless_window(int p_borderless) { - if (p_borderless) + // OrderOut prevents a lose focus bug with the window + [window_object orderOut:nil]; + + if (p_borderless) { [window_object setStyleMask:NSWindowStyleMaskBorderless]; - else { + } else { [window_object setStyleMask:NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask]; // Force update of the window styles @@ -1429,6 +1485,10 @@ void OS_OSX::set_borderless_window(int p_borderless) { // Restore the window title [window_object setTitle:[NSString stringWithUTF8String:title.utf8().get_data()]]; } + + _update_window(); + + [window_object makeKeyAndOrderFront:nil]; } bool OS_OSX::get_borderless_window() { @@ -1661,12 +1721,52 @@ OS_OSX::OS_OSX() { // In case we are unbundled, make us a proper UI application [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; -#if 0 // Menu bar setup must go between sharedApplication above and // finishLaunching below, in order to properly emulate the behavior // of NSApplicationMain - createMenuBar(); -#endif + NSMenuItem *menu_item; + NSString *title; + + NSString *nsappname = [[[NSBundle mainBundle] performSelector:@selector(localizedInfoDictionary)] objectForKey:@"CFBundleName"]; + if (nsappname == nil) + nsappname = [[NSProcessInfo processInfo] processName]; + + // Setup Apple menu + NSMenu *apple_menu = [[NSMenu alloc] initWithTitle:@""]; + title = [NSString stringWithFormat:NSLocalizedString(@"About %@", nil), nsappname]; + [apple_menu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; + + [apple_menu addItem:[NSMenuItem separatorItem]]; + + NSMenu *services = [[NSMenu alloc] initWithTitle:@""]; + menu_item = [apple_menu addItemWithTitle:NSLocalizedString(@"Services", nil) action:nil keyEquivalent:@""]; + [apple_menu setSubmenu:services forItem:menu_item]; + [NSApp setServicesMenu:services]; + [services release]; + + [apple_menu addItem:[NSMenuItem separatorItem]]; + + title = [NSString stringWithFormat:NSLocalizedString(@"Hide %@", nil), nsappname]; + [apple_menu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"]; + + menu_item = [apple_menu addItemWithTitle:NSLocalizedString(@"Hide Others", nil) action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; + [menu_item setKeyEquivalentModifierMask:(NSAlternateKeyMask | NSCommandKeyMask)]; + + [apple_menu addItemWithTitle:NSLocalizedString(@"Show all", nil) action:@selector(unhideAllApplications:) keyEquivalent:@""]; + + [apple_menu addItem:[NSMenuItem separatorItem]]; + + title = [NSString stringWithFormat:NSLocalizedString(@"Quit %@", nil), nsappname]; + [apple_menu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"]; + + // Setup menu bar + NSMenu *main_menu = [[NSMenu alloc] initWithTitle:@""]; + menu_item = [main_menu addItemWithTitle:@"" action:nil keyEquivalent:@""]; + [main_menu setSubmenu:apple_menu forItem:menu_item]; + [NSApp setMainMenu:main_menu]; + + [main_menu release]; + [apple_menu release]; [NSApp finishLaunching]; @@ -1676,11 +1776,13 @@ OS_OSX::OS_OSX() { cursor_shape = CURSOR_ARROW; - current_screen = 0; - maximized = false; minimized = false; window_size = Vector2(1024, 600); zoomed = false; display_scale = 1.0; } + +bool OS_OSX::_check_internal_feature_support(const String &p_feature) { + return p_feature == "pc" || p_feature == "s3tc"; +} |