summaryrefslogtreecommitdiff
path: root/platform/osx
diff options
context:
space:
mode:
Diffstat (limited to 'platform/osx')
-rw-r--r--platform/osx/audio_driver_osx.cpp175
-rw-r--r--platform/osx/audio_driver_osx.h8
-rw-r--r--platform/osx/detect.py70
-rw-r--r--platform/osx/export/export.cpp363
-rw-r--r--platform/osx/os_osx.h8
-rw-r--r--platform/osx/os_osx.mm362
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";
+}