diff options
Diffstat (limited to 'platform')
114 files changed, 3604 insertions, 2562 deletions
diff --git a/platform/android/AndroidManifest.xml.template b/platform/android/AndroidManifest.xml.template index be5afb406a..9d8eb951c4 100644 --- a/platform/android/AndroidManifest.xml.template +++ b/platform/android/AndroidManifest.xml.template @@ -17,7 +17,7 @@ android:launchMode="singleTask" android:screenOrientation="landscape" android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize"> - + <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> @@ -26,7 +26,7 @@ <service android:name="org.godotengine.godot.GodotDownloaderService" /> - + $$ADD_APPLICATION_CHUNKS$$ @@ -200,6 +200,6 @@ $$ADD_PERMISSION_CHUNKS$$ <uses-permission android:name="godot.custom.18"/> <uses-permission android:name="godot.custom.19"/> -<uses-sdk android:minSdkVersion="10" android:targetSdkVersion="23"/> - -</manifest> +<uses-sdk android:minSdkVersion="18" android:targetSdkVersion="23"/> + +</manifest> diff --git a/platform/android/SCsub b/platform/android/SCsub index 87e7ee4747..7fa0262359 100644 --- a/platform/android/SCsub +++ b/platform/android/SCsub @@ -46,8 +46,18 @@ gradle_baseout = open_utf8(abspath + "/java/build.gradle", "w") gradle_text = gradle_basein.read() - +gradle_maven_flat_text = "" +if len(env.android_flat_dirs) > 0: + gradle_maven_flat_text += "flatDir {\n" + gradle_maven_flat_text += "\tdirs " + for x in env.android_flat_dirs: + gradle_maven_flat_text += "'" + x + "'," + + gradle_maven_flat_text = gradle_maven_flat_text[:-1] + gradle_maven_flat_text += "\n\t}\n" + gradle_maven_repos_text = "" +gradle_maven_repos_text += gradle_maven_flat_text if len(env.android_maven_repos) > 0: gradle_maven_repos_text += "" @@ -73,7 +83,7 @@ for x in env.android_gradle_plugins: gradle_classpath = "" for x in env.android_gradle_classpath: gradle_classpath += "\t\tclasspath \"" + x + "\"\n" - + gradle_res_dirs_text = "" for x in env.android_res_dirs: @@ -93,13 +103,13 @@ gradle_asset_dirs_text = "" gradle_default_config_text = "" -minSdk = 14 +minSdk = 18 targetSdk = 23 for x in env.android_default_config: - if x.startswith("minSdkVersion") and int(x.split(" ")[-1]) < minSdk: + if x.startswith("minSdkVersion") and int(x.split(" ")[-1]) < minSdk: x = "minSdkVersion " + str(minSdk) - if x.startswith("targetSdkVersion") and int(x.split(" ")[-1]) > targetSdk: + if x.startswith("targetSdkVersion") and int(x.split(" ")[-1]) > targetSdk: x = "targetSdkVersion " + str(targetSdk) gradle_default_config_text += x + "\n\t\t" diff --git a/platform/android/audio_driver_opensl.cpp b/platform/android/audio_driver_opensl.cpp index e774f5bb7d..9ebed84ace 100644 --- a/platform/android/audio_driver_opensl.cpp +++ b/platform/android/audio_driver_opensl.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* audio_driver_opensl.cpp */ +/* audio_driver_opensl.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ diff --git a/platform/android/build.gradle.template b/platform/android/build.gradle.template index 1df56ce621..7cb6cf860a 100644 --- a/platform/android/build.gradle.template +++ b/platform/android/build.gradle.template @@ -1,6 +1,7 @@ buildscript { repositories { jcenter() + $$GRADLE_REPOSITORY_URLS$$ } dependencies { classpath 'com.android.tools.build:gradle:2.3.3' @@ -54,11 +55,11 @@ android { ] res.srcDirs = [ 'res' - $$GRADLE_RES_DIRS$$ + $$GRADLE_RES_DIRS$$ ] aidl.srcDirs = [ 'aidl' - $$GRADLE_AIDL_DIRS$$ + $$GRADLE_AIDL_DIRS$$ ] assets.srcDirs = [ 'assets' diff --git a/platform/android/detect.py b/platform/android/detect.py index 65442bf6f8..13fc4ee810 100644 --- a/platform/android/detect.py +++ b/platform/android/detect.py @@ -18,20 +18,21 @@ def can_build(): def get_opts(): + from SCons.Variables import BoolVariable, EnumVariable return [ ('ANDROID_NDK_ROOT', 'Path to the Android NDK', os.environ.get("ANDROID_NDK_ROOT", 0)), ('ndk_platform', 'Target platform (android-<api>, e.g. "android-18")', "android-18"), - ('android_arch', 'Target architecture (armv7/armv6/arm64v8/x86)', "armv7"), - ('android_neon', 'Enable NEON support (armv7 only)', "yes"), - ('android_stl', 'Enable Android STL support (for modules)', "no") + EnumVariable('android_arch', 'Target architecture', "armv7", ('armv7', 'armv6', 'arm64v8', 'x86')), + BoolVariable('android_neon', 'Enable NEON support (armv7 only)', True), + BoolVariable('android_stl', 'Enable Android STL support (for modules)', False), ] def get_flags(): return [ - ('tools', 'no'), + ('tools', False), ] @@ -93,7 +94,7 @@ def configure(env): env['android_arch'] = 'armv7' neon_text = "" - if env["android_arch"] == "armv7" and env['android_neon'] == 'yes': + if env["android_arch"] == "armv7" and env['android_neon']: neon_text = " (with NEON)" print("Building for Android (" + env['android_arch'] + ")" + neon_text) @@ -117,7 +118,7 @@ def configure(env): target_subpath = "arm-linux-androideabi-4.9" abi_subpath = "arm-linux-androideabi" arch_subpath = "armeabi-v7a" - if env['android_neon'] == 'yes': + if env['android_neon']: env.extra_suffix = ".armv7.neon" + env.extra_suffix else: env.extra_suffix = ".armv7" + env.extra_suffix @@ -199,7 +200,7 @@ def configure(env): elif env["android_arch"] == "armv7": target_opts = ['-target', 'armv7-none-linux-androideabi'] env.Append(CPPFLAGS='-D__ARM_ARCH_7__ -D__ARM_ARCH_7A__ -march=armv7-a -mfloat-abi=softfp'.split()) - if env['android_neon'] == 'yes': + if env['android_neon']: env['neon_enabled'] = True env.Append(CPPFLAGS=['-mfpu=neon', '-D__ARM_NEON__']) else: @@ -213,7 +214,7 @@ def configure(env): env.Append(CPPFLAGS=target_opts) env.Append(CPPFLAGS=common_opts) - if (env['android_stl'] == 'yes'): + if env['android_stl']: env.Append(CPPPATH=[env["ANDROID_NDK_ROOT"] + "/sources/cxx-stl/gnu-libstdc++/4.9/include"]) env.Append(CPPPATH=[env["ANDROID_NDK_ROOT"] + "/sources/cxx-stl/gnu-libstdc++/4.9/libs/" + arch_subpath + "/include"]) env.Append(LIBPATH=[env["ANDROID_NDK_ROOT"] + "/sources/cxx-stl/gnu-libstdc++/4.9/libs/" + arch_subpath]) @@ -240,10 +241,10 @@ def configure(env): env.Append(CPPPATH=['#platform/android']) env.Append(CPPFLAGS=['-DANDROID_ENABLED', '-DUNIX_ENABLED', '-DNO_FCNTL', '-DMPC_FIXED_POINT']) - env.Append(LIBS=['OpenSLES', 'EGL', 'GLESv3', 'android', 'log', 'z']) + env.Append(LIBS=['OpenSLES', 'EGL', 'GLESv3', 'android', 'log', 'z', 'dl']) # TODO: Move that to opus module's config - if("module_opus_enabled" in env and env["module_opus_enabled"] != "no"): + if 'module_opus_enabled' in env and env['module_opus_enabled']: if (env["android_arch"] == "armv6" or env["android_arch"] == "armv7"): env.Append(CFLAGS=["-DOPUS_ARM_OPT"]) env.opus_fixed_point = "yes" diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index 8b3a64bbe6..79be1501a7 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -204,7 +204,7 @@ class EditorExportAndroid : public EditorExportPlatform { String id; String name; String description; - int release; + int api_level; }; struct APKExportData { @@ -267,7 +267,6 @@ class EditorExportAndroid : public EditorExportPlatform { if (different) { - print_line("DIFFERENT!"); Vector<Device> ndevices; for (int i = 0; i < ldevices.size(); i++) { @@ -278,7 +277,7 @@ class EditorExportAndroid : public EditorExportPlatform { if (ea->devices[j].id == ldevices[i]) { d.description = ea->devices[j].description; d.name = ea->devices[j].name; - d.release = ea->devices[j].release; + d.api_level = ea->devices[j].api_level; } } @@ -299,7 +298,7 @@ class EditorExportAndroid : public EditorExportPlatform { String vendor; String device; d.description + "Device ID: " + d.id + "\n"; - d.release = 0; + d.api_level = 0; for (int j = 0; j < props.size(); j++) { String p = props[j]; @@ -310,9 +309,9 @@ class EditorExportAndroid : public EditorExportPlatform { } else if (p.begins_with("ro.build.display.id=")) { d.description += "Build: " + p.get_slice("=", 1).strip_edges() + "\n"; } else if (p.begins_with("ro.build.version.release=")) { - const String release_str = p.get_slice("=", 1).strip_edges(); - d.description += "Release: " + release_str + "\n"; - d.release = release_str.to_int(); + d.description += "Release: " + p.get_slice("=", 1).strip_edges() + "\n"; + } else if (p.begins_with("ro.build.version.sdk=")) { + d.api_level = p.get_slice("=", 1).to_int(); } else if (p.begins_with("ro.product.cpu.abi=")) { d.description += "CPU: " + p.get_slice("=", 1).strip_edges() + "\n"; } else if (p.begins_with("ro.product.manufacturer=")) { @@ -468,11 +467,36 @@ class EditorExportAndroid : public EditorExportPlatform { return zipfi; } - static Error save_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total) { + static Set<String> get_abis() { + Set<String> abis; + abis.insert("armeabi"); + abis.insert("armeabi-v7a"); + abis.insert("arm64-v8a"); + abis.insert("x86"); + abis.insert("x86_64"); + abis.insert("mips"); + abis.insert("mips64"); + return abis; + } + static Error save_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total) { APKExportData *ed = (APKExportData *)p_userdata; String dst_path = p_path; - dst_path = dst_path.replace_first("res://", "assets/"); + static Set<String> android_abis = get_abis(); + + if (dst_path.ends_with(".so")) { + String abi = dst_path.get_base_dir().get_file().strip_edges(); // parent dir name + if (android_abis.has(abi)) { + dst_path = "lib/" + abi + "/" + dst_path.get_file(); + } else { + String err = "Dynamic libraries must be located in the folder named after Android ABI they were compiled for. " + + p_path + " does not follow this convention."; + ERR_PRINT(err.utf8().get_data()); + return ERR_FILE_BAD_PATH; + } + } else { + dst_path = dst_path.replace_first("res://", "assets/"); + } zip_fileinfo zipfi = get_zip_fileinfo(); @@ -495,34 +519,30 @@ class EditorExportAndroid : public EditorExportPlatform { void _fix_manifest(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_manifest, bool p_give_internet) { - const int CHUNK_AXML_FILE = 0x00080003; - const int CHUNK_RESOURCEIDS = 0x00080180; + // Leaving the unused types commented because looking these constants up + // again later would be annoying + // const int CHUNK_AXML_FILE = 0x00080003; + // const int CHUNK_RESOURCEIDS = 0x00080180; const int CHUNK_STRINGS = 0x001C0001; - const int CHUNK_XML_END_NAMESPACE = 0x00100101; - const int CHUNK_XML_END_TAG = 0x00100103; - const int CHUNK_XML_START_NAMESPACE = 0x00100100; + // const int CHUNK_XML_END_NAMESPACE = 0x00100101; + // const int CHUNK_XML_END_TAG = 0x00100103; + // const int CHUNK_XML_START_NAMESPACE = 0x00100100; const int CHUNK_XML_START_TAG = 0x00100102; - const int CHUNK_XML_TEXT = 0x00100104; + // const int CHUNK_XML_TEXT = 0x00100104; const int UTF8_FLAG = 0x00000100; Vector<String> string_table; - uint32_t ofs = 0; - - uint32_t header = decode_uint32(&p_manifest[ofs]); - uint32_t filesize = decode_uint32(&p_manifest[ofs + 4]); - ofs += 8; + uint32_t ofs = 8; - //print_line("FILESIZE: "+itos(filesize)+" ACTUAL: "+itos(p_manifest.size())); + uint32_t string_count = 0; + uint32_t styles_count = 0; + uint32_t string_flags = 0; + uint32_t string_data_offset = 0; - uint32_t string_count; - uint32_t styles_count; - uint32_t string_flags; - uint32_t string_data_offset; - - uint32_t styles_offset; - uint32_t string_table_begins; - uint32_t string_table_ends; + uint32_t styles_offset = 0; + uint32_t string_table_begins = 0; + uint32_t string_table_ends = 0; Vector<uint8_t> stable_extra; String version_name = p_preset->get("version/name"); @@ -621,24 +641,16 @@ class EditorExportAndroid : public EditorExportPlatform { case CHUNK_XML_START_TAG: { int iofs = ofs + 8; - uint32_t line = decode_uint32(&p_manifest[iofs]); - uint32_t nspace = decode_uint32(&p_manifest[iofs + 8]); uint32_t name = decode_uint32(&p_manifest[iofs + 12]); - uint32_t check = decode_uint32(&p_manifest[iofs + 16]); String tname = string_table[name]; - - //printf("NSPACE: %i\n",nspace); - //printf("NAME: %i (%s)\n",name,tname.utf8().get_data()); - //printf("CHECK: %x\n",check); uint32_t attrcount = decode_uint32(&p_manifest[iofs + 20]); iofs += 28; - //printf("ATTRCOUNT: %x\n",attrcount); + for (uint32_t i = 0; i < attrcount; i++) { uint32_t attr_nspace = decode_uint32(&p_manifest[iofs]); uint32_t attr_name = decode_uint32(&p_manifest[iofs + 4]); uint32_t attr_value = decode_uint32(&p_manifest[iofs + 8]); - uint32_t attr_flags = decode_uint32(&p_manifest[iofs + 12]); uint32_t attr_resid = decode_uint32(&p_manifest[iofs + 16]); String value; @@ -653,12 +665,6 @@ class EditorExportAndroid : public EditorExportPlatform { else nspace = ""; - //printf("ATTR %i NSPACE: %i\n",i,attr_nspace); - //printf("ATTR %i NAME: %i (%s)\n",i,attr_name,attrname.utf8().get_data()); - //printf("ATTR %i VALUE: %i (%s)\n",i,attr_value,value.utf8().get_data()); - //printf("ATTR %i FLAGS: %x\n",i,attr_flags); - //printf("ATTR %i RESID: %x\n",i,attr_resid); - //replace project information if (tname == "manifest" && attrname == "package") { @@ -666,9 +672,6 @@ class EditorExportAndroid : public EditorExportPlatform { string_table[attr_value] = get_package_name(package_name); } - //print_line("tname: "+tname); - //print_line("nspace: "+nspace); - //print_line("attrname: "+attrname); if (tname == "manifest" && /*nspace=="android" &&*/ attrname == "versionCode") { print_line("FOUND versionCode"); @@ -687,13 +690,6 @@ class EditorExportAndroid : public EditorExportPlatform { if (tname == "activity" && /*nspace=="android" &&*/ attrname == "screenOrientation") { encode_uint32(orientation == 0 ? 0 : 1, &p_manifest[iofs + 16]); - /* - print_line("FOUND screen orientation"); - if (attr_value==0xFFFFFFFF) { - WARN_PRINT("Version name in a resource, should be plaintext") - } else { - string_table[attr_value]=(orientation==0?"landscape":"portrait"); - }*/ } if (tname == "uses-feature" && /*nspace=="android" &&*/ attrname == "glEsVersion") { @@ -746,13 +742,10 @@ class EditorExportAndroid : public EditorExportPlatform { } break; } - //printf("chunk %x: size: %d\n",chunk,size); ofs += size; } - //printf("end\n"); - //create new andriodmanifest binary Vector<uint8_t> ret; @@ -851,8 +844,6 @@ class EditorExportAndroid : public EditorExportPlatform { const int UTF8_FLAG = 0x00000100; print_line("*******************GORRRGLE***********************"); - uint32_t header = decode_uint32(&p_manifest[0]); - uint32_t filesize = decode_uint32(&p_manifest[4]); uint32_t string_block_len = decode_uint32(&p_manifest[16]); uint32_t string_count = decode_uint32(&p_manifest[20]); uint32_t string_flags = decode_uint32(&p_manifest[28]); @@ -862,10 +853,6 @@ class EditorExportAndroid : public EditorExportPlatform { String package_name = p_preset->get("package/name"); - //printf("stirng block len: %i\n",string_block_len); - //printf("stirng count: %i\n",string_count); - //printf("flags: %x\n",string_flags); - for (uint32_t i = 0; i < string_count; i++) { uint32_t offset = decode_uint32(&p_manifest[string_table_begins + i * 4]); @@ -883,7 +870,7 @@ class EditorExportAndroid : public EditorExportPlatform { String lang = str.substr(str.find_last("-") + 1, str.length()).replace("-", "_"); String prop = "application/config/name_" + lang; - if (ProjectSettings::get_singleton()->has(prop)) { + if (ProjectSettings::get_singleton()->has_setting(prop)) { str = ProjectSettings::get_singleton()->get(prop); } else { str = get_project_name(package_name); @@ -1073,7 +1060,11 @@ public: //export_temp ep.step("Exporting APK", 0); - p_debug_flags |= DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST; + const bool use_remote = (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) || (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT); + const bool use_reverse = devices[p_device].api_level >= 21; + + if (use_reverse) + p_debug_flags |= DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST; String export_to = EditorSettings::get_singleton()->get_settings_path() + "/tmp/tmpexport.apk"; Error err = export_project(p_preset, true, export_to, p_debug_flags); @@ -1119,40 +1110,54 @@ public: return ERR_CANT_CREATE; } - if (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) { + if (use_remote) { + if (use_reverse) { - args.clear(); - args.push_back("-s"); - args.push_back(devices[p_device].id); - args.push_back("reverse"); - args.push_back("--remove-all"); - OS::get_singleton()->execute(adb, args, true, NULL, NULL, &rv); + static const char *const msg = "** Device API >= 21; debugging over USB **"; + EditorNode::get_singleton()->get_log()->add_message(msg); + print_line(String(msg).to_upper()); - int dbg_port = EditorSettings::get_singleton()->get("network/debug/remote_port"); - args.clear(); - args.push_back("-s"); - args.push_back(devices[p_device].id); - args.push_back("reverse"); - args.push_back("tcp:" + itos(dbg_port)); - args.push_back("tcp:" + itos(dbg_port)); + args.clear(); + args.push_back("-s"); + args.push_back(devices[p_device].id); + args.push_back("reverse"); + args.push_back("--remove-all"); + OS::get_singleton()->execute(adb, args, true, NULL, NULL, &rv); - OS::get_singleton()->execute(adb, args, true, NULL, NULL, &rv); - print_line("Reverse result: " + itos(rv)); - } + if (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) { - if (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT) { + int dbg_port = EditorSettings::get_singleton()->get("network/debug/remote_port"); + args.clear(); + args.push_back("-s"); + args.push_back(devices[p_device].id); + args.push_back("reverse"); + args.push_back("tcp:" + itos(dbg_port)); + args.push_back("tcp:" + itos(dbg_port)); - int fs_port = EditorSettings::get_singleton()->get("filesystem/file_server/port"); + OS::get_singleton()->execute(adb, args, true, NULL, NULL, &rv); + print_line("Reverse result: " + itos(rv)); + } - args.clear(); - args.push_back("-s"); - args.push_back(devices[p_device].id); - args.push_back("reverse"); - args.push_back("tcp:" + itos(fs_port)); - args.push_back("tcp:" + itos(fs_port)); + if (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT) { - err = OS::get_singleton()->execute(adb, args, true, NULL, NULL, &rv); - print_line("Reverse result2: " + itos(rv)); + int fs_port = EditorSettings::get_singleton()->get("filesystem/file_server/port"); + + args.clear(); + args.push_back("-s"); + args.push_back(devices[p_device].id); + args.push_back("reverse"); + args.push_back("tcp:" + itos(fs_port)); + args.push_back("tcp:" + itos(fs_port)); + + err = OS::get_singleton()->execute(adb, args, true, NULL, NULL, &rv); + print_line("Reverse result2: " + itos(rv)); + } + } else { + + static const char *const msg = "** Device API < 21; debugging over Wi-Fi **"; + EditorNode::get_singleton()->get_log()->add_message(msg); + print_line(String(msg).to_upper()); + } } ep.step("Running on Device..", 3); @@ -1162,7 +1167,7 @@ public: args.push_back("shell"); args.push_back("am"); args.push_back("start"); - if ((bool)EditorSettings::get_singleton()->get("export/android/force_system_user") && devices[p_device].release >= 17) { // Multi-user introduced in Android 17 + if ((bool)EditorSettings::get_singleton()->get("export/android/force_system_user") && devices[p_device].api_level >= 17) { // Multi-user introduced in Android 17 args.push_back("--user"); args.push_back("0"); } @@ -1375,15 +1380,15 @@ public: } } - if (file == "lib/x86/libgodot_android.so" && !export_x86) { + if (file == "lib/x86/*.so" && !export_x86) { skip = true; } - if (file.match("lib/armeabi*/libgodot_android.so") && !export_arm) { + if (file.match("lib/armeabi*/*.so") && !export_arm) { skip = true; } - if (file.match("lib/arm64*/libgodot_android.so") && !export_arm64) { + if (file.match("lib/arm64*/*.so") && !export_arm64) { skip = true; } diff --git a/platform/android/file_access_android.cpp b/platform/android/file_access_android.cpp index f207b81b4b..0fdf9002d7 100644 --- a/platform/android/file_access_android.cpp +++ b/platform/android/file_access_android.cpp @@ -95,7 +95,7 @@ void FileAccessAndroid::seek_end(int64_t p_position) { pos = len + p_position; } -size_t FileAccessAndroid::get_pos() const { +size_t FileAccessAndroid::get_position() const { return pos; } @@ -146,6 +146,11 @@ Error FileAccessAndroid::get_error() const { return eof ? ERR_FILE_EOF : OK; //not sure what else it may happen } +void FileAccessAndroid::flush() { + + ERR_FAIL(); +} + void FileAccessAndroid::store_8(uint8_t p_dest) { ERR_FAIL(); diff --git a/platform/android/file_access_android.h b/platform/android/file_access_android.h index c2ce2b0bfe..c8fedbe684 100644 --- a/platform/android/file_access_android.h +++ b/platform/android/file_access_android.h @@ -53,7 +53,7 @@ public: virtual void seek(size_t p_position); ///< seek to a given position virtual void seek_end(int64_t p_position = 0); ///< seek from the end of file - virtual size_t get_pos() const; ///< get position in the file + virtual size_t get_position() const; ///< get position in the file virtual size_t get_len() const; ///< get size of the file virtual bool eof_reached() const; ///< reading passed EOF @@ -63,6 +63,7 @@ public: virtual Error get_error() const; ///< get last error + virtual void flush(); virtual void store_8(uint8_t p_dest); ///< store a byte virtual bool file_exists(const String &p_path); ///< return true if a file exists diff --git a/platform/android/file_access_jandroid.cpp b/platform/android/file_access_jandroid.cpp index ad855c790d..980afd8f46 100644 --- a/platform/android/file_access_jandroid.cpp +++ b/platform/android/file_access_jandroid.cpp @@ -106,7 +106,7 @@ void FileAccessJAndroid::seek_end(int64_t p_position) { seek(get_len()); } -size_t FileAccessJAndroid::get_pos() const { +size_t FileAccessJAndroid::get_position() const { JNIEnv *env = ThreadAndroid::get_env(); ERR_FAIL_COND_V(!is_open(), 0); @@ -157,6 +157,9 @@ Error FileAccessJAndroid::get_error() const { return OK; } +void FileAccessJAndroid::flush() { +} + void FileAccessJAndroid::store_8(uint8_t p_dest) { } diff --git a/platform/android/file_access_jandroid.h b/platform/android/file_access_jandroid.h index 8060312182..368d2c98fa 100644 --- a/platform/android/file_access_jandroid.h +++ b/platform/android/file_access_jandroid.h @@ -57,7 +57,7 @@ public: virtual void seek(size_t p_position); ///< seek to a given position virtual void seek_end(int64_t p_position = 0); ///< seek from the end of file - virtual size_t get_pos() const; ///< get position in the file + virtual size_t get_position() const; ///< get position in the file virtual size_t get_len() const; ///< get size of the file virtual bool eof_reached() const; ///< reading passed EOF @@ -67,6 +67,7 @@ public: virtual Error get_error() const; ///< get last error + virtual void flush(); virtual void store_8(uint8_t p_dest); ///< store a byte virtual bool file_exists(const String &p_path); ///< return true if a file exists diff --git a/platform/android/java/src/org/godotengine/godot/Godot.java b/platform/android/java/src/org/godotengine/godot/Godot.java index eab0387f32..59fefc498f 100644 --- a/platform/android/java/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/src/org/godotengine/godot/Godot.java @@ -92,7 +92,7 @@ import android.os.SystemClock; public class Godot extends Activity implements SensorEventListener, IDownloaderClient -{ +{ static final int MAX_SINGLETONS = 64; private IStub mDownloaderClientStub; @@ -139,9 +139,9 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC com.godot.game.R.string.text_button_pause; mPauseButton.setText(stringResourceID); } - + static public class SingletonBase { - + protected void registerClass(String p_name, String[] p_methods) { GodotLib.singleton(p_name,this); @@ -191,6 +191,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC protected void onMainPause() {} protected void onMainResume() {} protected void onMainDestroy() {} + protected boolean onMainBackPressed() { return false; } protected void onGLDrawFrame(GL10 gl) {} protected void onGLSurfaceChanged(GL10 gl, int width, int height) {} // singletons will always miss first onGLSurfaceChanged call @@ -210,6 +211,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC */ private String[] command_line; + private boolean use_apk_expansion; public GodotView mView; private boolean godot_initialized=false; @@ -264,27 +266,55 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC layout = new FrameLayout(this); layout.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT)); setContentView(layout); - + // GodotEditText layout - GodotEditText edittext = new GodotEditText(this); + GodotEditText edittext = new GodotEditText(this); edittext.setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.WRAP_CONTENT)); // ...add to FrameLayout layout.addView(edittext); - + mView = new GodotView(getApplication(),io,use_gl2,use_32_bits, this); layout.addView(mView,new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT)); - setKeepScreenOn(GodotLib.getGlobal("display/driver/keep_screen_on").equals("True")); - - edittext.setView(mView); - io.setEdit(edittext); - + edittext.setView(mView); + io.setEdit(edittext); + + final Godot godot = this; + mView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + Point fullSize = new Point(); + godot.getWindowManager().getDefaultDisplay().getSize(fullSize); + Rect gameSize = new Rect(); + godot.mView.getWindowVisibleDisplayFrame(gameSize); + + final int keyboardHeight = fullSize.y - gameSize.bottom; + Log.d("GODOT", "setVirtualKeyboardHeight: " + keyboardHeight); + GodotLib.setVirtualKeyboardHeight(keyboardHeight); + } + }); + // Ad layout adLayout = new RelativeLayout(this); adLayout.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT)); layout.addView(adLayout); - + + final String[] current_command_line = command_line; + final GodotView view = mView; + mView.queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.setup(current_command_line); + runOnUiThread(new Runnable() { + @Override + public void run() { + view.setKeepScreenOn("True".equals(GodotLib.getGlobal("display/driver/keep_screen_on"))); + } + }); + } + }); + } - + public void setKeepScreenOn(final boolean p_enabled) { keep_screen_on = p_enabled; if (mView != null){ @@ -315,13 +345,13 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC } }); } - + private static Godot _self; - + public static Godot getInstance(){ return Godot._self; } - + private String[] getCommandLine() { InputStream is; @@ -402,7 +432,6 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC Log.d("GODOT"," " + command_line[w]); } }*/ - GodotLib.initialize(this,io.needsReloadHooks(),command_line,getAssets()); mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME); @@ -411,9 +440,12 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC mGyroscope = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE); mSensorManager.registerListener(this, mGyroscope, SensorManager.SENSOR_DELAY_GAME); + GodotLib.initialize(this, io.needsReloadHooks(), getAssets(), use_apk_expansion); + result_callback = null; mPaymentsManager = PaymentsManager.createManager(this).initService(); + godot_initialized=true; } @@ -426,7 +458,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC - @Override + @Override protected void onCreate(Bundle icicle) { Log.d("GODOT", "** GODOT ACTIVITY CREATED HERE ***\n"); @@ -437,12 +469,10 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC //window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); - //check for apk expansion API if (true) { boolean md5mismatch = false; command_line = getCommandLine(); - boolean use_apk_expansion=false; String main_pack_md5=null; String main_pack_key=null; @@ -528,23 +558,23 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC if (!pack_valid) { Log.d("GODOT", "Pack Invalid, try re-downloading."); - + Intent notifierIntent = new Intent(this, this.getClass()); notifierIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); - + PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT); - + int startResult; try { Log.d("GODOT", "INITIALIZING DOWNLOAD"); startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired( - getApplicationContext(), - pendingIntent, + getApplicationContext(), + pendingIntent, GodotDownloaderService.class); Log.d("GODOT", "DOWNLOAD SERVICE FINISHED:" + startResult); - + if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) { Log.d("GODOT", "DOWNLOAD REQUIRED"); // This is where you do set up to display the download @@ -563,7 +593,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC mCellMessage = findViewById(com.godot.game.R.id.approveCellular); mPauseButton = (Button) findViewById(com.godot.game.R.id.pauseButton); mWiFiSettingsButton = (Button) findViewById(com.godot.game.R.id.wifiSettingsButton); - + return; } else{ Log.d("GODOT", "NO DOWNLOAD REQUIRED"); @@ -582,7 +612,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC initializeGodot(); - + //instanceSingleton( new GodotFacebook(this) ); @@ -590,14 +620,14 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC @Override protected void onDestroy(){ - + if(mPaymentsManager != null ) mPaymentsManager.destroy(); for(int i=0;i<singleton_count;i++) { singletons[i].onMainDestroy(); } super.onDestroy(); } - + @Override protected void onPause() { super.onPause(); if (!godot_initialized){ @@ -607,8 +637,13 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC return; } mView.onPause(); + mView.queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.focusout(); + } + }); mSensorManager.unregisterListener(this); - GodotLib.focusout(); for(int i=0;i<singleton_count;i++) { singletons[i].onMainPause(); @@ -625,10 +660,16 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC } mView.onResume(); + mView.queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.focusin(); + } + }); mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME); mSensorManager.registerListener(this, mMagnetometer, SensorManager.SENSOR_DELAY_GAME); mSensorManager.registerListener(this, mGyroscope, SensorManager.SENSOR_DELAY_GAME); - GodotLib.focusin(); + if(use_immersive && Build.VERSION.SDK_INT >= 19.0){ // check if the application runs on an android 4.4+ Window window = getWindow(); window.getDecorView().setSystemUiVisibility( @@ -644,8 +685,8 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC singletons[i].onMainResume(); } - - + + } @@ -670,32 +711,39 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC @Override public void onSensorChanged(SensorEvent event) { Display display = ((WindowManager) getSystemService(WINDOW_SERVICE)).getDefaultDisplay(); int displayRotation = display.getRotation(); - + float[] adjustedValues = new float[3]; final int axisSwap[][] = { - { 1, -1, 0, 1 }, // ROTATION_0 - {-1, -1, 1, 0 }, // ROTATION_90 - {-1, 1, 0, 1 }, // ROTATION_180 - { 1, 1, 1, 0 } }; // ROTATION_270 - - final int[] as = axisSwap[displayRotation]; - adjustedValues[0] = (float)as[0] * event.values[ as[2] ]; - adjustedValues[1] = (float)as[1] * event.values[ as[3] ]; + { 1, -1, 0, 1 }, // ROTATION_0 + {-1, -1, 1, 0 }, // ROTATION_90 + {-1, 1, 0, 1 }, // ROTATION_180 + { 1, 1, 1, 0 } }; // ROTATION_270 + + final int[] as = axisSwap[displayRotation]; + adjustedValues[0] = (float)as[0] * event.values[ as[2] ]; + adjustedValues[1] = (float)as[1] * event.values[ as[3] ]; adjustedValues[2] = event.values[2]; - - float x = adjustedValues[0]; - float y = adjustedValues[1]; - float z = adjustedValues[2]; - - int typeOfSensor = event.sensor.getType(); - if (typeOfSensor == event.sensor.TYPE_ACCELEROMETER) { - GodotLib.accelerometer(x,y,z); - } - if (typeOfSensor == event.sensor.TYPE_MAGNETIC_FIELD) { - GodotLib.magnetometer(x,y,z); - } - if (typeOfSensor == event.sensor.TYPE_GYROSCOPE) { - GodotLib.gyroscope(x,y,z); + + final float x = adjustedValues[0]; + final float y = adjustedValues[1]; + final float z = adjustedValues[2]; + + final int typeOfSensor = event.sensor.getType(); + if (mView != null) { + mView.queueEvent(new Runnable() { + @Override + public void run() { + if (typeOfSensor == Sensor.TYPE_ACCELEROMETER) { + GodotLib.accelerometer(x,y,z); + } + if (typeOfSensor == Sensor.TYPE_MAGNETIC_FIELD) { + GodotLib.magnetometer(x,y,z); + } + if (typeOfSensor == Sensor.TYPE_GYROSCOPE) { + GodotLib.gyroscope(x,y,z); + } + } + }); } } @@ -720,9 +768,23 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC */ @Override public void onBackPressed() { + boolean shouldQuit = true; + + for(int i=0;i<singleton_count;i++) { + if (singletons[i].onMainBackPressed()) { + shouldQuit = false; + } + } System.out.printf("** BACK REQUEST!\n"); - //GodotLib.back(); + if (shouldQuit && mView != null) { + mView.queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.back(); + } + }); + } } public void forceQuit() { @@ -733,7 +795,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC private boolean obbIsCorrupted(String f, String main_pack_md5){ - + try { InputStream fis = new FileInputStream(f); @@ -780,58 +842,64 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC } //@Override public boolean dispatchTouchEvent (MotionEvent event) { - public boolean gotTouchEvent(MotionEvent event) { + public boolean gotTouchEvent(final MotionEvent event) { super.onTouchEvent(event); - int evcount=event.getPointerCount(); + final int evcount=event.getPointerCount(); if (evcount==0) return true; - int[] arr = new int[event.getPointerCount()*3]; + if (mView != null) { + final int[] arr = new int[event.getPointerCount()*3]; - for(int i=0;i<event.getPointerCount();i++) { + for(int i=0;i<event.getPointerCount();i++) { - arr[i*3+0]=(int)event.getPointerId(i); - arr[i*3+1]=(int)event.getX(i); - arr[i*3+2]=(int)event.getY(i); - } + arr[i*3+0]=(int)event.getPointerId(i); + arr[i*3+1]=(int)event.getX(i); + arr[i*3+2]=(int)event.getY(i); + } - //System.out.printf("gaction: %d\n",event.getAction()); - switch(event.getAction()&MotionEvent.ACTION_MASK) { - - case MotionEvent.ACTION_DOWN: { - GodotLib.touch(0,0,evcount,arr); - //System.out.printf("action down at: %f,%f\n", event.getX(),event.getY()); - } break; - case MotionEvent.ACTION_MOVE: { - GodotLib.touch(1,0,evcount,arr); - /* - for(int i=0;i<event.getPointerCount();i++) { - System.out.printf("%d - moved to: %f,%f\n",i, event.getX(i),event.getY(i)); - } - */ - } break; - case MotionEvent.ACTION_POINTER_UP: { - final int indexPointUp = event.getActionIndex(); - final int pointer_idx = event.getPointerId(indexPointUp); - GodotLib.touch(4,pointer_idx,evcount,arr); - //System.out.printf("%d - s.up at: %f,%f\n",pointer_idx, event.getX(pointer_idx),event.getY(pointer_idx)); - } break; - case MotionEvent.ACTION_POINTER_DOWN: { - int pointer_idx = event.getActionIndex(); - GodotLib.touch(3,pointer_idx,evcount,arr); - //System.out.printf("%d - s.down at: %f,%f\n",pointer_idx, event.getX(pointer_idx),event.getY(pointer_idx)); - } break; - case MotionEvent.ACTION_CANCEL: - case MotionEvent.ACTION_UP: { - GodotLib.touch(2,0,evcount,arr); - /* - for(int i=0;i<event.getPointerCount();i++) { - System.out.printf("%d - up! %f,%f\n",i, event.getX(i),event.getY(i)); + //System.out.printf("gaction: %d\n",event.getAction()); + final int action = event.getAction() & MotionEvent.ACTION_MASK; + mView.queueEvent(new Runnable() { + @Override + public void run() { + switch(action) { + case MotionEvent.ACTION_DOWN: { + GodotLib.touch(0,0,evcount,arr); + //System.out.printf("action down at: %f,%f\n", event.getX(),event.getY()); + } break; + case MotionEvent.ACTION_MOVE: { + GodotLib.touch(1,0,evcount,arr); + /* + for(int i=0;i<event.getPointerCount();i++) { + System.out.printf("%d - moved to: %f,%f\n",i, event.getX(i),event.getY(i)); + } + */ + } break; + case MotionEvent.ACTION_POINTER_UP: { + final int indexPointUp = event.getActionIndex(); + final int pointer_idx = event.getPointerId(indexPointUp); + GodotLib.touch(4,pointer_idx,evcount,arr); + //System.out.printf("%d - s.up at: %f,%f\n",pointer_idx, event.getX(pointer_idx),event.getY(pointer_idx)); + } break; + case MotionEvent.ACTION_POINTER_DOWN: { + int pointer_idx = event.getActionIndex(); + GodotLib.touch(3,pointer_idx,evcount,arr); + //System.out.printf("%d - s.down at: %f,%f\n",pointer_idx, event.getX(pointer_idx),event.getY(pointer_idx)); + } break; + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: { + GodotLib.touch(2,0,evcount,arr); + /* + for(int i=0;i<event.getPointerCount();i++) { + System.out.printf("%d - up! %f,%f\n",i, event.getX(i),event.getY(i)); + } + */ + } break; + } } - */ - } break; - + }); } return true; } @@ -864,7 +932,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC private void queueEvent(Runnable runnable) { // TODO Auto-generated method stub - + } public PaymentsManager getPaymentsManager() { @@ -988,7 +1056,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC mProgressFraction.setText(Helpers.getDownloadProgressString (progress.mOverallProgress, progress.mOverallTotal)); - + } - + } diff --git a/platform/android/java/src/org/godotengine/godot/GodotLib.java b/platform/android/java/src/org/godotengine/godot/GodotLib.java index b487c17f53..e0ed4cd38c 100644 --- a/platform/android/java/src/org/godotengine/godot/GodotLib.java +++ b/platform/android/java/src/org/godotengine/godot/GodotLib.java @@ -37,7 +37,7 @@ public class GodotLib { public static GodotIO io; static { - System.loadLibrary("godot_android"); + System.loadLibrary("godot_android"); } /** @@ -45,7 +45,8 @@ public class GodotLib { * @param height the current view height */ - public static native void initialize(Godot p_instance,boolean need_reload_hook,String[] p_cmdline,Object p_asset_manager); + public static native void initialize(Godot p_instance,boolean need_reload_hook,Object p_asset_manager, boolean use_apk_expansion); + public static native void setup(String[] p_cmdline); public static native void resize(int width, int height,boolean reload); public static native void newcontext(boolean p_32_bits); public static native void back(); @@ -68,4 +69,6 @@ public class GodotLib { public static native void callobject(int p_ID, String p_method, Object[] p_params); public static native void calldeferred(int p_ID, String p_method, Object[] p_params); + public static native void setVirtualKeyboardHeight(int p_height); + } diff --git a/platform/android/java/src/org/godotengine/godot/GodotPaymentV3.java b/platform/android/java/src/org/godotengine/godot/GodotPaymentV3.java index a16c67523e..8fe79fdfc7 100644 --- a/platform/android/java/src/org/godotengine/godot/GodotPaymentV3.java +++ b/platform/android/java/src/org/godotengine/godot/GodotPaymentV3.java @@ -89,7 +89,7 @@ public class GodotPaymentV3 extends Godot.SingletonBase { } public void callbackSuccess(String ticket, String signature, String sku) { - GodotLib.callobject(purchaseCallbackId, "purchase_success", new Object[]{ticket, signature, sku}); + GodotLib.calldeferred(purchaseCallbackId, "purchase_success", new Object[]{ticket, signature, sku}); } public void callbackSuccessProductMassConsumed(String ticket, String signature, String sku) { diff --git a/platform/android/java/src/org/godotengine/godot/GodotView.java b/platform/android/java/src/org/godotengine/godot/GodotView.java index 19fcf8e687..b807b952d4 100644 --- a/platform/android/java/src/org/godotengine/godot/GodotView.java +++ b/platform/android/java/src/org/godotengine/godot/GodotView.java @@ -208,8 +208,9 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { @Override public void onInputDeviceAdded(int deviceId) { joystick joy = new joystick(); joy.device_id = deviceId; - int id = joy_devices.size(); + final int id = joy_devices.size(); InputDevice device = mInputManager.getInputDevice(deviceId); + final String name = device.getName(); joy.name = device.getName(); joy.axes = new ArrayList<InputDevice.MotionRange>(); joy.hats = new ArrayList<InputDevice.MotionRange>(); @@ -224,19 +225,29 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { } } joy_devices.add(joy); - GodotLib.joyconnectionchanged(id, true, joy.name); + queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.joyconnectionchanged(id, true, name); + } + }); } @Override public void onInputDeviceRemoved(int deviceId) { - int id = find_joy_device(deviceId); + final int id = find_joy_device(deviceId); joy_devices.remove(id); - GodotLib.joyconnectionchanged(id, false, ""); + queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.joyconnectionchanged(id, false, ""); + } + }); } @Override public void onInputDeviceChanged(int deviceId) { } - @Override public boolean onKeyUp(int keyCode, KeyEvent event) { + @Override public boolean onKeyUp(final int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { return true; @@ -249,22 +260,32 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { int source = event.getSource(); if ((source & InputDevice.SOURCE_JOYSTICK) != 0 || (source & InputDevice.SOURCE_DPAD) != 0 || (source & InputDevice.SOURCE_GAMEPAD) != 0) { - int button = get_godot_button(keyCode); - int device = find_joy_device(event.getDeviceId()); + final int button = get_godot_button(keyCode); + final int device = find_joy_device(event.getDeviceId()); - GodotLib.joybutton(device, button, false); + queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.joybutton(device, button, false); + } + }); return true; } else { - - GodotLib.key(keyCode, event.getUnicodeChar(0), false); + final int chr = event.getUnicodeChar(0); + queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.key(keyCode, chr, false); + } + }); }; return super.onKeyUp(keyCode, event); }; - @Override public boolean onKeyDown(int keyCode, KeyEvent event) { + @Override public boolean onKeyDown(final int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { - GodotLib.back(); + activity.onBackPressed(); // press 'back' button should not terminate program //normal handle 'back' event in game logic return true; @@ -281,16 +302,26 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { if (event.getRepeatCount() > 0) // ignore key echo return true; - int button = get_godot_button(keyCode); - int device = find_joy_device(event.getDeviceId()); + final int button = get_godot_button(keyCode); + final int device = find_joy_device(event.getDeviceId()); //Log.e(TAG, String.format("joy button down! button %x, %d, device %d", keyCode, button, device)); - - GodotLib.joybutton(device, button, true); + queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.joybutton(device, button, true); + } + }); return true; } else { - GodotLib.key(keyCode, event.getUnicodeChar(0), true); + final int chr = event.getUnicodeChar(0); + queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.key(keyCode, chr, true); + } + }); }; return super.onKeyDown(keyCode, event); } @@ -299,21 +330,32 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK && event.getAction() == MotionEvent.ACTION_MOVE) { - int device_id = find_joy_device(event.getDeviceId()); + final int device_id = find_joy_device(event.getDeviceId()); joystick joy = joy_devices.get(device_id); for (int i = 0; i < joy.axes.size(); i++) { InputDevice.MotionRange range = joy.axes.get(i); - float value = (event.getAxisValue(range.getAxis()) - range.getMin() ) / range.getRange() * 2.0f - 1.0f; + final float value = (event.getAxisValue(range.getAxis()) - range.getMin() ) / range.getRange() * 2.0f - 1.0f; //Log.e(TAG, String.format("axis event: %d, value %f", i, value)); - GodotLib.joyaxis(device_id, i, value); + final int idx = i; + queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.joyaxis(device_id, idx, value); + } + }); } for (int i = 0; i < joy.hats.size(); i+=2) { - int hatX = Math.round(event.getAxisValue(joy.hats.get(i).getAxis())); - int hatY = Math.round(event.getAxisValue(joy.hats.get(i+1).getAxis())); + final int hatX = Math.round(event.getAxisValue(joy.hats.get(i).getAxis())); + final int hatY = Math.round(event.getAxisValue(joy.hats.get(i+1).getAxis())); //Log.e(TAG, String.format("HAT EVENT %d, %d", hatX, hatY)); - GodotLib.joyhat(device_id, hatX, hatY); + queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.joyhat(device_id, hatX, hatY); + } + }); } return true; }; diff --git a/platform/android/java/src/org/godotengine/godot/input/GodotTextInputWrapper.java b/platform/android/java/src/org/godotengine/godot/input/GodotTextInputWrapper.java index c2d03e4ecc..ac424ab9f8 100644 --- a/platform/android/java/src/org/godotengine/godot/input/GodotTextInputWrapper.java +++ b/platform/android/java/src/org/godotengine/godot/input/GodotTextInputWrapper.java @@ -88,60 +88,50 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene public void beforeTextChanged(final CharSequence pCharSequence, final int start, final int count, final int after) { //Log.d(TAG, "beforeTextChanged(" + pCharSequence + ")start: " + start + ",count: " + count + ",after: " + after); - for (int i=0;i<count;i++){ - GodotLib.key(KeyEvent.KEYCODE_DEL, 0, true); - GodotLib.key(KeyEvent.KEYCODE_DEL, 0, false); - } + mView.queueEvent(new Runnable() { + @Override + public void run() { + for (int i = 0; i < count; ++i) { + GodotLib.key(KeyEvent.KEYCODE_DEL, 0, true); + GodotLib.key(KeyEvent.KEYCODE_DEL, 0, false); + } + } + }); } @Override public void onTextChanged(final CharSequence pCharSequence, final int start, final int before, final int count) { //Log.d(TAG, "onTextChanged(" + pCharSequence + ")start: " + start + ",count: " + count + ",before: " + before); - for (int i=start;i<start+count;i++){ - int ch = pCharSequence.charAt(i); - GodotLib.key(0, ch, true); - GodotLib.key(0, ch, false); - } - + mView.queueEvent(new Runnable() { + @Override + public void run() { + for (int i = start; i < start + count; ++i) { + final int ch = pCharSequence.charAt(i); + GodotLib.key(0, ch, true); + GodotLib.key(0, ch, false); + } + } + }); } @Override public boolean onEditorAction(final TextView pTextView, final int pActionID, final KeyEvent pKeyEvent) { if (this.mEdit == pTextView && this.isFullScreenEdit()) { - // user press the action button, delete all old text and insert new text - for (int i = this.mOriginText.length(); i > 0; i--) { - GodotLib.key(KeyEvent.KEYCODE_DEL, 0, true); - GodotLib.key(KeyEvent.KEYCODE_DEL, 0, false); - /* - if (BuildConfig.DEBUG) { - Log.d(TAG, "deleteBackward"); + final String characters = pKeyEvent.getCharacters(); + + mView.queueEvent(new Runnable() { + @Override + public void run() { + for (int i = 0; i < characters.length(); i++) { + final int ch = characters.codePointAt(i); + GodotLib.key(0, ch, true); + GodotLib.key(0, ch, false); + } } - */ - } - String text = pTextView.getText().toString(); - - /* If user input nothing, translate "\n" to engine. */ - if (text.compareTo("") == 0) { - text = "\n"; - } - - if ('\n' != text.charAt(text.length() - 1)) { - text += '\n'; - } - - for(int i = 0; i < text.length(); i++) { - int ch = text.codePointAt(i); - GodotLib.key(0, ch, true); - GodotLib.key(0, ch, false); - } - /* - if (BuildConfig.DEBUG) { - Log.d(TAG, "insertText(" + insertText + ")"); - } - */ + }); } - + if (pActionID == EditorInfo.IME_ACTION_DONE) { this.mView.requestFocus(); } diff --git a/platform/android/java/src/org/godotengine/godot/utils/CustomSSLSocketFactory.java b/platform/android/java/src/org/godotengine/godot/utils/CustomSSLSocketFactory.java index c9532a5d01..3fc8c48397 100644 --- a/platform/android/java/src/org/godotengine/godot/utils/CustomSSLSocketFactory.java +++ b/platform/android/java/src/org/godotengine/godot/utils/CustomSSLSocketFactory.java @@ -36,12 +36,9 @@ import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; +import javax.net.ssl.TrustManagerFactory; import org.apache.http.conn.ssl.SSLSocketFactory; @@ -56,19 +53,10 @@ public class CustomSSLSocketFactory extends SSLSocketFactory { public CustomSSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { super(truststore); - TrustManager tm = new X509TrustManager() { - public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { - } + TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509"); + tmf.init(truststore); - public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { - } - - public X509Certificate[] getAcceptedIssuers() { - return null; - } - }; - - sslContext.init(null, new TrustManager[] { tm }, null); + sslContext.init(null, tmf.getTrustManagers(), null); } @Override diff --git a/platform/android/java_glue.cpp b/platform/android/java_glue.cpp index 06abe9d751..0b193f5882 100644 --- a/platform/android/java_glue.cpp +++ b/platform/android/java_glue.cpp @@ -602,23 +602,10 @@ struct TST { TST tst; -struct JAndroidPointerEvent { - - Vector<OS_Android::TouchPos> points; - int pointer; - int what; -}; - -static List<JAndroidPointerEvent> pointer_events; -static List<Ref<InputEvent> > key_events; -static List<OS_Android::JoypadEvent> joy_events; static bool initialized = false; -static Mutex *input_mutex = NULL; -static Mutex *suspend_mutex = NULL; static int step = 0; static bool resized = false; static bool resized_reload = false; -static bool go_back_request = false; static Size2 new_size; static Vector3 accelerometer; static Vector3 magnetometer; @@ -626,8 +613,6 @@ static Vector3 gyroscope; static HashMap<String, JNISingleton *> jni_singletons; static jobject godot_io; -static Vector<int> joy_device_ids; - typedef void (*GFXInitFunc)(void *ud, bool gl2); static jmethodID _on_video_init = 0; @@ -756,7 +741,19 @@ static void _alert(const String &p_message, const String &p_title) { env->CallVoidMethod(_godot_instance, _alertDialog, jStrMessage, jStrTitle); } -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jobject obj, jobject activity, jboolean p_need_reload_hook, jobjectArray p_cmdline, jobject p_asset_manager) { +// volatile because it can be changed from non-main thread and we need to +// ensure the change is immediately visible to other threads. +static volatile int virtual_keyboard_height; + +static int _get_vk_height() { + return virtual_keyboard_height; +} + +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHeight(JNIEnv *env, jobject obj, jint p_height) { + virtual_keyboard_height = p_height; +} + +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jobject obj, jobject activity, jboolean p_need_reload_hook, jobject p_asset_manager, jboolean p_use_apk_expansion) { __android_log_print(ANDROID_LOG_INFO, "godot", "**INIT EVENT! - %p\n", env); @@ -826,36 +823,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *en AudioDriverAndroid::setup(gob); } - const char **cmdline = NULL; - int cmdlen = 0; - bool use_apk_expansion = false; - if (p_cmdline) { - cmdlen = env->GetArrayLength(p_cmdline); - if (cmdlen) { - cmdline = (const char **)malloc((env->GetArrayLength(p_cmdline) + 1) * sizeof(const char *)); - cmdline[cmdlen] = NULL; - - for (int i = 0; i < cmdlen; i++) { - - jstring string = (jstring)env->GetObjectArrayElement(p_cmdline, i); - const char *rawString = env->GetStringUTFChars(string, 0); - if (!rawString) { - __android_log_print(ANDROID_LOG_INFO, "godot", "cmdline arg %i is null\n", i); - } else { - //__android_log_print(ANDROID_LOG_INFO,"godot","cmdline arg %i is: %s\n",i,rawString); - - if (strcmp(rawString, "--main_pack") == 0) - use_apk_expansion = true; - } - - cmdline[i] = rawString; - } - } - } - - __android_log_print(ANDROID_LOG_INFO, "godot", "CMDLINE LEN %i - APK EXPANSION %I\n", cmdlen, int(use_apk_expansion)); - - os_android = new OS_Android(_gfx_init_func, env, _open_uri, _get_data_dir, _get_locale, _get_model, _get_screen_dpi, _show_vk, _hide_vk, _set_screen_orient, _get_unique_id, _get_system_dir, _play_video, _is_video_playing, _pause_video, _stop_video, _set_keep_screen_on, _alert, use_apk_expansion); + os_android = new OS_Android(_gfx_init_func, env, _open_uri, _get_data_dir, _get_locale, _get_model, _get_screen_dpi, _show_vk, _hide_vk, _get_vk_height, _set_screen_orient, _get_unique_id, _get_system_dir, _play_video, _is_video_playing, _pause_video, _stop_video, _set_keep_screen_on, _alert, p_use_apk_expansion); os_android->set_need_reload_hooks(p_need_reload_hook); char wd[500]; @@ -864,67 +832,15 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *en __android_log_print(ANDROID_LOG_INFO, "godot", "test construction %i\n", tst.a); __android_log_print(ANDROID_LOG_INFO, "godot", "running from dir %s\n", wd); - __android_log_print(ANDROID_LOG_INFO, "godot", "**SETUP"); - - Error err = Main::setup("apk", cmdlen, (char **)cmdline, false); - - if (err != OK) { - __android_log_print(ANDROID_LOG_INFO, "godot", "*****UNABLE TO SETUP"); - - return; //should exit instead and print the error - } - - __android_log_print(ANDROID_LOG_INFO, "godot", "*****SETUP OK"); - //video driver is determined here, because once initialized, it can't be changed - String vd = ProjectSettings::get_singleton()->get("display/driver"); + // String vd = ProjectSettings::get_singleton()->get("display/driver"); env->CallVoidMethod(_godot_instance, _on_video_init, (jboolean) true); - - __android_log_print(ANDROID_LOG_INFO, "godot", "**START"); - - input_mutex = Mutex::create(); - suspend_mutex = Mutex::create(); -} - -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jobject obj, jint width, jint height, jboolean reload) { - - __android_log_print(ANDROID_LOG_INFO, "godot", "^_^_^_^_^ resize %lld, %i, %i\n", Thread::get_caller_id(), width, height); - if (os_android) - os_android->set_display_size(Size2(width, height)); - - /*input_mutex->lock(); - resized=true; - if (reload) - resized_reload=true; - new_size=Size2(width,height); - input_mutex->unlock();*/ -} - -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jobject obj, bool p_32_bits) { - - __android_log_print(ANDROID_LOG_INFO, "godot", "^_^_^_^_^ newcontext %lld\n", Thread::get_caller_id()); - - if (os_android) { - os_android->set_context_is_16_bits(!p_32_bits); - } - - if (os_android && step > 0) { - - os_android->reload_gfx(); - } -} - -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jobject obj) { - - input_mutex->lock(); - go_back_request = true; - input_mutex->unlock(); } static void _initialize_java_modules() { - if (!ProjectSettings::get_singleton()->has("android/modules")) { + if (!ProjectSettings::get_singleton()->has_setting("android/modules")) { print_line("ANDROID MODULES: Nothing to load, aborting"); return; } @@ -985,74 +901,114 @@ static void _initialize_java_modules() { } } -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jobject obj) { - +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jobject obj, jobjectArray p_cmdline) { ThreadAndroid::setup_thread(); - //__android_log_print(ANDROID_LOG_INFO,"godot","**STEP EVENT! - %p-%i\n",env,Thread::get_caller_id()); + __android_log_print(ANDROID_LOG_INFO, "godot", "**SETUP"); - suspend_mutex->lock(); - input_mutex->lock(); - //first time step happens, initialize - if (step == 0) { - // ugly hack to initialize the rest of the engine - // because of the way android forces you to do everything with threads + const char **cmdline = NULL; + int cmdlen = 0; + bool use_apk_expansion = false; + if (p_cmdline) { + cmdlen = env->GetArrayLength(p_cmdline); + if (cmdlen) { + cmdline = (const char **)malloc((env->GetArrayLength(p_cmdline) + 1) * sizeof(const char *)); + cmdline[cmdlen] = NULL; - java_class_wrapper = memnew(JavaClassWrapper(_godot_instance)); - ProjectSettings::get_singleton()->add_singleton(ProjectSettings::Singleton("JavaClassWrapper", java_class_wrapper)); - _initialize_java_modules(); + for (int i = 0; i < cmdlen; i++) { - // Since Godot is initialized on the UI thread, _main_thread_id was set to that thread's id, - // but for Godot purposes, the main thread is the one running the game loop - Main::setup2(Thread::get_caller_id()); - ++step; - suspend_mutex->unlock(); - input_mutex->unlock(); - return; - }; - if (step == 1) { - if (!Main::start()) { + jstring string = (jstring)env->GetObjectArrayElement(p_cmdline, i); + const char *rawString = env->GetStringUTFChars(string, 0); + if (!rawString) { + __android_log_print(ANDROID_LOG_INFO, "godot", "cmdline arg %i is null\n", i); + } else { + //__android_log_print(ANDROID_LOG_INFO,"godot","cmdline arg %i is: %s\n",i,rawString); - input_mutex->unlock(); - suspend_mutex->lock(); - return; //should exit instead and print the error + if (strcmp(rawString, "-main_pack") == 0) + use_apk_expansion = true; + } + + cmdline[i] = rawString; + } } + } + __android_log_print(ANDROID_LOG_INFO, "godot", "CMDLINE LEN %i - APK EXPANSION %i\n", cmdlen, int(use_apk_expansion)); - os_android->main_loop_begin(); - ++step; +#if 0 + char *args[]={"-test","render",NULL}; + __android_log_print(ANDROID_LOG_INFO,"godot","pre asdasd setup..."); + Error err = Main::setup("apk",2,args,false); +#else + Error err = Main::setup("apk", cmdlen, (char **)cmdline, false); +#endif + if (cmdline) { + free(cmdline); } - while (pointer_events.size()) { + if (err != OK) { + __android_log_print(ANDROID_LOG_INFO, "godot", "*****UNABLE TO SETUP"); + return; //should exit instead and print the error + } + __android_log_print(ANDROID_LOG_INFO, "godot", "*****SETUP OK"); - JAndroidPointerEvent jpe = pointer_events.front()->get(); - os_android->process_touch(jpe.what, jpe.pointer, jpe.points); + java_class_wrapper = memnew(JavaClassWrapper(_godot_instance)); + ProjectSettings::get_singleton()->add_singleton(ProjectSettings::Singleton("JavaClassWrapper", java_class_wrapper)); + _initialize_java_modules(); +} - pointer_events.pop_front(); - } +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jobject obj, jint width, jint height, jboolean reload) { - while (key_events.size()) { + __android_log_print(ANDROID_LOG_INFO, "godot", "^_^_^_^_^ resize %lld, %i, %i\n", Thread::get_caller_id(), width, height); + if (os_android) + os_android->set_display_size(Size2(width, height)); - Ref<InputEvent> event = key_events.front()->get(); - os_android->process_event(event); + /*input_mutex->lock(); + resized=true; + if (reload) + resized_reload=true; + new_size=Size2(width,height); + input_mutex->unlock();*/ +} - key_events.pop_front(); - }; +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jobject obj, bool p_32_bits) { - while (joy_events.size()) { + __android_log_print(ANDROID_LOG_INFO, "godot", "^_^_^_^_^ newcontext %lld\n", Thread::get_caller_id()); + + if (os_android) { + os_android->set_context_is_16_bits(!p_32_bits); + } - OS_Android::JoypadEvent event = joy_events.front()->get(); - os_android->process_joy_event(event); + if (os_android && step > 0) { - joy_events.pop_front(); + os_android->reload_gfx(); } +} - if (go_back_request) { +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jobject obj) { + os_android->main_loop_request_go_back(); +} - os_android->main_loop_request_go_back(); - go_back_request = false; +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jobject obj) { + if (step == 0) { + __android_log_print(ANDROID_LOG_INFO, "godot", "**FIRST_STEP"); + + // Since Godot is initialized on the UI thread, _main_thread_id was set to that thread's id, + // but for Godot purposes, the main thread is the one running the game loop + Main::setup2(Thread::get_caller_id()); + ++step; + return; + } + + if (step == 1) { + if (!Main::start()) { + return; //should exit instead and print the error + } + + os_android->main_loop_begin(); + ++step; } - input_mutex->unlock(); + //__android_log_print(ANDROID_LOG_INFO,"godot","**STEP EVENT! - %p-%i\n",env,Thread::get_caller_id()); os_android->process_accelerometer(accelerometer); @@ -1067,8 +1023,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, job env->CallVoidMethod(_godot_instance, _finish); __android_log_print(ANDROID_LOG_INFO, "godot", "**FINISH REQUEST!!! - %p-%i\n", env, Thread::get_caller_id()); } - - suspend_mutex->unlock(); } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch(JNIEnv *env, jobject obj, jint ev, jint pointer, jint count, jintArray positions) { @@ -1086,16 +1040,8 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch(JNIEnv *env, jo points.push_back(tp); } - JAndroidPointerEvent jpe; - jpe.pointer = pointer; - jpe.points = points; - jpe.what = ev; - - input_mutex->lock(); - - pointer_events.push_back(jpe); + os_android->process_touch(ev, pointer, points); - input_mutex->unlock(); /* if (os_android) os_android->process_touch(ev,pointer,points); @@ -1365,9 +1311,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env jevent.index = p_button; jevent.pressed = p_pressed; - input_mutex->lock(); - joy_events.push_back(jevent); - input_mutex->unlock(); + os_android->process_joy_event(jevent); } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jobject obj, jint p_device, jint p_axis, jfloat p_value) { @@ -1378,9 +1322,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jevent.index = p_axis; jevent.value = p_value; - input_mutex->lock(); - joy_events.push_back(jevent); - input_mutex->unlock(); + os_android->process_joy_event(jevent); } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, jobject obj, jint p_device, jint p_hat_x, jint p_hat_y) { @@ -1401,9 +1343,8 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, j hat |= InputDefault::HAT_MASK_DOWN; } jevent.hat = hat; - input_mutex->lock(); - joy_events.push_back(jevent); - input_mutex->unlock(); + + os_android->process_joy_event(jevent); } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyconnectionchanged(JNIEnv *env, jobject obj, jint p_device, jboolean p_connected, jstring p_name) { @@ -1416,6 +1357,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyconnectionchanged( JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jobject obj, jint p_scancode, jint p_unicode_char, jboolean p_pressed) { Ref<InputEventKey> ievent; + ievent.instance(); int val = p_unicode_char; int scancode = android_get_keysym(p_scancode); ievent->set_scancode(scancode); @@ -1434,57 +1376,34 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jobj ievent->set_unicode(KEY_ENTER); } else if (p_scancode == 4) { - go_back_request = true; + os_android->main_loop_request_go_back(); } - input_mutex->lock(); - key_events.push_back(ievent); - input_mutex->unlock(); + os_android->process_event(ievent); } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_accelerometer(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z) { - - input_mutex->lock(); accelerometer = Vector3(x, y, z); - input_mutex->unlock(); } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_magnetometer(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z) { - - input_mutex->lock(); magnetometer = Vector3(x, y, z); - input_mutex->unlock(); } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gyroscope(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z) { - - input_mutex->lock(); gyroscope = Vector3(x, y, z); - input_mutex->unlock(); } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusin(JNIEnv *env, jobject obj) { - if (!suspend_mutex) - return; - suspend_mutex->lock(); - if (os_android && step > 0) os_android->main_loop_focusin(); - - suspend_mutex->unlock(); } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusout(JNIEnv *env, jobject obj) { - if (!suspend_mutex) - return; - suspend_mutex->lock(); - if (os_android && step > 0) os_android->main_loop_focusout(); - - suspend_mutex->unlock(); } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_audio(JNIEnv *env, jobject obj) { diff --git a/platform/android/java_glue.h b/platform/android/java_glue.h index 64970040f8..0aa2489813 100644 --- a/platform/android/java_glue.h +++ b/platform/android/java_glue.h @@ -36,7 +36,8 @@ #include <jni.h> extern "C" { -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jobject obj, jobject activity, jboolean p_need_reload_hook, jobjectArray p_cmdline, jobject p_asset_manager); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jobject obj, jobject activity, jboolean p_need_reload_hook, jobject p_asset_manager, jboolean p_use_apk_expansion); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jobject obj, jobjectArray p_cmdline); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jobject obj, jint width, jint height, jboolean reload); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jobject obj, bool p_32_bits); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jobject obj); @@ -58,6 +59,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_method(JNIEnv *env, j JNIEXPORT jstring JNICALL Java_org_godotengine_godot_GodotLib_getGlobal(JNIEnv *env, jobject obj, jstring path); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_callobject(JNIEnv *env, jobject p_obj, jint ID, jstring method, jobjectArray params); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv *env, jobject p_obj, jint ID, jstring method, jobjectArray params); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHeight(JNIEnv *env, jobject obj, jint p_height); } #endif diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index 1de2608b9e..45df312cae 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -47,6 +47,15 @@ #include "file_access_jandroid.h" #endif +class AndroidLogger : public Logger { +public: + virtual void logv(const char *p_format, va_list p_list, bool p_err) { + __android_log_vprint(p_err ? ANDROID_LOG_ERROR : ANDROID_LOG_INFO, "godot", p_format, p_list); + } + + virtual ~AndroidLogger() {} +}; + int OS_Android::get_video_driver_count() const { return 1; @@ -111,6 +120,15 @@ void OS_Android::initialize_core() { #endif } +void OS_Android::initialize_logger() { + Vector<Logger *> loggers; + loggers.push_back(memnew(AndroidLogger)); + // FIXME: Reenable once we figure out how to get this properly in user:// + // instead of littering the user's working dirs (res:// + pwd) with log files (GH-12277) + //loggers.push_back(memnew(RotatedFileLogger("user://logs/log.txt"))); + _set_logger(memnew(CompositeLogger(loggers))); +} + void OS_Android::set_opengl_extensions(const char *p_gl_extensions) { ERR_FAIL_COND(!p_gl_extensions); @@ -137,12 +155,7 @@ void OS_Android::initialize(const VideoMode &p_desired, int p_video_driver, int visual_server->init(); // visual_server->cursor_set_visible(false, 0); - AudioDriverManager::get_driver(p_audio_driver)->set_singleton(); - - if (AudioDriverManager::get_driver(p_audio_driver)->init() != OK) { - - ERR_PRINT("Initializing audio failed."); - } + AudioDriverManager::initialize(p_audio_driver); physics_server = memnew(PhysicsServerSW); physics_server->init(); @@ -167,23 +180,9 @@ void OS_Android::delete_main_loop() { } void OS_Android::finalize() { - memdelete(input); } -void OS_Android::vprint(const char *p_format, va_list p_list, bool p_stderr) { - - __android_log_vprint(p_stderr ? ANDROID_LOG_ERROR : ANDROID_LOG_INFO, "godot", p_format, p_list); -} - -void OS_Android::print(const char *p_format, ...) { - - va_list argp; - va_start(argp, p_format); - __android_log_vprint(ANDROID_LOG_INFO, "godot", p_format, argp); - va_end(argp); -} - void OS_Android::alert(const String &p_alert, const String &p_title) { //print("ALERT: %s\n", p_alert.utf8().get_data()); @@ -522,6 +521,15 @@ bool OS_Android::has_virtual_keyboard() const { return true; } +int OS_Android::get_virtual_keyboard_height() const { + if (get_virtual_keyboard_height_func) { + return get_virtual_keyboard_height_func(); + } + + ERR_PRINT("Cannot obtain virtual keyboard height."); + return 0; +} + void OS_Android::show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect) { if (show_virtual_keyboard_func) { @@ -707,7 +715,7 @@ bool OS_Android::_check_internal_feature_support(const String &p_feature) { return p_feature == "mobile" || p_feature == "etc" || p_feature == "etc2"; //TODO support etc2 only if GLES3 driver is selected } -OS_Android::OS_Android(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, OpenURIFunc p_open_uri_func, GetDataDirFunc p_get_data_dir_func, GetLocaleFunc p_get_locale_func, GetModelFunc p_get_model_func, GetScreenDPIFunc p_get_screen_dpi_func, ShowVirtualKeyboardFunc p_show_vk, HideVirtualKeyboardFunc p_hide_vk, SetScreenOrientationFunc p_screen_orient, GetUniqueIDFunc p_get_unique_id, GetSystemDirFunc p_get_sdir_func, VideoPlayFunc p_video_play_func, VideoIsPlayingFunc p_video_is_playing_func, VideoPauseFunc p_video_pause_func, VideoStopFunc p_video_stop_func, SetKeepScreenOnFunc p_set_keep_screen_on_func, AlertFunc p_alert_func, bool p_use_apk_expansion) { +OS_Android::OS_Android(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, OpenURIFunc p_open_uri_func, GetDataDirFunc p_get_data_dir_func, GetLocaleFunc p_get_locale_func, GetModelFunc p_get_model_func, GetScreenDPIFunc p_get_screen_dpi_func, ShowVirtualKeyboardFunc p_show_vk, HideVirtualKeyboardFunc p_hide_vk, VirtualKeyboardHeightFunc p_vk_height_func, SetScreenOrientationFunc p_screen_orient, GetUniqueIDFunc p_get_unique_id, GetSystemDirFunc p_get_sdir_func, VideoPlayFunc p_video_play_func, VideoIsPlayingFunc p_video_is_playing_func, VideoPauseFunc p_video_pause_func, VideoStopFunc p_video_stop_func, SetKeepScreenOnFunc p_set_keep_screen_on_func, AlertFunc p_alert_func, bool p_use_apk_expansion) { use_apk_expansion = p_use_apk_expansion; default_videomode.width = 800; @@ -737,11 +745,14 @@ OS_Android::OS_Android(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, OpenURI show_virtual_keyboard_func = p_show_vk; hide_virtual_keyboard_func = p_hide_vk; + get_virtual_keyboard_height_func = p_vk_height_func; set_screen_orientation_func = p_screen_orient; set_keep_screen_on_func = p_set_keep_screen_on_func; alert_func = p_alert_func; use_reload_hooks = false; + + _set_logger(memnew(AndroidLogger)); } OS_Android::~OS_Android() { diff --git a/platform/android/os_android.h b/platform/android/os_android.h index 119c14bff3..0c78c198a8 100644 --- a/platform/android/os_android.h +++ b/platform/android/os_android.h @@ -67,6 +67,7 @@ typedef void (*VideoPauseFunc)(); typedef void (*VideoStopFunc)(); typedef void (*SetKeepScreenOnFunc)(bool p_enabled); typedef void (*AlertFunc)(const String &, const String &); +typedef int (*VirtualKeyboardHeightFunc)(); class OS_Android : public OS_Unix { public: @@ -126,6 +127,7 @@ private: GetScreenDPIFunc get_screen_dpi_func; ShowVirtualKeyboardFunc show_virtual_keyboard_func; HideVirtualKeyboardFunc hide_virtual_keyboard_func; + VirtualKeyboardHeightFunc get_virtual_keyboard_height_func; SetScreenOrientationFunc set_screen_orientation_func; GetUniqueIDFunc get_unique_id_func; GetSystemDirFunc get_system_dir_func; @@ -149,6 +151,7 @@ public: virtual int get_audio_driver_count() const; virtual const char *get_audio_driver_name(int p_driver) const; + virtual void initialize_logger(); virtual void initialize_core(); virtual void initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver); @@ -161,8 +164,6 @@ public: static OS *get_singleton(); - virtual void vprint(const char *p_format, va_list p_list, bool p_stderr = false); - virtual void print(const char *p_format, ...); virtual void alert(const String &p_alert, const String &p_title = "ALERT!"); virtual void set_mouse_show(bool p_show); @@ -202,6 +203,7 @@ public: virtual bool has_virtual_keyboard() const; virtual void show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2()); virtual void hide_virtual_keyboard(); + virtual int get_virtual_keyboard_height() const; void set_opengl_extensions(const char *p_gl_extensions); void set_display_size(Size2 p_size); @@ -241,7 +243,7 @@ public: void joy_connection_changed(int p_device, bool p_connected, String p_name); virtual bool _check_internal_feature_support(const String &p_feature); - OS_Android(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, OpenURIFunc p_open_uri_func, GetDataDirFunc p_get_data_dir_func, GetLocaleFunc p_get_locale_func, GetModelFunc p_get_model_func, GetScreenDPIFunc p_get_screen_dpi_func, ShowVirtualKeyboardFunc p_show_vk, HideVirtualKeyboardFunc p_hide_vk, SetScreenOrientationFunc p_screen_orient, GetUniqueIDFunc p_get_unique_id, GetSystemDirFunc p_get_sdir_func, VideoPlayFunc p_video_play_func, VideoIsPlayingFunc p_video_is_playing_func, VideoPauseFunc p_video_pause_func, VideoStopFunc p_video_stop_func, SetKeepScreenOnFunc p_set_keep_screen_on_func, AlertFunc p_alert_func, bool p_use_apk_expansion); + OS_Android(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, OpenURIFunc p_open_uri_func, GetDataDirFunc p_get_data_dir_func, GetLocaleFunc p_get_locale_func, GetModelFunc p_get_model_func, GetScreenDPIFunc p_get_screen_dpi_func, ShowVirtualKeyboardFunc p_show_vk, HideVirtualKeyboardFunc p_hide_vk, VirtualKeyboardHeightFunc p_vk_height_func, SetScreenOrientationFunc p_screen_orient, GetUniqueIDFunc p_get_unique_id, GetSystemDirFunc p_get_sdir_func, VideoPlayFunc p_video_play_func, VideoIsPlayingFunc p_video_is_playing_func, VideoPauseFunc p_video_pause_func, VideoStopFunc p_video_stop_func, SetKeepScreenOnFunc p_set_keep_screen_on_func, AlertFunc p_alert_func, bool p_use_apk_expansion); ~OS_Android(); }; diff --git a/platform/android/power_android.cpp b/platform/android/power_android.cpp index 98d4d810b9..48c9377a5a 100644 --- a/platform/android/power_android.cpp +++ b/platform/android/power_android.cpp @@ -198,19 +198,19 @@ bool power_android::GetPowerInfo_Android() { if (Android_JNI_GetPowerInfo(&plugged, &charged, &battery, &this->nsecs_left, &this->percent_left) != -1) { if (plugged) { if (charged) { - this->power_state = POWERSTATE_CHARGED; + this->power_state = OS::POWERSTATE_CHARGED; } else if (battery) { - this->power_state = POWERSTATE_CHARGING; + this->power_state = OS::POWERSTATE_CHARGING; } else { - this->power_state = POWERSTATE_NO_BATTERY; + this->power_state = OS::POWERSTATE_NO_BATTERY; this->nsecs_left = -1; this->percent_left = -1; } } else { - this->power_state = POWERSTATE_ON_BATTERY; + this->power_state = OS::POWERSTATE_ON_BATTERY; } } else { - this->power_state = POWERSTATE_UNKNOWN; + this->power_state = OS::POWERSTATE_UNKNOWN; this->nsecs_left = -1; this->percent_left = -1; } @@ -218,12 +218,12 @@ bool power_android::GetPowerInfo_Android() { return true; } -PowerState power_android::get_power_state() { +OS::PowerState power_android::get_power_state() { if (GetPowerInfo_Android()) { return power_state; } else { WARN_PRINT("Power management is not implemented on this platform, defaulting to POWERSTATE_UNKNOWN"); - return POWERSTATE_UNKNOWN; + return OS::POWERSTATE_UNKNOWN; } } @@ -246,7 +246,7 @@ int power_android::get_power_percent_left() { } power_android::power_android() - : nsecs_left(-1), percent_left(-1), power_state(POWERSTATE_UNKNOWN) { + : nsecs_left(-1), percent_left(-1), power_state(OS::POWERSTATE_UNKNOWN) { } power_android::~power_android() { diff --git a/platform/android/power_android.h b/platform/android/power_android.h index fc618c660d..4c7af1c771 100644 --- a/platform/android/power_android.h +++ b/platform/android/power_android.h @@ -31,7 +31,7 @@ #ifndef PLATFORM_ANDROID_POWER_ANDROID_H_ #define PLATFORM_ANDROID_POWER_ANDROID_H_ -#include "os/power.h" +#include "os/os.h" #include <android/native_window_jni.h> class power_android { @@ -57,7 +57,7 @@ private: int nsecs_left; int percent_left; - PowerState power_state; + OS::PowerState power_state; bool GetPowerInfo_Android(); bool UpdatePowerInfo(); @@ -71,7 +71,7 @@ public: static struct LocalReferenceHolder LocalReferenceHolder_Setup(const char *func); static void LocalReferenceHolder_Cleanup(struct LocalReferenceHolder *refholder); - PowerState get_power_state(); + OS::PowerState get_power_state(); int get_power_seconds_left(); int get_power_percent_left(); }; diff --git a/platform/haiku/context_gl_haiku.cpp b/platform/haiku/context_gl_haiku.cpp index 2b943df5ba..80d0bd78d5 100644 --- a/platform/haiku/context_gl_haiku.cpp +++ b/platform/haiku/context_gl_haiku.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "context_gl_haiku.h" -#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED) +#if defined(OPENGL_ENABLED) ContextGL_Haiku::ContextGL_Haiku(HaikuDirectWindow *p_window) { window = p_window; diff --git a/platform/haiku/context_gl_haiku.h b/platform/haiku/context_gl_haiku.h index 40daf43ab9..a9a13a2b7f 100644 --- a/platform/haiku/context_gl_haiku.h +++ b/platform/haiku/context_gl_haiku.h @@ -30,7 +30,7 @@ #ifndef CONTEXT_GL_HAIKU_H #define CONTEXT_GL_HAIKU_H -#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED) +#if defined(OPENGL_ENABLED) #include "drivers/gl_context/context_gl.h" diff --git a/platform/haiku/detect.py b/platform/haiku/detect.py index c0e003a3d2..50f9783dd2 100644 --- a/platform/haiku/detect.py +++ b/platform/haiku/detect.py @@ -19,9 +19,10 @@ def can_build(): def get_opts(): + from SCons.Variables import EnumVariable return [ - ('debug_release', 'Add debug symbols to release version', 'no') + EnumVariable('debug_symbols', 'Add debug symbols to release version', 'yes', ('yes', 'no', 'full')), ] @@ -36,16 +37,21 @@ def configure(env): ## Build type if (env["target"] == "release"): - if (env["debug_release"] == "yes"): + env.Prepend(CCFLAGS=['-O3', '-ffast-math']) + if (env["debug_symbols"] == "yes"): + env.Prepend(CCFLAGS=['-g1']) + if (env["debug_symbols"] == "full"): env.Prepend(CCFLAGS=['-g2']) - else: - env.Prepend(CCFLAGS=['-O3', '-ffast-math']) elif (env["target"] == "release_debug"): env.Prepend(CCFLAGS=['-O2', '-ffast-math', '-DDEBUG_ENABLED']) + if (env["debug_symbols"] == "yes"): + env.Prepend(CCFLAGS=['-g1']) + if (env["debug_symbols"] == "full"): + env.Prepend(CCFLAGS=['-g2']) elif (env["target"] == "debug"): - env.Prepend(CCFLAGS=['-g2', '-DDEBUG_ENABLED', '-DDEBUG_MEMORY_ENABLED']) + env.Prepend(CCFLAGS=['-g3', '-DDEBUG_ENABLED', '-DDEBUG_MEMORY_ENABLED']) ## Architecture diff --git a/platform/haiku/haiku_direct_window.cpp b/platform/haiku/haiku_direct_window.cpp index 572df493ff..24a8a4b17b 100644 --- a/platform/haiku/haiku_direct_window.cpp +++ b/platform/haiku/haiku_direct_window.cpp @@ -157,8 +157,8 @@ void HaikuDirectWindow::HandleMouseButton(BMessage *message) { mouse_event.mouse_button.mod = GetKeyModifierState(modifiers); mouse_event->get_button_mask() = GetMouseButtonState(buttons); - mouse_event->get_pos().x = where.x; - mouse_event->get_pos().y = where.y; + mouse_event->get_position().x = where.x; + mouse_event->get_position().y = where.y; mouse_event.mouse_button.global_x = where.x; mouse_event.mouse_button.global_y = where.y; @@ -242,8 +242,8 @@ void HaikuDirectWindow::HandleMouseWheelChanged(BMessage *message) { mouse_event->get_button_index() = wheel_delta_y < 0 ? 4 : 5; mouse_event.mouse_button.mod = GetKeyModifierState(last_key_modifier_state); mouse_event->get_button_mask() = last_button_mask; - mouse_event->get_pos().x = last_mouse_position.x; - mouse_event->get_pos().y = last_mouse_position.y; + mouse_event->get_position().x = last_mouse_position.x; + mouse_event->get_position().y = last_mouse_position.y; mouse_event.mouse_button.global_x = last_mouse_position.x; mouse_event.mouse_button.global_y = last_mouse_position.y; diff --git a/platform/haiku/os_haiku.cpp b/platform/haiku/os_haiku.cpp index de2f79a0ac..1d52752f21 100644 --- a/platform/haiku/os_haiku.cpp +++ b/platform/haiku/os_haiku.cpp @@ -105,7 +105,7 @@ void OS_Haiku::initialize(const VideoMode &p_desired, int p_video_driver, int p_ window->SetFlags(flags); } -#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED) +#if defined(OPENGL_ENABLED) context_gl = memnew(ContextGL_Haiku(window)); context_gl->initialize(); context_gl->make_current(); @@ -137,11 +137,7 @@ void OS_Haiku::initialize(const VideoMode &p_desired, int p_video_driver, int p_ //physics_2d_server = Physics2DServerWrapMT::init_server<Physics2DServerSW>(); physics_2d_server->init(); - AudioDriverManager::get_driver(p_audio_driver)->set_singleton(); - - if (AudioDriverManager::get_driver(p_audio_driver)->init() != OK) { - ERR_PRINT("Initializing audio failed."); - } + AudioDriverManager::initialize(p_audio_driver); power_manager = memnew(PowerHaiku); } @@ -165,7 +161,7 @@ void OS_Haiku::finalize() { memdelete(input); -#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED) +#if defined(OPENGL_ENABLED) memdelete(context_gl); #endif } diff --git a/platform/haiku/os_haiku.h b/platform/haiku/os_haiku.h index cb68f9303f..d929f7e43b 100644 --- a/platform/haiku/os_haiku.h +++ b/platform/haiku/os_haiku.h @@ -60,7 +60,7 @@ private: AudioDriverMediaKit driver_media_kit; #endif -#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED) +#if defined(OPENGL_ENABLED) ContextGL_Haiku *context_gl; #endif @@ -117,7 +117,7 @@ public: virtual void get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen = 0) const; virtual String get_executable_path() const; - virtual PowerState get_power_state(); + virtual OS::PowerState get_power_state(); virtual int get_power_seconds_left(); virtual int get_power_percent_left(); diff --git a/platform/haiku/power_haiku.cpp b/platform/haiku/power_haiku.cpp index 449b43a621..8718b317a3 100644 --- a/platform/haiku/power_haiku.cpp +++ b/platform/haiku/power_haiku.cpp @@ -37,12 +37,12 @@ bool PowerHaiku::UpdatePowerInfo() { return false; } -PowerState PowerHaiku::get_power_state() { +OS::PowerState PowerHaiku::get_power_state() { if (UpdatePowerInfo()) { return power_state; } else { WARN_PRINT("Power management is not implemented on this platform, defaulting to POWERSTATE_UNKNOWN"); - return POWERSTATE_UNKNOWN; + return OS::POWERSTATE_UNKNOWN; } } @@ -65,7 +65,7 @@ int PowerX11::get_power_percent_left() { } PowerHaiku::PowerHaiku() - : nsecs_left(-1), percent_left(-1), power_state(POWERSTATE_UNKNOWN) { + : nsecs_left(-1), percent_left(-1), power_state(OS::POWERSTATE_UNKNOWN) { } PowerHaiku::~PowerHaiku() { diff --git a/platform/haiku/power_haiku.h b/platform/haiku/power_haiku.h index 12513bdaef..59a93cd976 100644 --- a/platform/haiku/power_haiku.h +++ b/platform/haiku/power_haiku.h @@ -31,11 +31,13 @@ #ifndef PLATFORM_HAIKU_POWER_HAIKU_H_ #define PLATFORM_HAIKU_POWER_HAIKU_H_ +#include <os/os.h> + class PowerHaiku { private: int nsecs_left; int percent_left; - PowerState power_state; + OS::PowerState power_state; bool UpdatePowerInfo(); @@ -43,7 +45,7 @@ public: PowerHaiku(); virtual ~PowerHaiku(); - PowerState get_power_state(); + OS::PowerState get_power_state(); int get_power_seconds_left(); int get_power_percent_left(); }; diff --git a/platform/iphone/SCsub b/platform/iphone/SCsub index 998d0a3f0d..61798c5f87 100644 --- a/platform/iphone/SCsub +++ b/platform/iphone/SCsub @@ -5,7 +5,6 @@ Import('env') iphone_lib = [ 'os_iphone.cpp', - 'audio_driver_iphone.cpp', 'sem_iphone.cpp', 'gl_view.mm', 'main.m', diff --git a/platform/iphone/app_delegate.mm b/platform/iphone/app_delegate.mm index c7b65b476b..65cafbd6d4 100644 --- a/platform/iphone/app_delegate.mm +++ b/platform/iphone/app_delegate.mm @@ -29,8 +29,8 @@ /*************************************************************************/ #import "app_delegate.h" -#include "audio_driver_iphone.h" #include "core/project_settings.h" +#include "drivers/coreaudio/audio_driver_coreaudio.h" #import "gl_view.h" #include "main/main.h" #include "os_iphone.h" @@ -81,7 +81,7 @@ void _set_keep_screen_on(bool p_enabled) { extern int gargc; extern char **gargv; -extern int iphone_main(int, int, int, char **); +extern int iphone_main(int, int, int, char **, String); extern void iphone_finish(); CMMotionManager *motionManager; @@ -393,15 +393,6 @@ static int frame_count = 0; }; ++frame_count; - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, - NSUserDomainMask, YES); - NSString *documentsDirectory = [paths objectAtIndex:0]; - // NSString *documentsDirectory = [[[NSFileManager defaultManager] - // URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] - // lastObject]; - OSIPhone::get_singleton()->set_data_dir( - String::utf8([documentsDirectory UTF8String])); - NSString *locale_code = [[NSLocale currentLocale] localeIdentifier]; OSIPhone::get_singleton()->set_locale( String::utf8([locale_code UTF8String])); @@ -604,7 +595,11 @@ static int frame_count = 0; glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight); - int err = iphone_main(backingWidth, backingHeight, gargc, gargv); + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, + NSUserDomainMask, YES); + NSString *documentsDirectory = [paths objectAtIndex:0]; + + int err = iphone_main(backingWidth, backingHeight, gargc, gargv, String::utf8([documentsDirectory UTF8String])); if (err != 0) { // bail, things did not go very well for us, should probably output a message on screen with our error code... exit(0); @@ -736,8 +731,8 @@ static int frame_count = 0; }; // Fixed audio can not resume if it is interrupted cause by an incoming phone call - if (AudioDriverIphone::get_singleton() != NULL) - AudioDriverIphone::get_singleton()->start(); + if (AudioDriverCoreAudio::get_singleton() != NULL) + AudioDriverCoreAudio::get_singleton()->start(); } - (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url { diff --git a/platform/iphone/audio_driver_iphone.cpp b/platform/iphone/audio_driver_iphone.cpp deleted file mode 100644 index dbc5bdb654..0000000000 --- a/platform/iphone/audio_driver_iphone.cpp +++ /dev/null @@ -1,187 +0,0 @@ -/*************************************************************************/ -/* audio_driver_iphone.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ -#include "audio_driver_iphone.h" - -Error AudioDriverIphone::init() { - - active = false; - channels = 2; - - AudioStreamBasicDescription strdesc; - strdesc.mFormatID = kAudioFormatLinearPCM; - strdesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; - strdesc.mChannelsPerFrame = channels; - strdesc.mSampleRate = 44100; - strdesc.mFramesPerPacket = 1; - strdesc.mBitsPerChannel = 16; - strdesc.mBytesPerFrame = - strdesc.mBitsPerChannel * strdesc.mChannelsPerFrame / 8; - strdesc.mBytesPerPacket = - strdesc.mBytesPerFrame * strdesc.mFramesPerPacket; - - AURenderCallbackStruct callback; - AudioComponentDescription desc; - const AudioUnitElement output_bus = 0; - const AudioUnitElement bus = output_bus; - const AudioUnitScope scope = kAudioUnitScope_Input; - - zeromem(&desc, sizeof(desc)); - desc.componentType = kAudioUnitType_Output; - desc.componentSubType = kAudioUnitSubType_RemoteIO; /* !!! FIXME: ? */ - AudioComponent comp = AudioComponentFindNext(NULL, &desc); - desc.componentManufacturer = kAudioUnitManufacturer_Apple; - - OSStatus 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)); - ERR_FAIL_COND_V(result != noErr, FAILED); - - zeromem(&callback, sizeof(AURenderCallbackStruct)); - callback.inputProc = &AudioDriverIphone::output_callback; - callback.inputProcRefCon = this; - 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); - ERR_FAIL_COND_V(result != noErr, FAILED); - - const int samples = 1024; - samples_in = memnew_arr(int32_t, samples); // whatever - buffer_frames = samples / channels; - - return FAILED; -}; - -OSStatus AudioDriverIphone::output_callback(void *inRefCon, - AudioUnitRenderActionFlags *ioActionFlags, - const AudioTimeStamp *inTimeStamp, - UInt32 inBusNumber, UInt32 inNumberFrames, - AudioBufferList *ioData) { - - AudioBuffer *abuf; - AudioDriverIphone *ad = (AudioDriverIphone *)inRefCon; - - bool mix = true; - - if (!ad->active) - mix = false; - else if (ad->mutex) { - mix = ad->mutex->try_lock() == OK; - }; - - if (!mix) { - for (unsigned int i = 0; i < ioData->mNumberBuffers; i++) { - 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; - 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++) { - - out[i] = ad->samples_in[i] >> 16; - } - - frames_left -= frames; - out += frames * ad->channels; - }; - }; - - if (ad->mutex) - ad->mutex->unlock(); - - return 0; -}; - -void AudioDriverIphone::start() { - active = true; - // Resume audio - // iOS audio-thread stoped if it is interrupted cause by an incoming phone call - // Use AudioOutputUnitStart to re-create audio-thread - OSStatus result = AudioOutputUnitStart(audio_unit); - ERR_FAIL_COND(result != noErr); -}; - -int AudioDriverIphone::get_mix_rate() const { - return 44100; -}; - -AudioDriver::SpeakerMode AudioDriverIphone::get_speaker_mode() const { - return SPEAKER_MODE_STEREO; -}; - -void AudioDriverIphone::lock() { - - if (active && mutex) - mutex->lock(); -}; - -void AudioDriverIphone::unlock() { - if (active && mutex) - mutex->unlock(); -}; - -void AudioDriverIphone::finish() { - - memdelete_arr(samples_in); -}; - -AudioDriverIphone::AudioDriverIphone() { - - mutex = Mutex::create(); //NULL; -}; - -AudioDriverIphone::~AudioDriverIphone(){ - -}; diff --git a/platform/iphone/detect.py b/platform/iphone/detect.py index 0b81422fa3..00d8a59f74 100644 --- a/platform/iphone/detect.py +++ b/platform/iphone/detect.py @@ -20,24 +20,24 @@ def can_build(): def get_opts(): - + from SCons.Variables import BoolVariable return [ ('IPHONEPLATFORM', 'Name of the iPhone platform', 'iPhoneOS'), ('IPHONEPATH', 'Path to iPhone toolchain', '/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain'), ('IPHONESDK', 'Path to the iPhone SDK', '/Applications/Xcode.app/Contents/Developer/Platforms/${IPHONEPLATFORM}.platform/Developer/SDKs/${IPHONEPLATFORM}.sdk/'), - ('game_center', 'Support for game center', 'yes'), - ('store_kit', 'Support for in-app store', 'yes'), - ('icloud', 'Support for iCloud', 'yes'), - ('ios_exceptions', 'Enable exceptions', 'no'), + BoolVariable('game_center', 'Support for game center', True), + BoolVariable('store_kit', 'Support for in-app store', True), + BoolVariable('icloud', 'Support for iCloud', True), + BoolVariable('ios_exceptions', 'Enable exceptions', False), ('ios_triple', 'Triple for ios toolchain', ''), - ('ios_sim', 'Build simulator binary', 'no'), + BoolVariable('ios_sim', 'Build simulator binary', False), ] def get_flags(): return [ - ('tools', 'no'), + ('tools', False), ] @@ -58,7 +58,7 @@ def configure(env): ## Architecture - if (env["ios_sim"] == "yes" or env["arch"] == "x86"): # i386, simulator + if env["ios_sim"] or env["arch"] == "x86": # i386, simulator env["arch"] = "x86" env["bits"] = "32" elif (env["arch"] == "arm" or env["arch"] == "arm32" or env["arch"] == "armv7" or env["bits"] == "32"): # arm @@ -91,7 +91,7 @@ def configure(env): env.Append(CPPFLAGS=['-DNEED_LONG_INT']) env.Append(CPPFLAGS=['-DLIBYUV_DISABLE_NEON']) - if env['ios_exceptions'] == 'yes': + if env['ios_exceptions']: env.Append(CPPFLAGS=['-fexceptions']) else: env.Append(CPPFLAGS=['-fno-exceptions']) @@ -129,15 +129,15 @@ def configure(env): ]) # Feature options - if env['game_center'] == 'yes': + if env['game_center']: env.Append(CPPFLAGS=['-DGAME_CENTER_ENABLED']) env.Append(LINKFLAGS=['-framework', 'GameKit']) - if env['store_kit'] == 'yes': + if env['store_kit']: env.Append(CPPFLAGS=['-DSTOREKIT_ENABLED']) env.Append(LINKFLAGS=['-framework', 'StoreKit']) - if env['icloud'] == 'yes': + if env['icloud']: env.Append(CPPFLAGS=['-DICLOUD_ENABLED']) env.Append(CPPPATH=['$IPHONESDK/usr/include', @@ -148,10 +148,10 @@ def configure(env): env['ENV']['CODESIGN_ALLOCATE'] = '/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/codesign_allocate' env.Append(CPPPATH=['#platform/iphone']) - env.Append(CPPFLAGS=['-DIPHONE_ENABLED', '-DUNIX_ENABLED', '-DGLES2_ENABLED', '-DMPC_FIXED_POINT']) + env.Append(CPPFLAGS=['-DIPHONE_ENABLED', '-DUNIX_ENABLED', '-DGLES2_ENABLED', '-DMPC_FIXED_POINT', '-DCOREAUDIO_ENABLED']) # TODO: Move that to opus module's config - if("module_opus_enabled" in env and env["module_opus_enabled"] != "no"): + if 'module_opus_enabled' in env and env['module_opus_enabled']: env.opus_fixed_point = "yes" if (env["arch"] == "arm"): env.Append(CFLAGS=["-DOPUS_ARM_OPT"]) diff --git a/platform/iphone/export/export.cpp b/platform/iphone/export/export.cpp index 8bb7f23ead..0507ef19d6 100644 --- a/platform/iphone/export/export.cpp +++ b/platform/iphone/export/export.cpp @@ -52,7 +52,14 @@ class EditorExportPlatformIOS : public EditorExportPlatform { Ref<ImageTexture> logo; - void _fix_config_file(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &pfile, const String &p_name, const String &p_binary); + typedef Error (*FileHandler)(String p_file, void *p_userdata); + static Error _walk_dir_recursive(DirAccess *p_da, FileHandler p_handler, void *p_userdata); + static Error _codesign(String p_file, void *p_userdata); + + void _fix_config_file(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &pfile, const String &p_name, const String &p_binary, bool p_debug); + static Error _export_dylibs(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total); + Error _export_loading_screens(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir); + Error _export_icons(const Ref<EditorExportPreset> &p_preset, const String &p_iconset_dir); protected: virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features); @@ -63,7 +70,7 @@ public: virtual String get_os_name() const { return "iOS"; } virtual Ref<Texture> get_logo() const { return logo; } - virtual String get_binary_extension() const { return "xcodeproj"; } + virtual String get_binary_extension() const { return "ipa"; } 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; @@ -96,16 +103,44 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options) r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_package/debug", PROPERTY_HINT_GLOBAL_FILE, "zip"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_package/release", PROPERTY_HINT_GLOBAL_FILE, "zip"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/app_store_team_id"), "")); + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/provisioning_profile_uuid_debug"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/code_sign_identity_debug"), "iPhone Developer")); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_method_debug", PROPERTY_HINT_ENUM, "App Store,Development,Ad-Hoc,Enterprise"), 1)); + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/provisioning_profile_uuid_release"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/code_sign_identity_release"), "iPhone Distribution")); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_method_release", PROPERTY_HINT_ENUM, "App Store,Development,Ad-Hoc,Enterprise"), 0)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/name"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/info"), "Made with Godot Engine")); - // r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "png"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/identifier"), "org.godotengine.iosgame")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/signature"), "godotiosgame")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/signature"), "????")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version"), "1.0")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version"), "1.0")); 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"), 1)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/iphone_120x120", PROPERTY_HINT_FILE, "png"), "")); // Home screen on iPhone/iPod Touch with retina display + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/ipad_76x76", PROPERTY_HINT_FILE, "png"), "")); // Home screen on iPad + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/iphone_180x180", PROPERTY_HINT_FILE, "png"), "")); // Home screen on iPhone with retina HD display + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/ipad_152x152", PROPERTY_HINT_FILE, "png"), "")); // Home screen on iPad with retina display + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/ipad_167x167", PROPERTY_HINT_FILE, "png"), "")); // Home screen on iPad Pro + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/spotlight_40x40", PROPERTY_HINT_FILE, "png"), "")); // Spotlight + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/spotlight_80x80", PROPERTY_HINT_FILE, "png"), "")); // Spotlight on devices with retina display + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "landscape_launch_screens/iphone_2208x1242", PROPERTY_HINT_FILE, "png"), "")); // iPhone 6 Plus, 6s Plus, 7 Plus + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "landscape_launch_screens/ipad_2732x2048", PROPERTY_HINT_FILE, "png"), "")); // 12.9-inch iPad Pro + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "landscape_launch_screens/ipad_2048x1536", PROPERTY_HINT_FILE, "png"), "")); // Other iPads + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "portrait_launch_screens/iphone_640x1136", PROPERTY_HINT_FILE, "png"), "")); // iPhone 5, 5s, SE + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "portrait_launch_screens/iphone_750x1334", PROPERTY_HINT_FILE, "png"), "")); // iPhone 6, 6s, 7 + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "portrait_launch_screens/iphone_1242x2208", PROPERTY_HINT_FILE, "png"), "")); // iPhone 6 Plus, 6s Plus, 7 Plus + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "portrait_launch_screens/ipad_2048x2732", PROPERTY_HINT_FILE, "png"), "")); // 12.9-inch iPad Pro + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "portrait_launch_screens/ipad_1536x2048", PROPERTY_HINT_FILE, "png"), "")); // Other iPads + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), false)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc"), false)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc2"), true)); @@ -113,11 +148,17 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options) /* probably need some more info */ } -void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &pfile, const String &p_name, const String &p_binary) { - +void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &pfile, const String &p_name, const String &p_binary, bool p_debug) { + static const String export_method_string[] = { + "app-store", + "development", + "ad-hoc", + "enterprise" + }; String str; String strnew; str.parse_utf8((const char *)pfile.ptr(), pfile.size()); + print_line(str); Vector<String> lines = str.split("\n"); for (int i = 0; i < lines.size(); i++) { if (lines[i].find("$binary") != -1) { @@ -136,6 +177,19 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_ strnew += lines[i].replace("$signature", p_preset->get("application/signature")) + "\n"; } else if (lines[i].find("$copyright") != -1) { strnew += lines[i].replace("$copyright", p_preset->get("application/copyright")) + "\n"; + } else if (lines[i].find("$team_id") != -1) { + strnew += lines[i].replace("$team_id", p_preset->get("application/app_store_team_id")) + "\n"; + } else if (lines[i].find("$export_method") != -1) { + int export_method = p_preset->get(p_debug ? "application/export_method_debug" : "application/export_method_release"); + strnew += lines[i].replace("$export_method", export_method_string[export_method]) + "\n"; + } else if (lines[i].find("$provisioning_profile_uuid_release") != -1) { + strnew += lines[i].replace("$provisioning_profile_uuid_release", p_preset->get("application/provisioning_profile_uuid_release")) + "\n"; + } else if (lines[i].find("$provisioning_profile_uuid_debug") != -1) { + strnew += lines[i].replace("$provisioning_profile_uuid_debug", p_preset->get("application/provisioning_profile_uuid_debug")) + "\n"; + } else if (lines[i].find("$code_sign_identity_debug") != -1) { + strnew += lines[i].replace("$code_sign_identity_debug", p_preset->get("application/code_sign_identity_debug")) + "\n"; + } else if (lines[i].find("$code_sign_identity_release") != -1) { + strnew += lines[i].replace("$code_sign_identity_release", p_preset->get("application/code_sign_identity_release")) + "\n"; } else { strnew += lines[i] + "\n"; } @@ -150,12 +204,214 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_ } } +Error EditorExportPlatformIOS::_export_dylibs(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total) { + if (!p_path.ends_with(".dylib")) return OK; + const String &dest_dir = *(String *)p_userdata; + String rel_path = p_path.replace_first("res://", "dylibs/"); + DirAccess *dest_dir_access = DirAccess::open(dest_dir); + ERR_FAIL_COND_V(!dest_dir_access, ERR_CANT_OPEN); + + String base_dir = rel_path.get_base_dir(); + Error make_dir_err = OK; + if (!dest_dir_access->dir_exists(base_dir)) { + make_dir_err = dest_dir_access->make_dir_recursive(base_dir); + } + if (make_dir_err != OK) { + memdelete(dest_dir_access); + return make_dir_err; + } + + Error copy_err = dest_dir_access->copy(p_path, dest_dir + rel_path); + memdelete(dest_dir_access); + + return copy_err; +} + +struct IconInfo { + const char *preset_key; + const char *idiom; + const char *export_name; + const char *actual_size_side; + const char *scale; + const char *unscaled_size; + bool is_required; +}; + +static const IconInfo icon_infos[] = { + { "required_icons/iphone_120x120", "iphone", "Icon-120.png", "120", "2x", "60x60", true }, + { "required_icons/iphone_120x120", "iphone", "Icon-120.png", "120", "3x", "40x40", true }, + + { "required_icons/ipad_76x76", "ipad", "Icon-76.png", "76", "1x", "76x76", false }, + + { "optional_icons/iphone_180x180", "iphone", "Icon-180.png", "180", "3x", "60x60", false }, + + { "optional_icons/ipad_152x152", "ipad", "Icon-152.png", "152", "2x", "76x76", false }, + + { "optional_icons/ipad_167x167", "ipad", "Icon-167.png", "167", "2x", "83.5x83.5", false }, + + { "optional_icons/spotlight_40x40", "ipad", "Icon-40.png", "40", "1x", "40x40", false }, + + { "optional_icons/spotlight_80x80", "iphone", "Icon-80.png", "80", "2x", "40x40", false }, + { "optional_icons/spotlight_80x80", "ipad", "Icon-80.png", "80", "2x", "40x40", false } + +}; + +Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_preset, const String &p_iconset_dir) { + String json_description = "{\"images\":["; + String sizes; + + DirAccess *da = DirAccess::open(p_iconset_dir); + ERR_FAIL_COND_V(!da, ERR_CANT_OPEN); + + for (int i = 0; i < (sizeof(icon_infos) / sizeof(icon_infos[0])); ++i) { + IconInfo info = icon_infos[i]; + String icon_path = p_preset->get(info.preset_key); + if (icon_path.length() == 0) { + if (info.is_required) { + ERR_PRINT("Required icon is not specified in the preset"); + return ERR_UNCONFIGURED; + } + continue; + } + Error err = da->copy(icon_path, p_iconset_dir + info.export_name); + if (err) { + memdelete(da); + String err_str = String("Failed to export icon: ") + icon_path; + ERR_PRINT(err_str.utf8().get_data()); + return err; + } + sizes += String(info.actual_size_side) + "\n"; + if (i > 0) { + json_description += ","; + } + json_description += String("{"); + json_description += String("\"idiom\":") + "\"" + info.idiom + "\","; + json_description += String("\"size\":") + "\"" + info.unscaled_size + "\","; + json_description += String("\"scale\":") + "\"" + info.scale + "\","; + json_description += String("\"filename\":") + "\"" + info.export_name + "\""; + json_description += String("}"); + } + json_description += "]}"; + memdelete(da); + + FileAccess *json_file = FileAccess::open(p_iconset_dir + "Contents.json", FileAccess::WRITE); + ERR_FAIL_COND_V(!json_file, ERR_CANT_CREATE); + CharString json_utf8 = json_description.utf8(); + json_file->store_buffer((const uint8_t *)json_utf8.get_data(), json_utf8.length()); + memdelete(json_file); + + FileAccess *sizes_file = FileAccess::open(p_iconset_dir + "sizes", FileAccess::WRITE); + ERR_FAIL_COND_V(!sizes_file, ERR_CANT_CREATE); + CharString sizes_utf8 = sizes.utf8(); + sizes_file->store_buffer((const uint8_t *)sizes_utf8.get_data(), sizes_utf8.length()); + memdelete(sizes_file); + + return OK; +} + +struct LoadingScreenInfo { + const char *preset_key; + const char *export_name; +}; + +static const LoadingScreenInfo loading_screen_infos[] = { + { "landscape_launch_screens/iphone_2208x1242", "Default-Landscape-736h@3x.png" }, + { "landscape_launch_screens/ipad_2732x2048", "Default-Landscape-1366h@2x.png" }, + { "landscape_launch_screens/ipad_2048x1536", "Default-Landscape@2x.png" }, + + { "portrait_launch_screens/iphone_640x1136", "Default-568h@2x.png" }, + { "portrait_launch_screens/iphone_750x1334", "Default-667h@2x.png" }, + { "portrait_launch_screens/iphone_1242x2208", "Default-Portrait-736h@3x.png" }, + { "portrait_launch_screens/ipad_2048x2732", "Default-Portrait-1366h@2x.png" }, + { "portrait_launch_screens/ipad_1536x2048", "Default-Portrait@2x.png" } +}; + +Error EditorExportPlatformIOS::_export_loading_screens(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir) { + DirAccess *da = DirAccess::open(p_dest_dir); + ERR_FAIL_COND_V(!da, ERR_CANT_OPEN); + + for (int i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) { + LoadingScreenInfo info = loading_screen_infos[i]; + String loading_screen_file = p_preset->get(info.preset_key); + Error err = da->copy(loading_screen_file, p_dest_dir + info.export_name); + if (err) { + memdelete(da); + String err_str = String("Failed to export loading screen: ") + loading_screen_file; + ERR_PRINT(err_str.utf8().get_data()); + return err; + } + } + memdelete(da); + + return OK; +} + +Error EditorExportPlatformIOS::_walk_dir_recursive(DirAccess *p_da, FileHandler p_handler, void *p_userdata) { + Vector<String> dirs; + String path; + String current_dir = p_da->get_current_dir(); + p_da->list_dir_begin(); + while ((path = p_da->get_next()).length() != 0) { + if (p_da->current_is_dir()) { + if (path != "." && path != "..") { + dirs.push_back(path); + } + } else { + Error err = p_handler(current_dir + "/" + path, p_userdata); + if (err) { + p_da->list_dir_end(); + return err; + } + } + } + p_da->list_dir_end(); + + for (int i = 0; i < dirs.size(); ++i) { + String dir = dirs[i]; + p_da->change_dir(dir); + Error err = _walk_dir_recursive(p_da, p_handler, p_userdata); + p_da->change_dir(".."); + if (err) { + return err; + } + } + + return OK; +} + +struct CodesignData { + const Ref<EditorExportPreset> &preset; + bool debug; + + CodesignData(const Ref<EditorExportPreset> &p_preset, bool p_debug) + : preset(p_preset), debug(p_debug) { + } +}; + +Error EditorExportPlatformIOS::_codesign(String p_file, void *p_userdata) { + if (p_file.ends_with(".dylib")) { + CodesignData *data = (CodesignData *)p_userdata; + print_line(String("Signing ") + p_file); + List<String> codesign_args; + codesign_args.push_back("-f"); + codesign_args.push_back("-s"); + codesign_args.push_back(data->preset->get(data->debug ? "application/code_sign_identity_debug" : "application/code_sign_identity_release")); + codesign_args.push_back(p_file); + return OS::get_singleton()->execute("codesign", codesign_args, true); + } + return OK; +} + Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { String src_pkg_name; String dest_dir = p_path.get_base_dir() + "/"; String binary_name = p_path.get_file().get_basename(); - EditorProgress ep("export", "Exporting for iOS", 3); + EditorProgress ep("export", "Exporting for iOS", 5); + + String team_id = p_preset->get("application/app_store_team_id"); + ERR_EXPLAIN("App Store Team ID not specified - cannot configure the project."); + ERR_FAIL_COND_V(team_id.length() == 0, ERR_CANT_OPEN); if (p_debug) src_pkg_name = p_preset->get("custom_package/debug"); @@ -206,6 +462,15 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p bool found_binary = false; int total_size = 0; + Set<String> files_to_parse; + files_to_parse.insert("godot_ios/godot_ios-Info.plist"); + files_to_parse.insert("godot_ios.xcodeproj/project.pbxproj"); + files_to_parse.insert("export_options.plist"); + files_to_parse.insert("godot_ios.xcodeproj/project.xcworkspace/contents.xcworkspacedata"); + files_to_parse.insert("godot_ios.xcodeproj/xcshareddata/xcschemes/godot_ios.xcscheme"); + + print_line("Unzipping..."); + while (ret == UNZ_OK) { bool is_execute = false; @@ -229,12 +494,9 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p file = file.replace_first("iphone/", ""); - if (file == "godot_ios.xcodeproj/project.pbxproj") { - print_line("parse pbxproj"); - _fix_config_file(p_preset, data, pkg_name, binary_name); - } else if (file == "godot_ios/godot_ios-Info.plist") { - print_line("parse plist"); - _fix_config_file(p_preset, data, pkg_name, binary_name); + if (files_to_parse.has(file)) { + print_line(String("parse ") + file); + _fix_config_file(p_preset, data, pkg_name, binary_name, p_debug); } else if (file.begins_with("godot.iphone")) { if (file != binary_to_use) { ret = unzGoToNextFile(src_pkg_zip); @@ -264,6 +526,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p if (dir_err) { ERR_PRINTS("Can't create '" + dir_name + "'."); unzClose(src_pkg_zip); + memdelete(tmp_app_path); return ERR_CANT_CREATE; } } @@ -273,6 +536,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p if (!f) { ERR_PRINTS("Can't write '" + file + "'."); unzClose(src_pkg_zip); + memdelete(tmp_app_path); return ERR_CANT_CREATE; }; f->store_buffer(data.ptr(), data.size()); @@ -295,26 +559,79 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p 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); + memdelete(tmp_app_path); return ERR_FILE_NOT_FOUND; } - ep.step("Making PKG", 1); + String iconset_dir = dest_dir + binary_name + "/Images.xcassets/AppIcon.appiconset/"; + Error err = OK; + if (!tmp_app_path->dir_exists(iconset_dir)) { + Error err = tmp_app_path->make_dir_recursive(iconset_dir); + } + memdelete(tmp_app_path); + if (err) + return err; + + err = _export_icons(p_preset, iconset_dir); + if (err) + return err; + + err = _export_loading_screens(p_preset, dest_dir + binary_name + "/"); + if (err) + return err; + + ep.step("Making .pck", 1); String pack_path = dest_dir + binary_name + ".pck"; - Error err = save_pack(p_preset, pack_path); + err = save_pack(p_preset, pack_path); + if (err) + return err; - if (err) { + err = export_project_files(p_preset, _export_dylibs, &dest_dir); + if (err) return err; - } #ifdef OSX_ENABLED - /* and open up xcode with our new project.... */ - List<String> args; - args.push_back(p_path); - err = OS::get_singleton()->execute("/usr/bin/open", args, false); + ep.step("Code-signing dylibs", 2); + DirAccess *dylibs_dir = DirAccess::open(dest_dir + "dylibs"); + ERR_FAIL_COND_V(!dylibs_dir, ERR_CANT_OPEN); + CodesignData codesign_data(p_preset, p_debug); + err = _walk_dir_recursive(dylibs_dir, _codesign, &codesign_data); + memdelete(dylibs_dir); ERR_FAIL_COND_V(err, err); + ep.step("Making .xcarchive", 3); + String archive_path = p_path.get_basename() + ".xcarchive"; + List<String> archive_args; + archive_args.push_back("-project"); + archive_args.push_back(dest_dir + binary_name + ".xcodeproj"); + archive_args.push_back("-scheme"); + archive_args.push_back(binary_name); + archive_args.push_back("-sdk"); + archive_args.push_back("iphoneos"); + archive_args.push_back("-configuration"); + archive_args.push_back(p_debug ? "Debug" : "Release"); + archive_args.push_back("-destination"); + archive_args.push_back("generic/platform=iOS"); + archive_args.push_back("archive"); + archive_args.push_back("-archivePath"); + archive_args.push_back(archive_path); + err = OS::get_singleton()->execute("xcodebuild", archive_args, true); + ERR_FAIL_COND_V(err, err); + + ep.step("Making .ipa", 4); + List<String> export_args; + export_args.push_back("-exportArchive"); + export_args.push_back("-archivePath"); + export_args.push_back(archive_path); + export_args.push_back("-exportOptionsPlist"); + export_args.push_back(dest_dir + "export_options.plist"); + export_args.push_back("-exportPath"); + export_args.push_back(dest_dir); + err = OS::get_singleton()->execute("xcodebuild", export_args, true); + ERR_FAIL_COND_V(err, err); +#else + print_line(".ipa can only be built on macOS. Leaving XCode project without building the package."); #endif return OK; diff --git a/platform/iphone/game_center.h b/platform/iphone/game_center.h index c0a7830fe9..21f40fa362 100644 --- a/platform/iphone/game_center.h +++ b/platform/iphone/game_center.h @@ -43,11 +43,13 @@ class GameCenter : public Object { List<Variant> pending_events; - bool connected; + bool authenticated; + + void return_connect_error(const char *p_error_description); public: - Error connect(); - bool is_connected(); + void connect(); + bool is_authenticated(); Error post_score(Variant p_score); Error award_achievement(Variant p_params); @@ -55,6 +57,7 @@ public: void request_achievements(); void request_achievement_descriptions(); Error show_game_center(Variant p_params); + Error request_identity_verification_signature(); void game_center_closed(); diff --git a/platform/iphone/game_center.mm b/platform/iphone/game_center.mm index c05bdea005..531b80eee3 100644 --- a/platform/iphone/game_center.mm +++ b/platform/iphone/game_center.mm @@ -49,8 +49,7 @@ extern "C" { GameCenter *GameCenter::instance = NULL; void GameCenter::_bind_methods() { - ClassDB::bind_method(D_METHOD("connect"), &GameCenter::connect); - ClassDB::bind_method(D_METHOD("is_connected"), &GameCenter::is_connected); + ClassDB::bind_method(D_METHOD("is_authenticated"), &GameCenter::is_authenticated); ClassDB::bind_method(D_METHOD("post_score"), &GameCenter::post_score); ClassDB::bind_method(D_METHOD("award_achievement"), &GameCenter::award_achievement); @@ -58,24 +57,41 @@ void GameCenter::_bind_methods() { ClassDB::bind_method(D_METHOD("request_achievements"), &GameCenter::request_achievements); ClassDB::bind_method(D_METHOD("request_achievement_descriptions"), &GameCenter::request_achievement_descriptions); ClassDB::bind_method(D_METHOD("show_game_center"), &GameCenter::show_game_center); + ClassDB::bind_method(D_METHOD("request_identity_verification_signature"), &GameCenter::request_identity_verification_signature); ClassDB::bind_method(D_METHOD("get_pending_event_count"), &GameCenter::get_pending_event_count); ClassDB::bind_method(D_METHOD("pop_pending_event"), &GameCenter::pop_pending_event); }; -Error GameCenter::connect() { +void GameCenter::return_connect_error(const char *p_error_description) { + authenticated = false; + Dictionary ret; + ret["type"] = "authentication"; + ret["result"] = "error"; + ret["error_code"] = 0; + ret["error_description"] = p_error_description; + pending_events.push_back(ret); +} + +void GameCenter::connect() { //if this class isn't available, game center isn't implemented if ((NSClassFromString(@"GKLocalPlayer")) == nil) { - GameCenter::get_singleton()->connected = false; - return ERR_UNAVAILABLE; + return_connect_error("GameCenter not available"); + return; } GKLocalPlayer *player = [GKLocalPlayer localPlayer]; - ERR_FAIL_COND_V(![player respondsToSelector:@selector(authenticateHandler)], ERR_UNAVAILABLE); + if (![player respondsToSelector:@selector(authenticateHandler)]) { + return_connect_error("GameCenter doesn't respond to 'authenticateHandler'"); + return; + } ViewController *root_controller = (ViewController *)((AppDelegate *)[[UIApplication sharedApplication] delegate]).window.rootViewController; - ERR_FAIL_COND_V(!root_controller, FAILED); + if (!root_controller) { + return_connect_error("Window doesn't have root ViewController"); + return; + } // This handler is called several times. First when the view needs to be shown, then again // after the view is cancelled or the user logs in. Or if the user's already logged in, it's @@ -89,23 +105,22 @@ Error GameCenter::connect() { ret["type"] = "authentication"; if (player.isAuthenticated) { ret["result"] = "ok"; - GameCenter::get_singleton()->connected = true; + ret["player_id"] = [player.playerID UTF8String]; + GameCenter::get_singleton()->authenticated = true; } else { ret["result"] = "error"; ret["error_code"] = error.code; ret["error_description"] = [error.localizedDescription UTF8String]; - GameCenter::get_singleton()->connected = false; + GameCenter::get_singleton()->authenticated = false; }; pending_events.push_back(ret); }; }); - - return OK; }; -bool GameCenter::is_connected() { - return connected; +bool GameCenter::is_authenticated() { + return authenticated; }; Error GameCenter::post_score(Variant p_score) { @@ -116,7 +131,7 @@ Error GameCenter::post_score(Variant p_score) { String category = params["category"]; NSString *cat_str = [[[NSString alloc] initWithUTF8String:category.utf8().get_data()] autorelease]; - GKScore *reporter = [[[GKScore alloc] initWithCategory:cat_str] autorelease]; + GKScore *reporter = [[[GKScore alloc] initWithLeaderboardIdentifier:cat_str] autorelease]; reporter.value = score; ERR_FAIL_COND_V([GKScore respondsToSelector:@selector(reportScores)], ERR_UNAVAILABLE); @@ -325,6 +340,34 @@ Error GameCenter::show_game_center(Variant p_params) { return OK; }; +Error GameCenter::request_identity_verification_signature() { + + ERR_FAIL_COND_V(!is_authenticated(), ERR_UNAUTHORIZED); + + GKLocalPlayer *player = [GKLocalPlayer localPlayer]; + [player generateIdentityVerificationSignatureWithCompletionHandler:^(NSURL *publicKeyUrl, NSData *signature, NSData *salt, uint64_t timestamp, NSError *error) { + + Dictionary ret; + ret["type"] = "identity_verification_signature"; + if (error == nil) { + ret["result"] = "ok"; + ret["public_key_url"] = [publicKeyUrl.absoluteString UTF8String]; + ret["signature"] = [[signature base64EncodedStringWithOptions:0] UTF8String]; + ret["salt"] = [[salt base64EncodedStringWithOptions:0] UTF8String]; + ret["timestamp"] = timestamp; + ret["player_id"] = [player.playerID UTF8String]; + } else { + ret["result"] = "error"; + ret["error_code"] = error.code; + ret["error_description"] = [error.localizedDescription UTF8String]; + }; + + pending_events.push_back(ret); + }]; + + return OK; +}; + void GameCenter::game_center_closed() { Dictionary ret; @@ -353,7 +396,7 @@ GameCenter *GameCenter::get_singleton() { GameCenter::GameCenter() { ERR_FAIL_COND(instance != NULL); instance = this; - connected = false; + authenticated = false; }; GameCenter::~GameCenter(){}; diff --git a/platform/iphone/gl_view.h b/platform/iphone/gl_view.h index a9fd8d5711..f7309396c6 100644 --- a/platform/iphone/gl_view.h +++ b/platform/iphone/gl_view.h @@ -100,6 +100,8 @@ - (void)destroyFramebuffer; - (void)audioRouteChangeListenerCallback:(NSNotification *)notification; +- (void)keyboardOnScreen:(NSNotification *)notification; +- (void)keyboardHidden:(NSNotification *)notification; @property NSTimeInterval animationInterval; @property(nonatomic, assign) BOOL useCADisplayLink; diff --git a/platform/iphone/gl_view.mm b/platform/iphone/gl_view.mm index 3e206c3a2c..02da706cc5 100644 --- a/platform/iphone/gl_view.mm +++ b/platform/iphone/gl_view.mm @@ -63,6 +63,7 @@ void _pause_video(); void _focus_out_video(); void _unpause_video(); void _stop_video(); +CGFloat _points_to_pixels(CGFloat); void _show_keyboard(String p_existing) { keyboard_text = p_existing; @@ -174,6 +175,19 @@ void _stop_video() { video_playing = false; } +CGFloat _points_to_pixels(CGFloat points) { + float pixelPerInch; + if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { + pixelPerInch = 132; + } else if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) { + pixelPerInch = 163; + } else { + pixelPerInch = 160; + } + CGFloat pointsPerInch = 72.0; + return (points / pointsPerInch * pixelPerInch); +} + @implementation GLView @synthesize animationInterval; @@ -537,6 +551,20 @@ static void clear_touches() { [self resignFirstResponder]; }; +- (void)keyboardOnScreen:(NSNotification *)notification { + NSDictionary *info = notification.userInfo; + NSValue *value = info[UIKeyboardFrameEndUserInfoKey]; + + CGRect rawFrame = [value CGRectValue]; + CGRect keyboardFrame = [self convertRect:rawFrame fromView:nil]; + + OSIPhone::get_singleton()->set_virtual_keyboard_height(_points_to_pixels(keyboardFrame.size.height)); +} + +- (void)keyboardHidden:(NSNotification *)notification { + OSIPhone::get_singleton()->set_virtual_keyboard_height(0); +} + - (void)deleteBackward { if (keyboard_text.length()) keyboard_text.erase(keyboard_text.length() - 1, 1); @@ -606,6 +634,18 @@ static void clear_touches() { name:AVAudioSessionRouteChangeNotification object:nil]; + printf("******** adding observer for keyboard show/hide\n"); + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(keyboardOnScreen:) + name:UIKeyboardDidShowNotification + object:nil]; + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(keyboardHidden:) + name:UIKeyboardDidHideNotification + object:nil]; + //self.autoresizesSubviews = YES; //[self setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleWidth]; diff --git a/platform/iphone/godot_iphone.cpp b/platform/iphone/godot_iphone.cpp index 8c6d6d8da4..7d21d35e18 100644 --- a/platform/iphone/godot_iphone.cpp +++ b/platform/iphone/godot_iphone.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "main/main.h" #include "os_iphone.h" +#include "ustring.h" #include <stdio.h> #include <string.h> @@ -41,9 +42,9 @@ int add_path(int p_argc, char **p_args); int add_cmdline(int p_argc, char **p_args); }; -int iphone_main(int, int, int, char **); +int iphone_main(int, int, int, char **, String); -int iphone_main(int width, int height, int argc, char **argv) { +int iphone_main(int width, int height, int argc, char **argv, String data_dir) { int len = strlen(argv[0]); @@ -63,7 +64,7 @@ int iphone_main(int width, int height, int argc, char **argv) { char cwd[512]; getcwd(cwd, sizeof(cwd)); printf("cwd %s\n", cwd); - os = new OSIPhone(width, height); + os = new OSIPhone(width, height, data_dir); char *fargv[64]; for (int i = 0; i < argc; i++) { diff --git a/platform/iphone/icloud.h b/platform/iphone/icloud.h index 7ab3e04bb8..6944ee6749 100644 --- a/platform/iphone/icloud.h +++ b/platform/iphone/icloud.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* icloud.h */ +/* icloud.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ diff --git a/platform/iphone/icloud.mm b/platform/iphone/icloud.mm index 84458ed79f..097018f296 100644 --- a/platform/iphone/icloud.mm +++ b/platform/iphone/icloud.mm @@ -1,5 +1,5 @@ /*************************************************************************/ -/* icloud.mm */ +/* icloud.mm */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ diff --git a/platform/iphone/os_iphone.cpp b/platform/iphone/os_iphone.cpp index 0e3eeed114..0efe22c1af 100644 --- a/platform/iphone/os_iphone.cpp +++ b/platform/iphone/os_iphone.cpp @@ -35,13 +35,13 @@ #include "servers/visual/visual_server_raster.h" //#include "servers/visual/visual_server_wrap_mt.h" -#include "audio_driver_iphone.h" #include "main/main.h" #include "core/io/file_access_pack.h" #include "core/os/dir_access.h" #include "core/os/file_access.h" #include "core/project_settings.h" +#include "drivers/unix/syslog_logger.h" #include "sem_iphone.h" @@ -97,8 +97,19 @@ void OSIPhone::initialize_core() { OS_Unix::initialize_core(); SemaphoreIphone::make_default(); + + set_data_dir(data_dir); }; +void OSIPhone::initialize_logger() { + Vector<Logger *> loggers; + loggers.push_back(memnew(SyslogLogger)); + // FIXME: Reenable once we figure out how to get this properly in user:// + // instead of littering the user's working dirs (res:// + pwd) with log files (GH-12277) + //loggers.push_back(memnew(RotatedFileLogger("user://logs/log.txt"))); + _set_logger(memnew(CompositeLogger(loggers))); +} + void OSIPhone::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { supported_orientations = 0; @@ -124,9 +135,8 @@ void OSIPhone::initialize(const VideoMode &p_desired, int p_video_driver, int p_ // reset this to what it should be, it will have been set to 0 after visual_server->init() is called RasterizerStorageGLES3::system_fbo = gl_view_base_fb; - audio_driver = memnew(AudioDriverIphone); - audio_driver->set_singleton(); - audio_driver->init(); + AudioDriverManager::add_driver(&audio_driver); + AudioDriverManager::initialize(p_audio_driver); // init physics servers physics_server = memnew(PhysicsServerSW); @@ -457,6 +467,14 @@ void OSIPhone::hide_virtual_keyboard() { _hide_keyboard(); }; +void OSIPhone::set_virtual_keyboard_height(int p_height) { + virtual_keyboard_height = p_height; +} + +int OSIPhone::get_virtual_keyboard_height() const { + return virtual_keyboard_height; +} + Error OSIPhone::shell_open(String p_uri) { return _shell_open(p_uri); }; @@ -558,7 +576,7 @@ bool OSIPhone::_check_internal_feature_support(const String &p_feature) { return p_feature == "mobile" || p_feature == "etc" || p_feature == "pvrtc" || p_feature == "etc2"; } -OSIPhone::OSIPhone(int width, int height) { +OSIPhone::OSIPhone(int width, int height, String p_data_dir) { main_loop = NULL; visual_server = NULL; @@ -570,6 +588,13 @@ OSIPhone::OSIPhone(int width, int height) { vm.resizable = false; set_video_mode(vm); event_count = 0; + virtual_keyboard_height = 0; + + // can't call set_data_dir from here, since it requires DirAccess + // which is initialized in initialize_core + data_dir = p_data_dir; + + _set_logger(memnew(SyslogLogger)); }; OSIPhone::~OSIPhone() { diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h index 475dceebf2..e70ac9ba98 100644 --- a/platform/iphone/os_iphone.h +++ b/platform/iphone/os_iphone.h @@ -32,6 +32,7 @@ #ifndef OS_IPHONE_H #define OS_IPHONE_H +#include "drivers/coreaudio/audio_driver_coreaudio.h" #include "drivers/unix/os_unix.h" #include "os/input.h" @@ -46,8 +47,6 @@ #include "servers/visual/rasterizer.h" #include "servers/visual_server.h" -class AudioDriverIphone; - class OSIPhone : public OS_Unix { public: @@ -70,7 +69,7 @@ private: PhysicsServer *physics_server; Physics2DServer *physics_2d_server; - AudioDriverIphone *audio_driver; + AudioDriverCoreAudio audio_driver; #ifdef GAME_CENTER_ENABLED GameCenter *game_center; @@ -91,6 +90,7 @@ private: virtual VideoMode get_default_video_mode() const; + virtual void initialize_logger(); virtual void initialize_core(); virtual void initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver); @@ -124,6 +124,8 @@ private: InputDefault *input; + int virtual_keyboard_height; + public: bool iterate(); @@ -133,6 +135,7 @@ public: void mouse_move(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y, bool p_use_as_mouse); void touches_cancelled(); void key(uint32_t p_key, bool p_pressed); + void set_virtual_keyboard_height(int p_height); int set_base_framebuffer(int p_fb); @@ -168,6 +171,7 @@ public: virtual bool has_virtual_keyboard() const; virtual void show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2()); virtual void hide_virtual_keyboard(); + virtual int get_virtual_keyboard_height() const; virtual void set_cursor_shape(CursorShape p_shape); @@ -197,7 +201,7 @@ public: virtual void native_video_stop(); virtual bool _check_internal_feature_support(const String &p_feature); - OSIPhone(int width, int height); + OSIPhone(int width, int height, String p_data_dir); ~OSIPhone(); }; diff --git a/platform/iphone/power_iphone.cpp b/platform/iphone/power_iphone.cpp index 2811e62108..055d31ef0a 100644 --- a/platform/iphone/power_iphone.cpp +++ b/platform/iphone/power_iphone.cpp @@ -30,15 +30,15 @@ #include "power_iphone.h" -bool PowerState::UpdatePowerInfo() { +bool OS::PowerState::UpdatePowerInfo() { return false; } -PowerState PowerIphone::get_power_state() { +OS::PowerState PowerIphone::get_power_state() { if (UpdatePowerInfo()) { return power_state; } else { - return POWERSTATE_UNKNOWN; + return OS::POWERSTATE_UNKNOWN; } } @@ -59,7 +59,7 @@ int PowerIphone::get_power_percent_left() { } PowerIphone::PowerIphone() - : nsecs_left(-1), percent_left(-1), power_state(POWERSTATE_UNKNOWN) { + : nsecs_left(-1), percent_left(-1), power_state(OS::POWERSTATE_UNKNOWN) { // TODO Auto-generated constructor stub } diff --git a/platform/iphone/power_iphone.h b/platform/iphone/power_iphone.h index b4fb8d62dc..6270a8069c 100644 --- a/platform/iphone/power_iphone.h +++ b/platform/iphone/power_iphone.h @@ -31,11 +31,13 @@ #ifndef PLATFORM_IPHONE_POWER_IPHONE_H_ #define PLATFORM_IPHONE_POWER_IPHONE_H_ +#include <os/os.h> + class PowerIphone { private: int nsecs_left; int percent_left; - PowerState power_state; + OS::PowerState power_state; bool UpdatePowerInfo(); @@ -43,7 +45,7 @@ public: PowerIphone(); virtual ~PowerIphone(); - PowerState get_power_state(); + OS::PowerState get_power_state(); int get_power_seconds_left(); int get_power_percent_left(); }; diff --git a/platform/javascript/SCsub b/platform/javascript/SCsub index b804863ee1..f01d9367d2 100644 --- a/platform/javascript/SCsub +++ b/platform/javascript/SCsub @@ -20,33 +20,32 @@ for x in javascript_files: javascript_objects.append(env_javascript.Object(x)) env.Append(LINKFLAGS=["-s", "EXPORTED_FUNCTIONS=\"['_main','_audio_server_mix_function','_main_after_fs_sync','_send_notification']\""]) -env.Append(LINKFLAGS=["--shell-file", '"platform/javascript/godot_shell.html"']) # output file name without file extension basename = "godot" + env["PROGSUFFIX"] target_dir = env.Dir("#bin") -js_file = target_dir.File(basename + ".js") -implicit_targets = [js_file] zip_dir = target_dir.Dir('.javascript_zip') -zip_files = env.InstallAs([zip_dir.File("godot.js"), zip_dir.File("godotfs.js")], [js_file, "#misc/dist/html_fs/godotfs.js"]) - -if env['wasm'] == 'yes': - wasm_file = target_dir.File(basename+'.wasm') - implicit_targets.append(wasm_file) - zip_files.append(InstallAs(zip_dir.File('godot.wasm'), wasm_file)) +zip_files = env.InstallAs(zip_dir.File('godot.html'), '#misc/dist/html/default.html') + +implicit_targets = [] +if env['wasm']: + wasm = target_dir.File(basename + '.wasm') + implicit_targets.append(wasm) + zip_files.append(InstallAs(zip_dir.File('godot.wasm'), wasm)) + prejs = env.File('pre_wasm.js') else: - asmjs_files = [target_dir.File(basename+'.asm.js'), target_dir.File(basename+'.html.mem')] - zip_files.append(InstallAs([zip_dir.File('godot.asm.js'), zip_dir.File('godot.mem')], asmjs_files)) + asmjs_files = [target_dir.File(basename + '.asm.js'), target_dir.File(basename + '.js.mem')] implicit_targets.extend(asmjs_files) + zip_files.append(InstallAs([zip_dir.File('godot.asm.js'), zip_dir.File('godot.mem')], asmjs_files)) + prejs = env.File('pre_asmjs.js') -# HTML file must be the first target in the list -html_file = env.Program(["#bin/godot"] + implicit_targets, javascript_objects, PROGSUFFIX=env["PROGSUFFIX"]+".html")[0] -Depends(html_file, "godot_shell.html") +js = env.Program(['#bin/godot'] + implicit_targets, javascript_objects, PROGSUFFIX=env['PROGSUFFIX'] + '.js')[0]; +zip_files.append(InstallAs(zip_dir.File('godot.js'), js)) -# Emscripten hardcodes file names, so replace common base name with -# placeholder while leaving extension; also change `.html.mem` to just `.mem` -fixup_html = env.Substfile(html_file, SUBST_DICT=[(basename, '$$GODOT_BASE'), ('.html.mem', '.mem')], SUBSTFILESUFFIX='.fixup.html') +postjs = env.File('engine.js') +env.Depends(js, [prejs, postjs]) +env.Append(LINKFLAGS=['--pre-js', prejs.path]) +env.Append(LINKFLAGS=['--post-js', postjs.path]) -zip_files.append(InstallAs(zip_dir.File('godot.html'), fixup_html)) -Zip('#bin/godot', zip_files, ZIPSUFFIX=env['PROGSUFFIX']+env['ZIPSUFFIX'], ZIPROOT=zip_dir, ZIPCOMSTR="Archving $SOURCES as $TARGET") +Zip('#bin/godot', zip_files, ZIPSUFFIX=env['PROGSUFFIX'] + env['ZIPSUFFIX'], ZIPROOT=zip_dir, ZIPCOMSTR="Archving $SOURCES as $TARGET") diff --git a/platform/javascript/audio_driver_javascript.cpp b/platform/javascript/audio_driver_javascript.cpp index 4c0e5fd966..cd3974669f 100644 --- a/platform/javascript/audio_driver_javascript.cpp +++ b/platform/javascript/audio_driver_javascript.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "audio_driver_javascript.h" +#include <emscripten.h> #include <string.h> #define MAX_NUMBER_INTERFACES 3 @@ -38,22 +39,91 @@ //AudioDriverJavaScript* AudioDriverJavaScript::s_ad=NULL; +AudioDriverJavaScript *AudioDriverJavaScript::singleton_js = NULL; const char *AudioDriverJavaScript::get_name() const { return "JavaScript"; } +extern "C" { + +void js_audio_driver_mix_function(int p_frames) { + + //print_line("MIXI! "+itos(p_frames)); + AudioDriverJavaScript::singleton_js->mix_to_js(p_frames); +} +} + +void AudioDriverJavaScript::mix_to_js(int p_frames) { + + int todo = p_frames; + int offset = 0; + + while (todo) { + + int tomix = MIN(todo, INTERNAL_BUFFER_SIZE); + + audio_server_process(p_frames, stream_buffer); + for (int i = 0; i < tomix * internal_buffer_channels; i++) { + internal_buffer[i] = float(stream_buffer[i] >> 16) * 32768.0; + } + + /* clang-format off */ + EM_ASM_({ + var data = HEAPF32.subarray($0 / 4, $0 / 4 + $2 * 2); + + for (var channel = 0; channel < _as_output_buffer.numberOfChannels; channel++) { + var outputData = _as_output_buffer.getChannelData(channel); + // Loop through samples + for (var sample = 0; sample < $2; sample++) { + // make output equal to the same as the input + outputData[sample + $1] = data[sample * 2 + channel]; + } + } + }, internal_buffer, offset, tomix); + /* clang-format on */ + + todo -= tomix; + offset += tomix; + } +} + Error AudioDriverJavaScript::init() { return OK; } void AudioDriverJavaScript::start() { + + internal_buffer_channels = 2; + internal_buffer = memnew_arr(float, INTERNAL_BUFFER_SIZE *internal_buffer_channels); + stream_buffer = memnew_arr(int32_t, INTERNAL_BUFFER_SIZE * 4); //max 4 channels + + /* clang-format off */ + EM_ASM( + _as_audioctx = new (window.AudioContext || window.webkitAudioContext)(); + + audio_server_mix_function = Module.cwrap('js_audio_driver_mix_function', 'void', ['number']); + ); + + int buffer_latency = 16384; + EM_ASM_( { + _as_script_node = _as_audioctx.createScriptProcessor($0, 0, 2); + _as_script_node.connect(_as_audioctx.destination); + console.log(_as_script_node.bufferSize); + + _as_script_node.onaudioprocess = function(audioProcessingEvent) { + // The output buffer contains the samples that will be modified and played + _as_output_buffer = audioProcessingEvent.outputBuffer; + audio_server_mix_function(_as_output_buffer.getChannelData(0).length); + } + }, buffer_latency); + /* clang-format on */ } int AudioDriverJavaScript::get_mix_rate() const { - return 44100; + return mix_rate; } AudioDriver::SpeakerMode AudioDriverJavaScript::get_speaker_mode() const { @@ -63,7 +133,7 @@ AudioDriver::SpeakerMode AudioDriverJavaScript::get_speaker_mode() const { void AudioDriverJavaScript::lock() { - /* + /*no locking, as threads are not supported if (active && mutex) mutex->lock(); */ @@ -71,7 +141,7 @@ void AudioDriverJavaScript::lock() { void AudioDriverJavaScript::unlock() { - /* + /*no locking, as threads are not supported if (active && mutex) mutex->unlock(); */ @@ -81,4 +151,7 @@ void AudioDriverJavaScript::finish() { } AudioDriverJavaScript::AudioDriverJavaScript() { + + mix_rate = 44100; + singleton_js = this; } diff --git a/platform/javascript/audio_driver_javascript.h b/platform/javascript/audio_driver_javascript.h index c5cebe800f..c3adeca07b 100644 --- a/platform/javascript/audio_driver_javascript.h +++ b/platform/javascript/audio_driver_javascript.h @@ -35,7 +35,23 @@ #include "os/mutex.h" class AudioDriverJavaScript : public AudioDriver { + + enum { + INTERNAL_BUFFER_SIZE = 4096, + STREAM_SCALE_BITS = 12 + + }; + + int mix_rate; + float *internal_buffer; + int internal_buffer_channels; + int internal_buffer_size; + int32_t *stream_buffer; + public: + void mix_to_js(int p_frames); + static AudioDriverJavaScript *singleton_js; + virtual const char *get_name() const; virtual Error init(); diff --git a/platform/javascript/detect.py b/platform/javascript/detect.py index 5f066f1b15..a2988d9c60 100644 --- a/platform/javascript/detect.py +++ b/platform/javascript/detect.py @@ -13,22 +13,22 @@ def get_name(): def can_build(): - return ("EMSCRIPTEN_ROOT" in os.environ) + return ("EMSCRIPTEN_ROOT" in os.environ or "EMSCRIPTEN" in os.environ) def get_opts(): - + from SCons.Variables import BoolVariable return [ - ['wasm', 'Compile to WebAssembly', 'no'], - ['javascript_eval', 'Enable JavaScript eval interface', 'yes'], + BoolVariable('wasm', 'Compile to WebAssembly', False), + BoolVariable('javascript_eval', 'Enable JavaScript eval interface', True), ] def get_flags(): return [ - ('tools', 'no'), - ('module_theora_enabled', 'no'), + ('tools', False), + ('module_theora_enabled', False), ] @@ -66,7 +66,10 @@ def configure(env): ## Compiler configuration env['ENV'] = os.environ - env.PrependENVPath('PATH', os.environ['EMSCRIPTEN_ROOT']) + if ("EMSCRIPTEN_ROOT" in os.environ): + env.PrependENVPath('PATH', os.environ['EMSCRIPTEN_ROOT']) + elif ("EMSCRIPTEN" in os.environ): + env.PrependENVPath('PATH', os.environ['EMSCRIPTEN']) env['CC'] = 'emcc' env['CXX'] = 'em++' env['LINK'] = 'emcc' @@ -95,14 +98,15 @@ def configure(env): # These flags help keep the file size down env.Append(CPPFLAGS=["-fno-exceptions", '-DNO_SAFE_CAST', '-fno-rtti']) - if env['javascript_eval'] == 'yes': + if env['javascript_eval']: env.Append(CPPFLAGS=['-DJAVASCRIPT_EVAL_ENABLED']) ## Link flags + env.Append(LINKFLAGS=['-s', 'EXTRA_EXPORTED_RUNTIME_METHODS="[\'FS\']"']) env.Append(LINKFLAGS=['-s', 'USE_WEBGL2=1']) - if (env['wasm'] == 'yes'): + if env['wasm']: env.Append(LINKFLAGS=['-s', 'BINARYEN=1']) # In contrast to asm.js, enabling memory growth on WebAssembly has no # major performance impact, and causes only a negligible increase in @@ -112,7 +116,8 @@ def configure(env): else: env.Append(LINKFLAGS=['-s', 'ASM_JS=1']) env.Append(LINKFLAGS=['--separate-asm']) + env.Append(LINKFLAGS=['--memory-init-file', '1']) # TODO: Move that to opus module's config - if("module_opus_enabled" in env and env["module_opus_enabled"] != "no"): + if 'module_opus_enabled' in env and env['module_opus_enabled']: env.opus_fixed_point = "yes" diff --git a/platform/javascript/engine.js b/platform/javascript/engine.js new file mode 100644 index 0000000000..99d1c20bbd --- /dev/null +++ b/platform/javascript/engine.js @@ -0,0 +1,366 @@ + return Module; + }, +}; + +(function() { + var engine = Engine; + + var USING_WASM = engine.USING_WASM; + var DOWNLOAD_ATTEMPTS_MAX = 4; + + var basePath = null; + var engineLoadPromise = null; + + var loadingFiles = {}; + + function getBasePath(path) { + + if (path.endsWith('/')) + path = path.slice(0, -1); + if (path.lastIndexOf('.') > path.lastIndexOf('/')) + path = path.slice(0, path.lastIndexOf('.')); + return path; + } + + function getBaseName(path) { + + path = getBasePath(path); + return path.slice(path.lastIndexOf('/') + 1); + } + + Engine = function Engine() { + + this.rtenv = null; + + var gameInitPromise = null; + var unloadAfterInit = true; + var memorySize = 268435456; + + var progressFunc = null; + var pckProgressTracker = {}; + var lastProgress = { loaded: 0, total: 0 }; + + var canvas = null; + var stdout = null; + var stderr = null; + + this.initGame = function(mainPack) { + + if (!gameInitPromise) { + + if (mainPack === undefined) { + if (basePath !== null) { + mainPack = basePath + '.pck'; + } else { + return Promise.reject(new Error("No main pack to load specified")); + } + } + if (basePath === null) + basePath = getBasePath(mainPack); + + gameInitPromise = Engine.initEngine().then( + instantiate.bind(this) + ); + var gameLoadPromise = loadPromise(mainPack, pckProgressTracker).then(function(xhr) { return xhr.response; }); + gameInitPromise = Promise.all([gameLoadPromise, gameInitPromise]).then(function(values) { + // resolve with pck + return new Uint8Array(values[0]); + }); + if (unloadAfterInit) + gameInitPromise.then(Engine.unloadEngine); + requestAnimationFrame(animateProgress); + } + return gameInitPromise; + }; + + function instantiate(initializer) { + + var rtenvOpts = { + noInitialRun: true, + thisProgram: getBaseName(basePath), + engine: this, + }; + if (typeof stdout === 'function') + rtenvOpts.print = stdout; + if (typeof stderr === 'function') + rtenvOpts.printErr = stderr; + if (typeof WebAssembly === 'object' && initializer instanceof ArrayBuffer) { + rtenvOpts.instantiateWasm = function(imports, onSuccess) { + WebAssembly.instantiate(initializer, imports).then(function(result) { + onSuccess(result.instance); + }); + return {}; + }; + } else if (initializer.asm && initializer.mem) { + rtenvOpts.asm = initializer.asm; + rtenvOpts.memoryInitializerRequest = initializer.mem; + rtenvOpts.TOTAL_MEMORY = memorySize; + } else { + throw new Error("Invalid initializer"); + } + + return new Promise(function(resolve, reject) { + rtenvOpts.onRuntimeInitialized = resolve; + rtenvOpts.onAbort = reject; + rtenvOpts.engine.rtenv = Engine.RuntimeEnvironment(rtenvOpts); + }); + } + + this.start = function(mainPack) { + + return this.initGame(mainPack).then(synchronousStart.bind(this)); + }; + + function synchronousStart(pckView) { + // TODO don't expect canvas when runninng as cli tool + if (canvas instanceof HTMLCanvasElement) { + this.rtenv.canvas = canvas; + } else { + var firstCanvas = document.getElementsByTagName('canvas')[0]; + if (firstCanvas instanceof HTMLCanvasElement) { + this.rtenv.canvas = firstCanvas; + } else { + throw new Error("No canvas found"); + } + } + + var actualCanvas = this.rtenv.canvas; + var context = false; + try { + context = actualCanvas.getContext('webgl2') || actualCanvas.getContext('experimental-webgl2'); + } catch (e) {} + if (!context) { + throw new Error("WebGL 2 not available"); + } + + // canvas can grab focus on click + if (actualCanvas.tabIndex < 0) { + actualCanvas.tabIndex = 0; + } + // necessary to calculate cursor coordinates correctly + actualCanvas.style.padding = 0; + actualCanvas.style.borderWidth = 0; + actualCanvas.style.borderStyle = 'none'; + // until context restoration is implemented + actualCanvas.addEventListener('webglcontextlost', function(ev) { + alert("WebGL context lost, please reload the page"); + ev.preventDefault(); + }, false); + + this.rtenv.FS.createDataFile('/', this.rtenv.thisProgram + '.pck', pckView, true, true, true); + gameInitPromise = null; + this.rtenv.callMain(); + } + + this.setProgressFunc = function(func) { + progressFunc = func; + }; + + function animateProgress() { + + var loaded = 0; + var total = 0; + var totalIsValid = true; + var progressIsFinal = true; + + [loadingFiles, pckProgressTracker].forEach(function(tracker) { + Object.keys(tracker).forEach(function(file) { + if (!tracker[file].final) + progressIsFinal = false; + if (!totalIsValid || tracker[file].total === 0) { + totalIsValid = false; + total = 0; + } else { + total += tracker[file].total; + } + loaded += tracker[file].loaded; + }); + }); + if (loaded !== lastProgress.loaded || total !== lastProgress.total) { + lastProgress.loaded = loaded; + lastProgress.total = total; + if (typeof progressFunc === 'function') + progressFunc(loaded, total); + } + if (!progressIsFinal) + requestAnimationFrame(animateProgress); + } + + this.setCanvas = function(elem) { + canvas = elem; + }; + + this.setAsmjsMemorySize = function(size) { + memorySize = size; + }; + + this.setUnloadAfterInit = function(enabled) { + + if (enabled && !unloadAfterInit && gameInitPromise) { + gameInitPromise.then(Engine.unloadEngine); + } + unloadAfterInit = enabled; + }; + + this.setStdoutFunc = function(func) { + + var print = function(text) { + if (arguments.length > 1) { + text = Array.prototype.slice.call(arguments).join(" "); + } + func(text); + }; + if (this.rtenv) + this.rtenv.print = print; + stdout = print; + }; + + this.setStderrFunc = function(func) { + + var printErr = function(text) { + if (arguments.length > 1) + text = Array.prototype.slice.call(arguments).join(" "); + func(text); + }; + if (this.rtenv) + this.rtenv.printErr = printErr; + stderr = printErr; + }; + + + }; // Engine() + + Engine.RuntimeEnvironment = engine.RuntimeEnvironment; + + Engine.initEngine = function(newBasePath) { + + if (newBasePath !== undefined) basePath = getBasePath(newBasePath); + if (engineLoadPromise === null) { + if (USING_WASM) { + if (typeof WebAssembly !== 'object') + return Promise.reject(new Error("Browser doesn't support WebAssembly")); + // TODO cache/retrieve module to/from idb + engineLoadPromise = loadPromise(basePath + '.wasm').then(function(xhr) { + return xhr.response; + }); + } else { + var asmjsPromise = loadPromise(basePath + '.asm.js').then(function(xhr) { + return asmjsModulePromise(xhr.response); + }); + var memPromise = loadPromise(basePath + '.mem'); + engineLoadPromise = Promise.all([asmjsPromise, memPromise]).then(function(values) { + return { asm: values[0], mem: values[1] }; + }); + } + engineLoadPromise = engineLoadPromise.catch(function(err) { + engineLoadPromise = null; + throw err; + }); + } + return engineLoadPromise; + }; + + function asmjsModulePromise(module) { + var elem = document.createElement('script'); + var script = new Blob([ + 'Engine.asm = (function() { var Module = {};', + module, + 'return Module.asm; })();' + ]); + var url = URL.createObjectURL(script); + elem.src = url; + return new Promise(function(resolve, reject) { + elem.addEventListener('load', function() { + URL.revokeObjectURL(url); + var asm = Engine.asm; + Engine.asm = undefined; + setTimeout(function() { + // delay to reclaim compilation memory + resolve(asm); + }, 1); + }); + elem.addEventListener('error', function() { + URL.revokeObjectURL(url); + reject("asm.js faiilure"); + }); + document.body.appendChild(elem); + }); + } + + Engine.unloadEngine = function() { + engineLoadPromise = null; + }; + + function loadPromise(file, tracker) { + if (tracker === undefined) + tracker = loadingFiles; + return new Promise(function(resolve, reject) { + loadXHR(resolve, reject, file, tracker); + }); + } + + function loadXHR(resolve, reject, file, tracker) { + + var xhr = new XMLHttpRequest; + xhr.open('GET', file); + if (!file.endsWith('.js')) { + xhr.responseType = 'arraybuffer'; + } + ['loadstart', 'progress', 'load', 'error', 'timeout', 'abort'].forEach(function(ev) { + xhr.addEventListener(ev, onXHREvent.bind(xhr, resolve, reject, file, tracker)); + }); + xhr.send(); + } + + function onXHREvent(resolve, reject, file, tracker, ev) { + + if (this.status >= 400) { + + if (this.status < 500 || ++tracker[file].attempts >= DOWNLOAD_ATTEMPTS_MAX) { + reject(new Error("Failed loading file '" + file + "': " + this.statusText)); + this.abort(); + return; + } else { + loadXHR(resolve, reject, file); + } + } + + switch (ev.type) { + case 'loadstart': + if (tracker[file] === undefined) { + tracker[file] = { + total: ev.total, + loaded: ev.loaded, + attempts: 0, + final: false, + }; + } + break; + + case 'progress': + tracker[file].loaded = ev.loaded; + tracker[file].total = ev.total; + break; + + case 'load': + tracker[file].final = true; + resolve(this); + break; + + case 'error': + case 'timeout': + if (++tracker[file].attempts >= DOWNLOAD_ATTEMPTS_MAX) { + tracker[file].final = true; + reject(new Error("Failed loading file '" + file + "'")); + } else { + loadXHR(resolve, reject, file); + } + break; + + case 'abort': + tracker[file].final = true; + reject(new Error("Loading file '" + file + "' was aborted.")); + break; + } + } +})(); diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp index 5a161dd0ab..4a97bf4c32 100644 --- a/platform/javascript/export/export.cpp +++ b/platform/javascript/export/export.cpp @@ -100,8 +100,8 @@ void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Re for (int i = 0; i < lines.size(); i++) { String current_line = lines[i]; - current_line = current_line.replace("$GODOT_TMEM", itos(memory_mb * 1024 * 1024)); - current_line = current_line.replace("$GODOT_BASE", p_name); + current_line = current_line.replace("$GODOT_TOTAL_MEMORY", itos(memory_mb * 1024 * 1024)); + current_line = current_line.replace("$GODOT_BASENAME", p_name); current_line = current_line.replace("$GODOT_HEAD_INCLUDE", p_preset->get("html/head_include")); current_line = current_line.replace("$GODOT_DEBUG_ENABLED", p_debug ? "true" : "false"); str_export += current_line + "\n"; @@ -114,28 +114,6 @@ void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Re } } -void EditorExportPlatformJavaScript::_fix_fsloader_js(Vector<uint8_t> &p_js, const String &p_pack_name, uint64_t p_pack_size) { - - String str_template = String::utf8(reinterpret_cast<const char *>(p_js.ptr()), p_js.size()); - String str_export; - Vector<String> lines = str_template.split("\n"); - for (int i = 0; i < lines.size(); i++) { - if (lines[i].find("$GODOT_PACK_NAME") != -1) { - str_export += lines[i].replace("$GODOT_PACK_NAME", p_pack_name); - } else if (lines[i].find("$GODOT_PACK_SIZE") != -1) { - str_export += lines[i].replace("$GODOT_PACK_SIZE", itos(p_pack_size)); - } else { - str_export += lines[i] + "\n"; - } - } - - CharString cs = str_export.utf8(); - p_js.resize(cs.length()); - for (int i = 0; i < cs.length(); i++) { - p_js[i] = cs[i]; - } -} - void EditorExportPlatformJavaScript::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) { if (p_preset->get("texture_format/s3tc")) { @@ -286,10 +264,6 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese _fix_html(data, p_preset, p_path.get_file().get_basename(), p_debug); file = p_path.get_file(); - } else if (file == "godotfs.js") { - - _fix_fsloader_js(data, pck_path.get_file(), pack_size); - file = p_path.get_file().get_basename() + "fs.js"; } else if (file == "godot.js") { file = p_path.get_file().get_basename() + ".js"; diff --git a/platform/javascript/godot_shell.html b/platform/javascript/godot_shell.html deleted file mode 100644 index ee7399a129..0000000000 --- a/platform/javascript/godot_shell.html +++ /dev/null @@ -1,347 +0,0 @@ -<!DOCTYPE html> -<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang=""> -<head> - <meta charset="utf-8" /> - <title></title> - <style type="text/css"> - body { - margin: 0; - border: 0 none; - padding: 0; - text-align: center; - background-color: #222226; - font-family: 'Droid Sans', Arial, sans-serif; - } - - - /* Godot Engine default theme style - * ================================ */ - - .godot { - color: #e0e0e0; - background-color: #3b3943; - background-image: linear-gradient(to bottom, #403e48, #35333c); - border: 1px solid #45434e; - box-shadow: 0 0 1px 1px #2f2d35; - } - - button.godot { - font-family: 'Droid Sans', Arial, sans-serif; /* override user agent style */ - padding: 1px 5px; - background-color: #37353f; - background-image: linear-gradient(to bottom, #413e49, #3a3842); - border: 1px solid #514f5d; - border-radius: 1px; - box-shadow: 0 0 1px 1px #2a2930; - } - - button.godot:hover { - color: #f0f0f0; - background-color: #44414e; - background-image: linear-gradient(to bottom, #494652, #423f4c); - border: 1px solid #5a5667; - box-shadow: 0 0 1px 1px #26252b; - } - - button.godot:active { - color: #fff; - background-color: #3e3b46; - background-image: linear-gradient(to bottom, #36343d, #413e49); - border: 1px solid #4f4c59; - box-shadow: 0 0 1px 1px #26252b; - } - - button.godot:disabled { - color: rgba(230, 230, 230, 0.2); - background-color: #3d3d3d; - background-image: linear-gradient(to bottom, #434343, #393939); - border: 1px solid #474747; - box-shadow: 0 0 1px 1px #2d2b33; - } - - - /* Canvas / wrapper - * ================ */ - - #container { - display: inline-block; /* scale with canvas */ - vertical-align: top; /* prevent extra height */ - position: relative; /* root for absolutely positioned overlay */ - margin: 0; - border: 0 none; - padding: 0; - background-color: #0c0c0c; - } - - #canvas { - display: block; - margin: 0 auto; - /* canvas must have border and padding set to zero to - * calculate cursor coordinates correctly */ - border: 0 none; - padding: 0; - color: white; - } - - #canvas:focus { - outline: none; - } - - - /* Status display - * ============== */ - - #status-container { - position: absolute; - left: 0; - top: 0; - right: 0; - bottom: 0; - display: flex; - justify-content: center; - align-items: center; - /* don't consume click events - make children visible explicitly */ - visibility: hidden; - } - - #status { - line-height: 1.3; - cursor: pointer; - visibility: visible; - padding: 4px 6px; - } - - - /* Debug output - * ============ */ - - #output-panel { - display: none; - max-width: 700px; - font-size: small; - margin: 6px auto 0; - padding: 0 4px 4px; - text-align: left; - line-height: 2.2; - } - - #output-header { - display: flex; - justify-content: space-between; - align-items: center; - } - - #output-container { - padding: 6px; - background-color: #2c2a32; - box-shadow: inset 0 0 1px 1px #232127; - color: #bbb; - } - - #output-scroll { - line-height: 1; - height: 12em; - overflow-y: scroll; - white-space: pre-wrap; - font-size: small; - font-family: "Lucida Console", Monaco, monospace; - } - </style> -$GODOT_HEAD_INCLUDE -</head> -<body> - <div id="container"> - <canvas id="canvas" width="640" height="480" tabindex="0" oncontextmenu="event.preventDefault();"> - HTML5 canvas appears to be unsupported in the current browser.<br /> - Please try updating or use a different browser. - </canvas> - <div id="status-container"> - <span id="status" class="godot" onclick="this.style.visibility='hidden';">Downloading page...</span> - </div> - </div> - <div id="output-panel" class="godot"> - <div id="output-header"> - Output: - <button class="godot" type="button" autocomplete="off" onclick="Presentation.clearOutput();">Clear</button> - </div> - <div id="output-container"><div id="output-scroll"></div></div> - </div> - - <!-- Scripts --> - <script type="text/javascript">//<![CDATA[ - var Presentation = (function() { - var statusElement = document.getElementById("status"); - var canvasElement = document.getElementById("canvas"); - - var presentation = { - postRun: [], - setStatusVisible: function setStatusVisible(visible) { - statusElement.style.visibility = (visible ? "visible" : "hidden"); - }, - setStatus: function setStatus(text) { - if (text.length === 0) { - // emscripten sets empty string as status after "Running..." - // per timeout, but another status may have been set by then - if (Presentation.setStatus.lastText === "Running...") - Presentation.setStatusVisible(false); - return; - } - Presentation.setStatus.lastText = text; - while (statusElement.lastChild) { - statusElement.removeChild(statusElement.lastChild); - } - var lines = text.split("\n"); - lines.forEach(function(line, index) { - statusElement.appendChild(document.createTextNode(line)); - statusElement.appendChild(document.createElement("br")); - }); - var closeNote = document.createElement("span"); - closeNote.style.fontSize = "small"; - closeNote.textContent = "click to close"; - statusElement.appendChild(closeNote); - Presentation.setStatusVisible(true); - }, - isWebGL2Available: function isWebGL2Available() { - var context; - try { - context = canvasElement.getContext("webgl2") || canvasElement.getContext("experimental-webgl2"); - } catch (e) {} - return !!context; - }, - }; - - window.onerror = function(event) { presentation.setStatus("Failure during start-up\nSee JavaScript console") }; - - if ($GODOT_DEBUG_ENABLED) { // debugging enabled - var outputRoot = document.getElementById("output-panel"); - var outputElement = document.getElementById("output-scroll"); - const maxOutputMessages = 400; - - presentation.setOutputVisible = function setOutputVisible(visible) { - outputRoot.style.display = (visible ? "block" : "none"); - }; - presentation.clearOutput = function clearOutput() { - while (outputElement.firstChild) { - outputElement.firstChild.remove(); - } - }; - - presentation.setOutputVisible(true); - - presentation.print = function print(text) { - if (arguments.length > 1) { - text = Array.prototype.slice.call(arguments).join(" "); - } - if (text.length <= 0) return; - while (outputElement.childElementCount >= maxOutputMessages) { - outputElement.firstChild.remove(); - } - var msg = document.createElement("div"); - if (String.prototype.trim.call(text).startsWith("**ERROR**") - || String.prototype.trim.call(text).startsWith("**EXCEPTION**")) { - msg.style.color = "#d44"; - } else if (String.prototype.trim.call(text).startsWith("**WARNING**")) { - msg.style.color = "#ccc000"; - } else if (String.prototype.trim.call(text).startsWith("**SCRIPT ERROR**")) { - msg.style.color = "#c6d"; - } - msg.textContent = text; - var scrollToBottom = outputElement.scrollHeight - (outputElement.clientHeight + outputElement.scrollTop) < 10; - outputElement.appendChild(msg); - if (scrollToBottom) { - outputElement.scrollTop = outputElement.scrollHeight; - } - }; - - presentation.postRun.push(function() { - window.onerror = function(event) { presentation.print("**EXCEPTION**:", event) }; - }); - - } else { - presentation.postRun.push(function() { window.onerror = null; }); - } - - return presentation; - })(); - - // Emscripten interface - var Module = (function() { - const BASE_NAME = '$GODOT_BASE'; - var module = { - thisProgram: BASE_NAME, - wasmBinaryFile: BASE_NAME + '.wasm', - TOTAL_MEMORY: $GODOT_TMEM, - print: function print(text) { - if (arguments.length > 1) { - text = Array.prototype.slice.call(arguments).join(" "); - } - console.log(text); - if (typeof Presentation !== "undefined" && typeof Presentation.print === "function") { - Presentation.print(text); - } - }, - printErr: function printErr(text) { - if (arguments.length > 1) { - text = Array.prototype.slice.call(arguments).join(" "); - } - console.error(text); - if (typeof Presentation !== "undefined" && typeof Presentation.print === "function") { - Presentation.print("**ERROR**:", text) - } - }, - canvas: document.getElementById("canvas"), - setStatus: function setStatus(text) { - var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/); - var now = Date.now(); - if (m) { - if (now - Date.now() < 30) // if this is a progress update, skip it if too soon - return; - text = m[1]; - } - if (typeof Presentation !== "undefined" && typeof Presentation.setStatus == "function") { - Presentation.setStatus(text); - } - } - }; - - // As a default initial behavior, pop up an alert when WebGL context is lost. To make your - // application robust, you may want to override this behavior before shipping! - // See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2 - module.canvas.addEventListener("webglcontextlost", function(e) { alert("WebGL context lost. Plase reload the page."); e.preventDefault(); }, false); - - if (typeof Presentation !== "undefined" && Presentation.postRun instanceof Array) { - module.postRun = Presentation.postRun; - } - - return module; - })(); - - if (!Presentation.isWebGL2Available()) { - Presentation.setStatus("WebGL 2 appears to be unsupported.\nPlease update browser and drivers."); - Presentation.preventLoading = true; - } else { - Presentation.setStatus("Downloading..."); - } - - if (Presentation.preventLoading) { - // prevent *fs.js and Emscripten's SCRIPT placeholder from loading any files - Presentation._XHR_send = XMLHttpRequest.prototype.send; - XMLHttpRequest.prototype.send = function() {}; - Presentation._Node_appendChild = Node.prototype.appendChild; - Node.prototype.appendChild = function(node) { - if (!(node instanceof HTMLScriptElement)) { - return Presentation._Node_appendChild.call(this, node); - } - } - } - //]]></script> - <script type="text/javascript" src="$GODOT_BASEfs.js"></script> -{{{ SCRIPT }}} - <script type="text/javascript"> - if (Presentation.preventLoading) { - XMLHttpRequest.prototype.send = Presentation._XHR_send; - Node.prototype.appendChild = Presentation._Node_appendChild; - } - </script> -</body> -</html> diff --git a/platform/javascript/javascript_eval.cpp b/platform/javascript/javascript_eval.cpp index 74f8d80a76..1d737879f6 100644 --- a/platform/javascript/javascript_eval.cpp +++ b/platform/javascript/javascript_eval.cpp @@ -39,24 +39,41 @@ JavaScript *JavaScript::get_singleton() { return singleton; } +extern "C" EMSCRIPTEN_KEEPALIVE uint8_t *resize_poolbytearray_and_open_write(PoolByteArray *p_arr, PoolByteArray::Write *r_write, int p_len) { + + p_arr->resize(p_len); + *r_write = p_arr->write(); + return r_write->ptr(); +} + Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) { union { - int i; + bool b; double d; char *s; } js_data[4]; + + PoolByteArray arr; + PoolByteArray::Write arr_write; + /* clang-format off */ Variant::Type return_type = static_cast<Variant::Type>(EM_ASM_INT({ + const CODE = $0; + const USE_GLOBAL_EXEC_CONTEXT = $1; + const PTR = $2; + const ELEM_LEN = $3; + const BYTEARRAY_PTR = $4; + const BYTEARRAY_WRITE_PTR = $5; var eval_ret; try { - if ($3) { // p_use_global_exec_context + if (USE_GLOBAL_EXEC_CONTEXT) { // indirect eval call grants global execution context var global_eval = eval; - eval_ret = global_eval(UTF8ToString($2)); + eval_ret = global_eval(UTF8ToString(CODE)); } else { - eval_ret = eval(UTF8ToString($2)); + eval_ret = eval(UTF8ToString(CODE)); } } catch (e) { Module.printErr(e); @@ -66,16 +83,11 @@ Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) { switch (typeof eval_ret) { case 'boolean': - // bitwise op yields 32-bit int - setValue($0, eval_ret|0, 'i32'); + setValue(PTR, eval_ret, 'i32'); return 1; // BOOL case 'number': - if ((eval_ret|0)===eval_ret) { - setValue($0, eval_ret|0, 'i32'); - return 2; // INT - } - setValue($0, eval_ret, 'double'); + setValue(PTR, eval_ret, 'double'); return 3; // REAL case 'string': @@ -85,7 +97,7 @@ Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) { if (array_ptr===0) { throw new Error('String allocation failed (probably out of memory)'); } - setValue($0, array_ptr|0 , '*'); + setValue(PTR, array_ptr , '*'); stringToUTF8(eval_ret, array_ptr, array_len); return 4; // STRING } catch (e) { @@ -102,41 +114,50 @@ Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) { break; } - else if (typeof eval_ret.x==='number' && typeof eval_ret.y==='number') { - setValue($0, eval_ret.x, 'double'); - setValue($0+$1, eval_ret.y, 'double'); + if (ArrayBuffer.isView(eval_ret) && !(eval_ret instanceof Uint8Array)) { + eval_ret = new Uint8Array(eval_ret.buffer); + } + else if (eval_ret instanceof ArrayBuffer) { + eval_ret = new Uint8Array(eval_ret); + } + if (eval_ret instanceof Uint8Array) { + var bytes_ptr = ccall('resize_poolbytearray_and_open_write', 'number', ['number', 'number' ,'number'], [BYTEARRAY_PTR, BYTEARRAY_WRITE_PTR, eval_ret.length]); + HEAPU8.set(eval_ret, bytes_ptr); + return 20; // POOL_BYTE_ARRAY + } + + if (typeof eval_ret.x==='number' && typeof eval_ret.y==='number') { + setValue(PTR, eval_ret.x, 'double'); + setValue(PTR + ELEM_LEN, eval_ret.y, 'double'); if (typeof eval_ret.z==='number') { - setValue($0+$1*2, eval_ret.z, 'double'); + setValue(PTR + ELEM_LEN*2, eval_ret.z, 'double'); return 7; // VECTOR3 } else if (typeof eval_ret.width==='number' && typeof eval_ret.height==='number') { - setValue($0+$1*2, eval_ret.width, 'double'); - setValue($0+$1*3, eval_ret.height, 'double'); + setValue(PTR + ELEM_LEN*2, eval_ret.width, 'double'); + setValue(PTR + ELEM_LEN*3, eval_ret.height, 'double'); return 6; // RECT2 } return 5; // VECTOR2 } - else if (typeof eval_ret.r==='number' && typeof eval_ret.g==='number' && typeof eval_ret.b==='number') { - // assume 8-bit rgb components since we're on the web - setValue($0, eval_ret.r, 'double'); - setValue($0+$1, eval_ret.g, 'double'); - setValue($0+$1*2, eval_ret.b, 'double'); - setValue($0+$1*3, typeof eval_ret.a==='number' ? eval_ret.a : 1, 'double'); + if (typeof eval_ret.r === 'number' && typeof eval_ret.g === 'number' && typeof eval_ret.b === 'number') { + setValue(PTR, eval_ret.r, 'double'); + setValue(PTR + ELEM_LEN, eval_ret.g, 'double'); + setValue(PTR + ELEM_LEN*2, eval_ret.b, 'double'); + setValue(PTR + ELEM_LEN*3, typeof eval_ret.a === 'number' ? eval_ret.a : 1, 'double'); return 14; // COLOR } break; } return 0; // NIL - }, js_data, sizeof *js_data, p_code.utf8().get_data(), p_use_global_exec_context)); + }, p_code.utf8().get_data(), p_use_global_exec_context, js_data, sizeof *js_data, &arr, &arr_write)); /* clang-format on */ switch (return_type) { case Variant::BOOL: - return !!js_data->i; - case Variant::INT: - return js_data->i; + return js_data->b; case Variant::REAL: return js_data->d; case Variant::STRING: { @@ -153,7 +174,10 @@ Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) { case Variant::RECT2: return Rect2(js_data[0].d, js_data[1].d, js_data[2].d, js_data[3].d); case Variant::COLOR: - return Color(js_data[0].d / 255., js_data[1].d / 255., js_data[2].d / 255., js_data[3].d); + return Color(js_data[0].d, js_data[1].d, js_data[2].d, js_data[3].d); + case Variant::POOL_BYTE_ARRAY: + arr_write = PoolByteArray::Write(); + return arr; } return Variant(); } diff --git a/platform/javascript/javascript_main.cpp b/platform/javascript/javascript_main.cpp index 4c948bf181..ed4f416cfd 100644 --- a/platform/javascript/javascript_main.cpp +++ b/platform/javascript/javascript_main.cpp @@ -39,8 +39,13 @@ static void main_loop() { os->main_loop_iterate(); } -extern "C" void main_after_fs_sync() { +extern "C" void main_after_fs_sync(char *p_idbfs_err) { + String idbfs_err = String::utf8(p_idbfs_err); + if (!idbfs_err.empty()) { + print_line("IndexedDB not available: " + idbfs_err); + } + os->set_idbfs_available(idbfs_err.empty()); // Ease up compatibility ResourceLoader::set_abort_on_missing_resources(false); Main::start(); @@ -60,14 +65,7 @@ int main(int argc, char *argv[]) { FS.mkdir('/userfs'); FS.mount(IDBFS, {}, '/userfs'); FS.syncfs(true, function(err) { - if (err) { - Module.setStatus('Failed to load persistent data\nPlease allow (third-party) cookies'); - Module.printErr('Failed to populate IDB file system: ' + err.message); - Module.noExitRuntime = false; - } else { - Module.print('Successfully populated IDB file system'); - ccall('main_after_fs_sync', null); - } + Module['ccall']('main_after_fs_sync', null, ['string'], [err ? err.message : ""]) }); ); /* clang-format on */ diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp index a0e5610591..f6446e77da 100644 --- a/platform/javascript/os_javascript.cpp +++ b/platform/javascript/os_javascript.cpp @@ -85,6 +85,10 @@ void OS_JavaScript::initialize_core() { FileAccess::make_default<FileAccessBufferedFA<FileAccessUnix> >(FileAccess::ACCESS_RESOURCES); } +void OS_JavaScript::initialize_logger() { + _set_logger(memnew(StdLogger)); +} + void OS_JavaScript::set_opengl_extensions(const char *p_gl_extensions) { ERR_FAIL_COND(!p_gl_extensions); @@ -172,14 +176,14 @@ static EM_BOOL _mousebutton_callback(int event_type, const EmscriptenMouseEvent if (!is_canvas_focused()) { focus_canvas(); } - mask |= 1 << ev->get_button_index(); - } else if (mask & (1 << ev->get_button_index())) { - mask &= ~(1 << ev->get_button_index()); + mask |= ev->get_button_index(); + } else if (mask & ev->get_button_index()) { + mask &= ~ev->get_button_index(); } else { // release event, but press was outside the canvas, so ignore return false; } - ev->set_button_mask(mask >> 1); + ev->set_button_mask(mask); _input->parse_input_event(ev); // prevent selection dragging @@ -200,7 +204,7 @@ static EM_BOOL _mousemove_callback(int event_type, const EmscriptenMouseEvent *m Ref<InputEventMouseMotion> ev; ev.instance(); dom2godot_mod(mouse_event, ev); - ev->set_button_mask(input_mask >> 1); + ev->set_button_mask(input_mask); ev->set_position(pos); ev->set_global_position(ev->get_position()); @@ -227,7 +231,7 @@ static EM_BOOL _wheel_callback(int event_type, const EmscriptenWheelEvent *wheel Ref<InputEventMouseButton> ev; ev.instance(); - ev->set_button_mask(_input->get_mouse_button_mask() >> 1); + ev->set_button_mask(_input->get_mouse_button_mask()); ev->set_position(_input->get_mouse_position()); ev->set_global_position(ev->get_position()); @@ -291,7 +295,7 @@ static EM_BOOL _touchpress_callback(int event_type, const EmscriptenTouchEvent * Ref<InputEventMouseButton> ev_mouse; ev_mouse.instance(); - ev_mouse->set_button_mask(_input->get_mouse_button_mask() >> 1); + ev_mouse->set_button_mask(_input->get_mouse_button_mask()); dom2godot_mod(touch_event, ev_mouse); const EmscriptenTouchPoint &first_touch = touch_event->touches[lowest_id_index]; @@ -334,7 +338,7 @@ static EM_BOOL _touchmove_callback(int event_type, const EmscriptenTouchEvent *t Ref<InputEventMouseMotion> ev_mouse; ev_mouse.instance(); dom2godot_mod(touch_event, ev_mouse); - ev_mouse->set_button_mask(_input->get_mouse_button_mask() >> 1); + ev_mouse->set_button_mask(_input->get_mouse_button_mask()); const EmscriptenTouchPoint &first_touch = touch_event->touches[lowest_id_index]; ev_mouse->set_position(Point2(first_touch.canvasX, first_touch.canvasY)); @@ -464,11 +468,7 @@ void OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, i print_line("Init Audio"); AudioDriverManager::add_driver(&audio_driver_javascript); - audio_driver_javascript.set_singleton(); - if (audio_driver_javascript.init() != OK) { - - ERR_PRINT("Initializing audio failed."); - } + AudioDriverManager::initialize(p_audio_driver); RasterizerGLES3::register_config(); RasterizerGLES3::make_current(); @@ -521,21 +521,6 @@ void OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, i #undef SET_EM_CALLBACK #undef EM_CHECK - /* clang-format off */ - EM_ASM_ARGS({ - const send_notification = Module.cwrap('send_notification', null, ['number']); - const notifs = arguments; - (['mouseover', 'mouseleave', 'focus', 'blur']).forEach(function(event, i) { - Module.canvas.addEventListener(event, send_notification.bind(this, notifs[i])); - }); - }, - MainLoop::NOTIFICATION_WM_MOUSE_ENTER, - MainLoop::NOTIFICATION_WM_MOUSE_EXIT, - MainLoop::NOTIFICATION_WM_FOCUS_IN, - MainLoop::NOTIFICATION_WM_FOCUS_OUT - ); -/* clang-format on */ - #ifdef JAVASCRIPT_EVAL_ENABLED javascript_eval = memnew(JavaScript); ProjectSettings::get_singleton()->add_singleton(ProjectSettings::Singleton("JavaScript", javascript_eval)); @@ -818,13 +803,29 @@ void OS_JavaScript::main_loop_begin() { if (main_loop) main_loop->init(); + + /* clang-format off */ + EM_ASM_ARGS({ + const send_notification = Module.cwrap('send_notification', null, ['number']); + const notifs = arguments; + (['mouseover', 'mouseleave', 'focus', 'blur']).forEach(function(event, i) { + Module.canvas.addEventListener(event, send_notification.bind(null, notifs[i])); + }); + }, + MainLoop::NOTIFICATION_WM_MOUSE_ENTER, + MainLoop::NOTIFICATION_WM_MOUSE_EXIT, + MainLoop::NOTIFICATION_WM_FOCUS_IN, + MainLoop::NOTIFICATION_WM_FOCUS_OUT + ); + /* clang-format on */ } + bool OS_JavaScript::main_loop_iterate() { if (!main_loop) return false; - if (time_to_save_sync >= 0) { + if (idbfs_available && time_to_save_sync >= 0) { int64_t newtime = get_ticks_msec(); int64_t elapsed = newtime - last_sync_time; last_sync_time = newtime; @@ -914,10 +915,10 @@ String OS_JavaScript::get_executable_path() const { void OS_JavaScript::_close_notification_funcs(const String &p_file, int p_flags) { - print_line("close " + p_file + " flags " + itos(p_flags)); - if (p_file.begins_with("/userfs") && p_flags & FileAccess::WRITE) { - static_cast<OS_JavaScript *>(get_singleton())->last_sync_time = OS::get_singleton()->get_ticks_msec(); - static_cast<OS_JavaScript *>(get_singleton())->time_to_save_sync = 5000; //five seconds since last save + OS_JavaScript *os = static_cast<OS_JavaScript *>(get_singleton()); + if (os->idbfs_available && p_file.begins_with("/userfs") && p_flags & FileAccess::WRITE) { + os->last_sync_time = OS::get_singleton()->get_ticks_msec(); + os->time_to_save_sync = 5000; //five seconds since last save } } @@ -975,7 +976,7 @@ String OS_JavaScript::get_joy_guid(int p_device) const { return input->get_joy_guid_remapped(p_device); } -PowerState OS_JavaScript::get_power_state() { +OS::PowerState OS_JavaScript::get_power_state() { return power_manager->get_power_state(); } @@ -992,6 +993,16 @@ bool OS_JavaScript::_check_internal_feature_support(const String &p_feature) { return p_feature == "web" || p_feature == "s3tc"; // TODO check for these features really being available } +void OS_JavaScript::set_idbfs_available(bool p_idbfs_available) { + + idbfs_available = p_idbfs_available; +} + +bool OS_JavaScript::is_userfs_persistent() const { + + return idbfs_available; +} + OS_JavaScript::OS_JavaScript(const char *p_execpath, GetDataDirFunc p_get_data_dir_func) { set_cmdline(p_execpath, get_cmdline_args()); main_loop = NULL; @@ -1003,6 +1014,7 @@ OS_JavaScript::OS_JavaScript(const char *p_execpath, GetDataDirFunc p_get_data_d get_data_dir_func = p_get_data_dir_func; FileAccessUnix::close_notification_func = _close_notification_funcs; + idbfs_available = false; time_to_save_sync = -1; } diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h index f78a3f2768..1c939d3fd5 100644 --- a/platform/javascript/os_javascript.h +++ b/platform/javascript/os_javascript.h @@ -49,6 +49,7 @@ typedef String (*GetDataDirFunc)(); class OS_JavaScript : public OS_Unix { + bool idbfs_available; int64_t time_to_save_sync; int64_t last_sync_time; @@ -92,6 +93,7 @@ public: virtual int get_audio_driver_count() const; virtual const char *get_audio_driver_name(int p_driver) const; + virtual void initialize_logger(); virtual void initialize_core(); virtual void initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver); @@ -104,11 +106,6 @@ public: //static OS* get_singleton(); - virtual void print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type) { - - OS::print_error(p_function, p_file, p_line, p_code, p_rationale, p_type); - } - virtual void alert(const String &p_alert, const String &p_title = "ALERT!"); virtual void set_mouse_mode(MouseMode p_mode); @@ -140,6 +137,8 @@ public: virtual bool can_draw() const; + virtual bool is_userfs_persistent() const; + virtual void set_cursor_shape(CursorShape p_shape); void main_loop_begin(); @@ -165,12 +164,14 @@ public: virtual String get_joy_guid(int p_device) const; bool joy_connection_changed(int p_type, const EmscriptenGamepadEvent *p_event); - virtual PowerState get_power_state(); + virtual OS::PowerState get_power_state(); virtual int get_power_seconds_left(); virtual int get_power_percent_left(); virtual bool _check_internal_feature_support(const String &p_feature); + void set_idbfs_available(bool p_idbfs_available); + OS_JavaScript(const char *p_execpath, GetDataDirFunc p_get_data_dir_func); ~OS_JavaScript(); }; diff --git a/platform/javascript/power_javascript.cpp b/platform/javascript/power_javascript.cpp index 3d54146595..10964502d4 100644 --- a/platform/javascript/power_javascript.cpp +++ b/platform/javascript/power_javascript.cpp @@ -36,12 +36,12 @@ bool PowerJavascript::UpdatePowerInfo() { return false; } -PowerState PowerJavascript::get_power_state() { +OS::PowerState PowerJavascript::get_power_state() { if (UpdatePowerInfo()) { return power_state; } else { WARN_PRINT("Power management is not implemented on this platform, defaulting to POWERSTATE_UNKNOWN"); - return POWERSTATE_UNKNOWN; + return OS::POWERSTATE_UNKNOWN; } } @@ -64,7 +64,7 @@ int PowerJavascript::get_power_percent_left() { } PowerJavascript::PowerJavascript() - : nsecs_left(-1), percent_left(-1), power_state(POWERSTATE_UNKNOWN) { + : nsecs_left(-1), percent_left(-1), power_state(OS::POWERSTATE_UNKNOWN) { } PowerJavascript::~PowerJavascript() { diff --git a/platform/javascript/power_javascript.h b/platform/javascript/power_javascript.h index 834d765557..8454c5d728 100644 --- a/platform/javascript/power_javascript.h +++ b/platform/javascript/power_javascript.h @@ -31,13 +31,13 @@ #ifndef PLATFORM_JAVASCRIPT_POWER_JAVASCRIPT_H_ #define PLATFORM_JAVASCRIPT_POWER_JAVASCRIPT_H_ -#include "os/power.h" +#include "os/os.h" class PowerJavascript { private: int nsecs_left; int percent_left; - PowerState power_state; + OS::PowerState power_state; bool UpdatePowerInfo(); @@ -45,7 +45,7 @@ public: PowerJavascript(); virtual ~PowerJavascript(); - PowerState get_power_state(); + OS::PowerState get_power_state(); int get_power_seconds_left(); int get_power_percent_left(); }; diff --git a/platform/javascript/pre_asmjs.js b/platform/javascript/pre_asmjs.js new file mode 100644 index 0000000000..3c497721b6 --- /dev/null +++ b/platform/javascript/pre_asmjs.js @@ -0,0 +1,3 @@ +var Engine = { + USING_WASM: false, + RuntimeEnvironment: function(Module) { diff --git a/platform/javascript/pre_wasm.js b/platform/javascript/pre_wasm.js new file mode 100644 index 0000000000..be4383c8c9 --- /dev/null +++ b/platform/javascript/pre_wasm.js @@ -0,0 +1,3 @@ +var Engine = { + USING_WASM: true, + RuntimeEnvironment: function(Module) { diff --git a/platform/osx/SCsub b/platform/osx/SCsub index 27117c2e8d..be3950bc6d 100644 --- a/platform/osx/SCsub +++ b/platform/osx/SCsub @@ -1,15 +1,22 @@ #!/usr/bin/env python +import os Import('env') +def make_debug(target, source, env): + os.system('dsymutil %s -o %s.dSYM' % (target[0], target[0])) + files = [ + 'crash_handler_osx.mm', 'os_osx.mm', 'godot_main_osx.mm', - 'audio_driver_osx.cpp', 'sem_osx.cpp', 'dir_access_osx.mm', 'joypad_osx.cpp', 'power_osx.cpp', ] -env.Program('#bin/godot', files) +binary = env.Program('#bin/godot', files) +if env["debug_symbols"] == "full" or env["debug_symbols"] == "yes": + env.AddPostAction(binary, make_debug) + diff --git a/platform/osx/audio_driver_osx.cpp b/platform/osx/audio_driver_osx.cpp deleted file mode 100644 index 8c5a734f76..0000000000 --- a/platform/osx/audio_driver_osx.cpp +++ /dev/null @@ -1,292 +0,0 @@ -/*************************************************************************/ -/* audio_driver_osx.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ -#ifdef OSX_ENABLED - -#include "audio_driver_osx.h" -#include "core/project_settings.h" -#include "os/os.h" - -#define kOutputBus 0 - -static OSStatus outputDeviceAddressCB(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *__nullable inClientData) { - AudioDriverOSX *driver = (AudioDriverOSX *)inClientData; - - driver->reopen(); - - return noErr; -} - -Error AudioDriverOSX::initDevice() { - AudioComponentDescription desc; - zeromem(&desc, sizeof(desc)); - desc.componentType = kAudioUnitType_Output; - desc.componentSubType = kAudioUnitSubType_HALOutput; - desc.componentManufacturer = kAudioUnitManufacturer_Apple; - - AudioComponent comp = AudioComponentFindNext(NULL, &desc); - ERR_FAIL_COND_V(comp == NULL, FAILED); - - OSStatus result = AudioComponentInstanceNew(comp, &audio_unit); - ERR_FAIL_COND_V(result != noErr, FAILED); - - AudioStreamBasicDescription strdesc; - - // TODO: Implement this - /*zeromem(&strdesc, sizeof(strdesc)); - UInt32 size = sizeof(strdesc); - result = AudioUnitGetProperty(audio_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kOutputBus, &strdesc, &size); - ERR_FAIL_COND_V(result != noErr, FAILED); - - switch (strdesc.mChannelsPerFrame) { - case 2: // Stereo - case 6: // Surround 5.1 - case 8: // Surround 7.1 - channels = strdesc.mChannelsPerFrame; - break; - - default: - // Unknown number of channels, default to stereo - channels = 2; - break; - }*/ - - mix_rate = GLOBAL_DEF("audio/mix_rate", 44100); - - zeromem(&strdesc, sizeof(strdesc)); - strdesc.mFormatID = kAudioFormatLinearPCM; - strdesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; - strdesc.mChannelsPerFrame = channels; - strdesc.mSampleRate = mix_rate; - strdesc.mFramesPerPacket = 1; - strdesc.mBitsPerChannel = 16; - strdesc.mBytesPerFrame = strdesc.mBitsPerChannel * strdesc.mChannelsPerFrame / 8; - strdesc.mBytesPerPacket = strdesc.mBytesPerFrame * strdesc.mFramesPerPacket; - - result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &strdesc, sizeof(strdesc)); - ERR_FAIL_COND_V(result != noErr, FAILED); - - int latency = GLOBAL_DEF("audio/output_latency", 25); - unsigned int buffer_size = closest_power_of_2(latency * mix_rate / 1000); - - if (OS::get_singleton()->is_stdout_verbose()) { - print_line("audio buffer size: " + itos(buffer_size) + " calculated latency: " + itos(buffer_size * 1000 / mix_rate)); - } - - samples_in.resize(buffer_size); - buffer_frames = buffer_size / channels; - - AURenderCallbackStruct callback; - zeromem(&callback, sizeof(AURenderCallbackStruct)); - callback.inputProc = &AudioDriverOSX::output_callback; - callback.inputProcRefCon = this; - result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, kOutputBus, &callback, sizeof(callback)); - ERR_FAIL_COND_V(result != noErr, FAILED); - - result = AudioUnitInitialize(audio_unit); - ERR_FAIL_COND_V(result != noErr, FAILED); - - 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); - - return initDevice(); -}; - -Error AudioDriverOSX::reopen() { - bool restart = false; - - lock(); - - if (active) { - restart = true; - } - - Error 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) { - - AudioDriverOSX *ad = (AudioDriverOSX *)inRefCon; - - if (!ad->active || !ad->try_lock()) { - for (unsigned int i = 0; i < ioData->mNumberBuffers; i++) { - AudioBuffer *abuf = &ioData->mBuffers[i]; - zeromem(abuf->mData, abuf->mDataByteSize); - }; - return 0; - }; - - for (unsigned int i = 0; i < ioData->mNumberBuffers; i++) { - - 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->audio_server_process(frames, ad->samples_in.ptr()); - - for (int j = 0; j < frames * ad->channels; j++) { - - out[j] = ad->samples_in[j] >> 16; - } - - frames_left -= frames; - out += frames * ad->channels; - }; - }; - - ad->unlock(); - - return 0; -}; - -void AudioDriverOSX::start() { - if (!active) { - OSStatus result = AudioOutputUnitStart(audio_unit); - if (result != noErr) { - ERR_PRINT("AudioOutputUnitStart failed"); - } else { - active = true; - } - } -}; - -int AudioDriverOSX::get_mix_rate() const { - return 44100; -}; - -AudioDriver::SpeakerMode AudioDriverOSX::get_speaker_mode() const { - return SPEAKER_MODE_STEREO; -}; - -void AudioDriverOSX::lock() { - if (mutex) - mutex->lock(); -}; - -void AudioDriverOSX::unlock() { - if (mutex) - mutex->unlock(); -}; - -bool AudioDriverOSX::try_lock() { - if (mutex) - return mutex->try_lock() == OK; - return true; -} - -void AudioDriverOSX::finish() { - finishDevice(); - - OSStatus result = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &outputDeviceAddress, &outputDeviceAddressCB, this); - if (result != noErr) { - ERR_PRINT("AudioObjectRemovePropertyListener failed"); - } - - AURenderCallbackStruct callback; - zeromem(&callback, sizeof(AURenderCallbackStruct)); - result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, kOutputBus, &callback, sizeof(callback)); - if (result != noErr) { - ERR_PRINT("AudioUnitSetProperty failed"); - } - - if (mutex) { - memdelete(mutex); - mutex = NULL; - } -}; - -AudioDriverOSX::AudioDriverOSX() { - active = false; - mutex = NULL; - - mix_rate = 44100; - channels = 2; - samples_in.clear(); -}; - -AudioDriverOSX::~AudioDriverOSX(){}; - -#endif diff --git a/platform/osx/audio_driver_osx.h b/platform/osx/crash_handler_osx.h index ac178b89f3..ff037e6b7a 100644 --- a/platform/osx/audio_driver_osx.h +++ b/platform/osx/crash_handler_osx.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* audio_driver_osx.h */ +/* crash_handler_osx.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -27,58 +27,21 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef OSX_ENABLED +#ifndef CRASH_HANDLER_OSX_H +#define CRASH_HANDLER_OSX_H -#ifndef AUDIO_DRIVER_OSX_H -#define AUDIO_DRIVER_OSX_H +class CrashHandler { -#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; - - int mix_rate; - int channels; - int buffer_frames; - - Vector<int32_t> samples_in; - - static OSStatus output_callback(void *inRefCon, - AudioUnitRenderActionFlags *ioActionFlags, - const AudioTimeStamp *inTimeStamp, - UInt32 inBusNumber, UInt32 inNumberFrames, - AudioBufferList *ioData); - - Error initDevice(); - Error finishDevice(); + bool disabled; public: - const char *get_name() const { - return "AudioUnit"; - }; - - virtual Error init(); - virtual void start(); - virtual int get_mix_rate() const; - virtual SpeakerMode get_speaker_mode() const; - virtual void lock(); - virtual void unlock(); - virtual void finish(); + void initialize(); - bool try_lock(); - Error reopen(); + void disable(); + bool is_disabled() const { return disabled; }; - AudioDriverOSX(); - ~AudioDriverOSX(); + CrashHandler(); + ~CrashHandler(); }; #endif - -#endif diff --git a/platform/osx/crash_handler_osx.mm b/platform/osx/crash_handler_osx.mm new file mode 100644 index 0000000000..2ed88db309 --- /dev/null +++ b/platform/osx/crash_handler_osx.mm @@ -0,0 +1,179 @@ +/*************************************************************************/ +/* crash_handler_osx.mm */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "main/main.h" +#include "os_osx.h" + +#include <string.h> +#include <unistd.h> + +// Note: Dump backtrace in 32bit mode is getting a bus error on the fgets by the ->execute, so enable only on 64bit +#if defined(DEBUG_ENABLED) && defined(__x86_64__) +#define CRASH_HANDLER_ENABLED 1 +#endif + +#ifdef CRASH_HANDLER_ENABLED +#include <cxxabi.h> +#include <dlfcn.h> +#include <execinfo.h> +#include <signal.h> +#include <stdlib.h> + +#include <mach-o/dyld.h> +#include <mach-o/getsect.h> + +#ifdef __x86_64__ +static uint64_t load_address() { + const struct segment_command_64 *cmd = getsegbyname("__TEXT"); +#else +static uint32_t load_address() { + const struct segment_command *cmd = getsegbyname("__TEXT"); +#endif + char full_path[1024]; + uint32_t size = sizeof(full_path); + + if (cmd && !_NSGetExecutablePath(full_path, &size)) { + uint32_t dyld_count = _dyld_image_count(); + for (uint32_t i = 0; i < dyld_count; i++) { + const char *image_name = _dyld_get_image_name(i); + if (image_name && strncmp(image_name, full_path, 1024) == 0) { + return cmd->vmaddr + _dyld_get_image_vmaddr_slide(i); + } + } + } + + return 0; +} + +static void handle_crash(int sig) { + if (OS::get_singleton() == NULL) + return; + + void *bt_buffer[256]; + size_t size = backtrace(bt_buffer, 256); + String _execpath = OS::get_singleton()->get_executable_path(); + String msg = GLOBAL_GET("debug/settings/crash_handler/message"); + + // Dump the backtrace to stderr with a message to the user + fprintf(stderr, "%s: Program crashed with signal %d\n", __FUNCTION__, sig); + fprintf(stderr, "Dumping the backtrace. %ls\n", msg.c_str()); + char **strings = backtrace_symbols(bt_buffer, size); + if (strings) { + void *load_addr = (void *)load_address(); + + for (int i = 1; i < size; i++) { + char fname[1024]; + Dl_info info; + + snprintf(fname, 1024, "%s", strings[i]); + + // Try to demangle the function name to provide a more readable one + if (dladdr(bt_buffer[i], &info) && info.dli_sname) { + if (info.dli_sname[0] == '_') { + int status; + char *demangled = abi::__cxa_demangle(info.dli_sname, NULL, 0, &status); + + if (status == 0 && demangled) { + snprintf(fname, 1024, "%s", demangled); + } + + if (demangled) + free(demangled); + } + } + + String output = fname; + + // Try to get the file/line number using atos + if (bt_buffer[i] > (void *)0x0 && OS::get_singleton()) { + List<String> args; + char str[1024]; + + args.push_back("-o"); + args.push_back(_execpath); + args.push_back("-arch"); +#ifdef __x86_64__ + args.push_back("x86_64"); +#else + args.push_back("i386"); +#endif + args.push_back("-l"); + snprintf(str, 1024, "%p", load_addr); + args.push_back(str); + snprintf(str, 1024, "%p", bt_buffer[i]); + args.push_back(str); + + int ret; + String out = ""; + Error err = OS::get_singleton()->execute(String("atos"), args, true, NULL, &out, &ret); + if (err == OK && out.substr(0, 2) != "0x") { + out.erase(out.length() - 1, 1); + output = out; + } + } + + fprintf(stderr, "[%d] %ls\n", i, output.c_str()); + } + + free(strings); + } + fprintf(stderr, "-- END OF BACKTRACE --\n"); + + // Abort to pass the error to the OS + abort(); +} +#endif + +CrashHandler::CrashHandler() { + disabled = false; +} + +CrashHandler::~CrashHandler() { +} + +void CrashHandler::disable() { + if (disabled) + return; + +#ifdef CRASH_HANDLER_ENABLED + signal(SIGSEGV, NULL); + signal(SIGFPE, NULL); + signal(SIGILL, NULL); +#endif + + disabled = true; +} + +void CrashHandler::initialize() { +#ifdef CRASH_HANDLER_ENABLED + signal(SIGSEGV, handle_crash); + signal(SIGFPE, handle_crash); + signal(SIGILL, handle_crash); +#endif +} diff --git a/platform/osx/detect.py b/platform/osx/detect.py index d3ebdfe992..31032659b6 100644 --- a/platform/osx/detect.py +++ b/platform/osx/detect.py @@ -19,9 +19,11 @@ def can_build(): def get_opts(): + from SCons.Variables import EnumVariable return [ ('osxcross_sdk', 'OSXCross SDK version', 'darwin14'), + EnumVariable('debug_symbols', 'Add debug symbols to release version', 'yes', ('yes', 'no', 'full')), ] @@ -36,10 +38,18 @@ def configure(env): ## Build type if (env["target"] == "release"): - env.Prepend(CCFLAGS=['-O2', '-ffast-math', '-fomit-frame-pointer', '-ftree-vectorize', '-msse2']) + env.Prepend(CCFLAGS=['-O3', '-ffast-math', '-fomit-frame-pointer', '-ftree-vectorize', '-msse2']) + if (env["debug_symbols"] == "yes"): + env.Prepend(CCFLAGS=['-g1']) + if (env["debug_symbols"] == "full"): + env.Prepend(CCFLAGS=['-g2']) elif (env["target"] == "release_debug"): env.Prepend(CCFLAGS=['-O2', '-DDEBUG_ENABLED']) + if (env["debug_symbols"] == "yes"): + env.Prepend(CCFLAGS=['-g1']) + if (env["debug_symbols"] == "full"): + env.Prepend(CCFLAGS=['-g2']) elif (env["target"] == "debug"): env.Prepend(CCFLAGS=['-g3', '-DDEBUG_ENABLED', '-DDEBUG_MEMORY_ENABLED']) @@ -52,7 +62,7 @@ def configure(env): ## Compiler configuration - if (not os.environ.has_key("OSXCROSS_ROOT")): # regular native build + if "OSXCROSS_ROOT" not in os.environ: # regular native build if (env["bits"] == "fat"): env.Append(CCFLAGS=['-arch', 'i386', '-arch', 'x86_64']) env.Append(LINKFLAGS=['-arch', 'i386', '-arch', 'x86_64']) @@ -87,13 +97,13 @@ def configure(env): ## Dependencies - if (env['builtin_libtheora'] != 'no'): + if env['builtin_libtheora']: env["x86_libtheora_opt_gcc"] = True ## Flags env.Append(CPPPATH=['#platform/osx']) - env.Append(CPPFLAGS=['-DOSX_ENABLED', '-DUNIX_ENABLED', '-DGLES2_ENABLED', '-DAPPLE_STYLE_KEYS']) + env.Append(CPPFLAGS=['-DOSX_ENABLED', '-DUNIX_ENABLED', '-DGLES2_ENABLED', '-DAPPLE_STYLE_KEYS', '-DCOREAUDIO_ENABLED']) env.Append(LINKFLAGS=['-framework', 'Cocoa', '-framework', 'Carbon', '-framework', 'OpenGL', '-framework', 'AGL', '-framework', 'AudioUnit', '-framework', 'CoreAudio', '-lz', '-framework', 'IOKit', '-framework', 'ForceFeedback']) env.Append(LIBS=['pthread']) diff --git a/platform/osx/dir_access_osx.h b/platform/osx/dir_access_osx.h index c988dfe425..9a7773f5ee 100644 --- a/platform/osx/dir_access_osx.h +++ b/platform/osx/dir_access_osx.h @@ -46,6 +46,9 @@ class DirAccessOSX : public DirAccessUnix { protected: virtual String fix_unicode_name(const char *p_name) const; + + virtual int get_drive_count(); + virtual String get_drive(int p_drive); }; #endif //UNIX ENABLED diff --git a/platform/osx/dir_access_osx.mm b/platform/osx/dir_access_osx.mm index 6e8ceb5e19..6121e6ccfb 100644 --- a/platform/osx/dir_access_osx.mm +++ b/platform/osx/dir_access_osx.mm @@ -33,7 +33,8 @@ #include <errno.h> -#include <Foundation/NSString.h> +#include <AppKit/NSWorkspace.h> +#include <Foundation/Foundation.h> String DirAccessOSX::fix_unicode_name(const char *p_name) const { @@ -45,4 +46,19 @@ String DirAccessOSX::fix_unicode_name(const char *p_name) const { return fname; } +int DirAccessOSX::get_drive_count() { + NSArray *vols = [[NSWorkspace sharedWorkspace] mountedLocalVolumePaths]; + return [vols count]; +} + +String DirAccessOSX::get_drive(int p_drive) { + NSArray *vols = [[NSWorkspace sharedWorkspace] mountedLocalVolumePaths]; + int count = [vols count]; + + ERR_FAIL_INDEX_V(p_drive, count, ""); + + NSString *path = vols[p_drive]; + return String([path UTF8String]); +} + #endif //posix_enabled diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp index 7f749030ec..8a6f1dc04c 100644 --- a/platform/osx/export/export.cpp +++ b/platform/osx/export/export.cpp @@ -53,9 +53,16 @@ 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); + +#ifdef OSX_ENABLED + bool use_codesign() const { return true; } + bool use_dmg() const { return true; } +#else + bool use_codesign() const { return false; } + bool use_dmg() const { return false; } #endif protected: @@ -67,11 +74,7 @@ public: 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 String get_binary_extension() const { return use_dmg() ? "dmg" : "zip"; } 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; @@ -88,8 +91,25 @@ public: }; void EditorExportPlatformOSX::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) { + if (p_preset->get("texture_format/s3tc")) { + r_features->push_back("s3tc"); + } + if (p_preset->get("texture_format/etc")) { + r_features->push_back("etc"); + } + if (p_preset->get("texture_format/etc2")) { + r_features->push_back("etc2"); + } - // what does this need to do? + int bits = p_preset->get("application/bits_mode"); + + if (bits == 0 || bits == 1) { + r_features->push_back("64"); + } + + if (bits == 0 || bits == 2) { + r_features->push_back("32"); + } } void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options) { @@ -112,6 +132,10 @@ void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options) r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/entitlements"), "")); #endif + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc2"), false)); } void EditorExportPlatformOSX::_make_icon(const Ref<Image> &p_icon, Vector<uint8_t> &p_data) { @@ -199,7 +223,6 @@ void EditorExportPlatformOSX::_fix_plist(const Ref<EditorExportPreset> &p_preset } } -#ifdef OSX_ENABLED /** If we're running the OSX version of the Godot editor we'll: - export our application bundle to a temporary folder @@ -209,6 +232,7 @@ void EditorExportPlatformOSX::_fix_plist(const Ref<EditorExportPreset> &p_preset 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"); @@ -218,14 +242,25 @@ Error EditorExportPlatformOSX::_code_sign(const Ref<EditorExportPreset> &p_prese 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); + + String str; + Error err = OS::get_singleton()->execute("codesign", args, true, NULL, &str, NULL, true); + ERR_FAIL_COND_V(err != OK, err); + + print_line("codesign: " + str); + if (str.find("no identity found") != -1) { + EditorNode::add_io_error("codesign: no identity found"); + return FAILED; + } 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; + + OS::get_singleton()->move_to_trash(p_dmg_path); + args.push_back("create"); args.push_back(p_dmg_path); args.push_back("-volname"); @@ -234,8 +269,20 @@ Error EditorExportPlatformOSX::_create_dmg(const String &p_dmg_path, const Strin 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); + + String str; + Error err = OS::get_singleton()->execute("hdiutil", args, true, NULL, &str, NULL, true); + ERR_FAIL_COND_V(err != OK, err); + + print_line("hdiutil returned: " + str); + if (str.find("create failed") != -1) { + if (str.find("File exists") != -1) { + EditorNode::add_io_error("hdiutil: create failed - file exists"); + } else { + EditorNode::add_io_error("hdiutil: create failed"); + } + return FAILED; + } return OK; } @@ -288,28 +335,45 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p 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) + Error err = OK; + String tmp_app_path_name = ""; + zlib_filefunc_def io2 = io; + FileAccess *dst_f = NULL; + io2.opaque = &dst_f; + zipFile dst_pkg_zip = NULL; + + if (use_dmg()) { + // We're on OSX so we can export to DMG, but first we create our application bundle + tmp_app_path_name = EditorSettings::get_singleton()->get_settings_path() + "/tmp/" + pkg_name + ".app"; + print_line("Exporting to " + tmp_app_path_name); + DirAccess *tmp_app_path = DirAccess::create_for_path(tmp_app_path_name); + if (!tmp_app_path) { + err = 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? + if (err == OK) { + print_line("Creating " + tmp_app_path_name + "/Contents/MacOS"); + err = tmp_app_path->make_dir_recursive(tmp_app_path_name + "/Contents/MacOS"); + } - // 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) + if (err == OK) { + print_line("Creating " + tmp_app_path_name + "/Contents/Resources"); + err = tmp_app_path->make_dir_recursive(tmp_app_path_name + "/Contents/Resources"); + } + } else { + // Open our destination zip file + dst_pkg_zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, NULL, &io2); + if (!dst_pkg_zip) { + err = ERR_CANT_CREATE; + } + } - /* Now process our template */ + // Now process our template bool found_binary = false; int total_size = 0; - while (ret == UNZ_OK) { + while (ret == UNZ_OK && err == OK) { bool is_execute = false; //get filename @@ -371,287 +435,152 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p 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); + if (use_dmg()) { + // 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); + if (f) { + f->store_buffer(data.ptr(), data.size()); + f->close(); + if (is_execute) { + // Chmod with 0755 if the file is executable + f->_chmod(file, 0755); + } + memdelete(f); + } else { + err = ERR_CANT_CREATE; + } } else { - // seems to already be set correctly - // chmod(file.utf8().get_data(), 0644); + // 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; + fi.tmz_date.tm_sec = info.tmu_date.tm_sec; + fi.tmz_date.tm_mon = info.tmu_date.tm_mon; + fi.tmz_date.tm_mday = info.tmu_date.tm_mday; + fi.tmz_date.tm_year = info.tmu_date.tm_year; + fi.dosDate = info.dosDate; + fi.internal_fa = info.internal_fa; + fi.external_fa = info.external_fa; + + int zerr = zipOpenNewFileInZip(dst_pkg_zip, + file.utf8().get_data(), + &fi, + NULL, + 0, + NULL, + 0, + NULL, + Z_DEFLATED, + Z_DEFAULT_COMPRESSION); + + print_line("OPEN ERR: " + itos(zerr)); + zerr = zipWriteInFileInZip(dst_pkg_zip, data.ptr(), data.size()); + print_line("WRITE ERR: " + itos(zerr)); + zipCloseFileInZip(dst_pkg_zip); } } ret = unzGoToNextFile(src_pkg_zip); } - /* we're done with our source 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 = 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) { + if (err == OK) { + ep.step("Making PKG", 1); - //get filename - unz_file_info info; - char fname[16384]; - ret = unzGetCurrentFileInfo(src_pkg_zip, &info, fname, 16384, NULL, 0, NULL, 0); + if (use_dmg()) { + String pack_path = tmp_app_path_name + "/Contents/Resources/" + pkg_name + ".pck"; + err = save_pack(p_preset, pack_path); - String file = fname; + // see if we can code sign our new package + String identity = p_preset->get("codesign/identity"); + if (err == OK && identity != "") { + ep.step("Code signing bundle", 2); - print_line("READ: " + file); - Vector<uint8_t> data; - data.resize(info.uncompressed_size); + // 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 - //read - unzOpenCurrentFile(src_pkg_zip); - unzReadCurrentFile(src_pkg_zip, data.ptr(), data.size()); - unzCloseCurrentFile(src_pkg_zip); + // start with our application + err = _code_sign(p_preset, tmp_app_path_name + "/Contents/MacOS/" + pkg_name); - //write + ///@TODO we should check the contents of /Contents/Frameworks for frameworks to sign + } - file = file.replace_first("osx_template.app/", ""); + if (err == OK && identity != "") { + // we should probably loop through all resources and sign them? + err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Resources/icon.icns"); + } - if (file == "Contents/Info.plist") { - print_line("parse plist"); - _fix_plist(p_preset, data, pkg_name); - } + if (err == OK && identity != "") { + err = _code_sign(p_preset, pack_path); + } - if (file.begins_with("Contents/MacOS/godot_")) { - if (file != "Contents/MacOS/" + binary_to_use) { - ret = unzGoToNextFile(src_pkg_zip); - continue; //ignore! + if (err == OK && identity != "") { + err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Info.plist"); } - 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); - } + // and finally create a DMG + if (err == OK) { + ep.step("Making DMG", 3); + err = _create_dmg(p_path, pkg_name, tmp_app_path_name); } - //bleh? - } - if (data.size() > 0) { - print_line("ADDING: " + file + " size: " + itos(data.size())); + // Clean up temporary .app dir + OS::get_singleton()->move_to_trash(tmp_app_path_name); + } else { - /* 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; - fi.tmz_date.tm_sec = info.tmu_date.tm_sec; - fi.tmz_date.tm_mon = info.tmu_date.tm_mon; - fi.tmz_date.tm_mday = info.tmu_date.tm_mday; - fi.tmz_date.tm_year = info.tmu_date.tm_year; - fi.dosDate = info.dosDate; - fi.internal_fa = info.internal_fa; - fi.external_fa = info.external_fa; - - int err = zipOpenNewFileInZip(dst_pkg_zip, - file.utf8().get_data(), - &fi, - NULL, - 0, - NULL, - 0, - NULL, - Z_DEFLATED, - Z_DEFAULT_COMPRESSION); - - print_line("OPEN ERR: " + itos(err)); - err = zipWriteInFileInZip(dst_pkg_zip, data.ptr(), data.size()); - print_line("WRITE ERR: " + itos(err)); - zipCloseFileInZip(dst_pkg_zip); + String pack_path = EditorSettings::get_singleton()->get_settings_path() + "/tmp/" + pkg_name + ".pck"; + Error err = save_pack(p_preset, pack_path); + + if (err == OK) { + zipOpenNewFileInZip(dst_pkg_zip, + (pkg_name + ".app/Contents/Resources/" + pkg_name + ".pck").utf8().get_data(), + NULL, + NULL, + 0, + NULL, + 0, + NULL, + Z_DEFLATED, + Z_DEFAULT_COMPRESSION); + + FileAccess *pf = FileAccess::open(pack_path, FileAccess::READ); + if (pf) { + const int BSIZE = 16384; + uint8_t buf[BSIZE]; + + while (true) { + + int r = pf->get_buffer(buf, BSIZE); + if (r <= 0) + break; + zipWriteInFileInZip(dst_pkg_zip, buf, r); + } + zipCloseFileInZip(dst_pkg_zip); + memdelete(pf); + } else { + err = ERR_CANT_OPEN; + } + } } - - 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(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/" + pkg_name + ".pck"; - Error err = save_pack(p_preset, pack_path); - - if (err) { + if (dst_pkg_zip) { zipClose(dst_pkg_zip, NULL); - unzClose(src_pkg_zip); - return err; } - { - //write datapack - - zipOpenNewFileInZip(dst_pkg_zip, - (pkg_name + ".app/Contents/Resources/" + pkg_name + ".pck").utf8().get_data(), - NULL, - NULL, - 0, - NULL, - 0, - NULL, - Z_DEFLATED, - Z_DEFAULT_COMPRESSION); - - FileAccess *pf = FileAccess::open(pack_path, FileAccess::READ); - ERR_FAIL_COND_V(!pf, ERR_CANT_OPEN); - const int BSIZE = 16384; - uint8_t buf[BSIZE]; - - while (true) { - - int r = pf->get_buffer(buf, BSIZE); - if (r <= 0) - break; - zipWriteInFileInZip(dst_pkg_zip, buf, r); - } - zipCloseFileInZip(dst_pkg_zip); - memdelete(pf); - } - - 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/godot_main_osx.mm b/platform/osx/godot_main_osx.mm index 83d782cc2f..e2740fc666 100644 --- a/platform/osx/godot_main_osx.mm +++ b/platform/osx/godot_main_osx.mm @@ -35,7 +35,6 @@ #include <unistd.h> int main(int argc, char **argv) { - int first_arg = 1; const char *dbg_arg = "-NSDocumentRevisionsDebugMode"; printf("arguments\n"); @@ -74,6 +73,13 @@ int main(int argc, char **argv) { } } +#ifdef DEBUG_ENABLED + // lets report the path we made current after all that + char cwd[4096]; + getcwd(cwd, 4096); + printf("Current path: %s\n", cwd); +#endif + OS_OSX os; Error err = Main::setup(argv[0], argc - first_arg, &argv[first_arg]); diff --git a/platform/osx/joypad_osx.h b/platform/osx/joypad_osx.h index e271f4b947..4bdef8eb83 100644 --- a/platform/osx/joypad_osx.h +++ b/platform/osx/joypad_osx.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* joypad_osx.h */ +/* joypad_osx.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h index 5bfed1ee50..420bb50af9 100644 --- a/platform/osx/os_osx.h +++ b/platform/osx/os_osx.h @@ -30,13 +30,12 @@ #ifndef OS_OSX_H #define OS_OSX_H -#include "drivers/alsa/audio_driver_alsa.h" -#include "drivers/rtaudio/audio_driver_rtaudio.h" +#include "crash_handler_osx.h" +#include "drivers/coreaudio/audio_driver_coreaudio.h" #include "drivers/unix/os_unix.h" #include "joypad_osx.h" #include "main/input_default.h" #include "os/input.h" -#include "platform/osx/audio_driver_osx.h" #include "power_osx.h" #include "servers/audio_server.h" #include "servers/physics_2d/physics_2d_server_sw.h" @@ -68,7 +67,7 @@ public: IP_Unix *ip_unix; - AudioDriverOSX audio_driver_osx; + AudioDriverCoreAudio audio_driver; InputDefault *input; JoypadOSX *joypad_osx; @@ -110,22 +109,26 @@ public: power_osx *power_manager; + CrashHandler crash_handler; + float _mouse_scale(float p_scale) { - if (display_scale > 1.0) + if (_display_scale() > 1.0) return p_scale; else return 1.0; } - void _update_window(); + float _display_scale() const; + float _display_scale(id screen) const; - float display_scale; + void _update_window(); protected: virtual int get_video_driver_count() const; virtual const char *get_video_driver_name(int p_driver) const; virtual VideoMode get_default_video_mode() const; + virtual void initialize_logger(); virtual void initialize_core(); virtual void initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver); virtual void finalize(); @@ -140,8 +143,6 @@ public: virtual String get_name(); - virtual void print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type = ERR_ERROR); - virtual void alert(const String &p_alert, const String &p_title = "ALERT!"); virtual void set_cursor_shape(CursorShape p_shape); @@ -149,7 +150,7 @@ public: virtual void set_mouse_show(bool p_show); virtual void set_mouse_grab(bool p_grab); virtual bool is_mouse_grab_enabled() const; - virtual void warp_mouse_pos(const Point2 &p_to); + virtual void warp_mouse_position(const Point2 &p_to); virtual Point2 get_mouse_position() const; virtual int get_mouse_button_state() const; virtual void set_window_title(const String &p_title); @@ -160,6 +161,8 @@ public: virtual MainLoop *get_main_loop() const; + virtual String get_system_dir(SystemDir p_dir) const; + virtual bool can_draw() const; virtual void set_clipboard(const String &p_text); @@ -210,17 +213,25 @@ public: virtual void set_ime_position(const Point2 &p_pos); virtual void set_ime_intermediate_text_callback(ImeCallback p_callback, void *p_inp); - virtual PowerState get_power_state(); + virtual OS::PowerState get_power_state(); virtual int get_power_seconds_left(); virtual int get_power_percent_left(); virtual bool _check_internal_feature_support(const String &p_feature); + virtual void set_use_vsync(bool p_enable); + virtual bool is_vsync_enabled() const; + void run(); void set_mouse_mode(MouseMode p_mode); MouseMode get_mouse_mode() const; + void disable_crash_handler(); + bool is_disable_crash_handler() const; + + virtual Error move_to_trash(const String &p_path); + OS_OSX(); }; diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index f1260bc088..33586086dc 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -180,13 +180,13 @@ static bool mouse_down_control = false; if (newBackingScaleFactor != oldBackingScaleFactor) { //Set new display scale and window size - OS_OSX::singleton->display_scale = newBackingScaleFactor; + float newDisplayScale = OS_OSX::singleton->is_hidpi_allowed() ? newBackingScaleFactor : 1.0; 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; + OS_OSX::singleton->window_size.width = fbRect.size.width * newDisplayScale; + OS_OSX::singleton->window_size.height = fbRect.size.height * newDisplayScale; //Update context if (OS_OSX::singleton->main_loop) { @@ -206,8 +206,9 @@ static bool mouse_down_control = false; 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; + float displayScale = OS_OSX::singleton->_display_scale(); + OS_OSX::singleton->window_size.width = fbRect.size.width * displayScale; + OS_OSX::singleton->window_size.height = fbRect.size.height * displayScale; if (OS_OSX::singleton->main_loop) { Main::force_redraw(); @@ -352,7 +353,8 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; - (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange { const NSRect contentRect = [OS_OSX::singleton->window_view frame]; - NSRect pointInWindowRect = NSMakeRect(OS_OSX::singleton->im_position.x / OS_OSX::singleton->display_scale, contentRect.size.height - (OS_OSX::singleton->im_position.y / OS_OSX::singleton->display_scale) - 1, 0, 0); + float displayScale = OS_OSX::singleton->_display_scale(); + NSRect pointInWindowRect = NSMakeRect(OS_OSX::singleton->im_position.x / displayScale, contentRect.size.height - (OS_OSX::singleton->im_position.y / displayScale) - 1, 0, 0); NSPoint pointOnScreen = [[OS_OSX::singleton->window_view window] convertRectToScreen:pointInWindowRect].origin; return NSMakeRect(pointOnScreen.x, pointOnScreen.y, 0, 0); @@ -910,6 +912,8 @@ OS::VideoMode OS_OSX::get_default_video_mode() const { void OS_OSX::initialize_core() { + crash_handler.initialize(); + OS_Unix::initialize_core(); DirAccess::make_default<DirAccessOSX>(DirAccess::ACCESS_RESOURCES); @@ -938,15 +942,6 @@ void OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_au kTISNotifySelectedKeyboardInputSourceChanged, NULL, CFNotificationSuspensionBehaviorDeliverImmediately); - if (is_hidpi_allowed() && [[NSScreen mainScreen] respondsToSelector:@selector(backingScaleFactor)]) { - for (NSScreen *screen in [NSScreen screens]) { - float s = [screen backingScaleFactor]; - if (s > display_scale) { - display_scale = s; - } - } - } - window_delegate = [[GodotWindowDelegate alloc] init]; // Don't use accumulation buffer support; it's not accelerated @@ -970,10 +965,19 @@ void OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_au window_view = [[GodotContentView alloc] init]; - window_size.width = p_desired.width * display_scale; - window_size.height = p_desired.height * display_scale; + float displayScale = 1.0; + if (is_hidpi_allowed()) { + // note that mainScreen is not screen #0 but the one with the keyboard focus. + NSScreen *screen = [NSScreen mainScreen]; + if ([screen respondsToSelector:@selector(backingScaleFactor)]) { + displayScale = fmax(displayScale, [screen backingScaleFactor]); + } + } + + window_size.width = p_desired.width * displayScale; + window_size.height = p_desired.height * displayScale; - if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6 && display_scale > 1) { + if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6 && displayScale > 1.0) { [window_view setWantsBestResolutionOpenGLSurface:YES]; //if (current_videomode.resizable) [window_object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; @@ -1069,7 +1073,7 @@ void OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_au bool use_gl2 = p_video_driver != 1; - AudioDriverManager::add_driver(&audio_driver_osx); + AudioDriverManager::add_driver(&audio_driver); // only opengl support here... RasterizerGLES3::register_config(); @@ -1086,12 +1090,7 @@ void OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_au visual_server->init(); // visual_server->cursor_set_visible(false, 0); - AudioDriverManager::get_driver(p_audio_driver)->set_singleton(); - - if (AudioDriverManager::get_driver(p_audio_driver)->init() != OK) { - - ERR_PRINT("Initializing audio failed."); - } + AudioDriverManager::initialize(p_audio_driver); // physics_server = memnew(PhysicsServerSW); @@ -1148,43 +1147,69 @@ String OS_OSX::get_name() { return "OSX"; } -void OS_OSX::print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type) { - #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200 - if (!_print_error_enabled) - return; - - const char *err_details; - if (p_rationale && p_rationale[0]) - err_details = p_rationale; - else - err_details = p_code; +class OSXTerminalLogger : public StdLogger { +public: + virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type = ERR_ERROR) { + if (!should_log(true)) { + return; + } - switch (p_type) { - case ERR_ERROR: - os_log_error(OS_LOG_DEFAULT, "ERROR: %{public}s: %{public}s\nAt: %{public}s:%i.", p_function, err_details, p_file, p_line); - print("\E[1;31mERROR: %s: \E[0m\E[1m%s\n", p_function, err_details); - print("\E[0;31m At: %s:%i.\E[0m\n", p_file, p_line); - break; - case ERR_WARNING: - os_log_info(OS_LOG_DEFAULT, "WARNING: %{public}s: %{public}s\nAt: %{public}s:%i.", p_function, err_details, p_file, p_line); - print("\E[1;33mWARNING: %s: \E[0m\E[1m%s\n", p_function, err_details); - print("\E[0;33m At: %s:%i.\E[0m\n", p_file, p_line); - break; - case ERR_SCRIPT: - os_log_error(OS_LOG_DEFAULT, "SCRIPT ERROR: %{public}s: %{public}s\nAt: %{public}s:%i.", p_function, err_details, p_file, p_line); - print("\E[1;35mSCRIPT ERROR: %s: \E[0m\E[1m%s\n", p_function, err_details); - print("\E[0;35m At: %s:%i.\E[0m\n", p_file, p_line); - break; - case ERR_SHADER: - os_log_error(OS_LOG_DEFAULT, "SHADER ERROR: %{public}s: %{public}s\nAt: %{public}s:%i.", p_function, err_details, p_file, p_line); - print("\E[1;36mSHADER ERROR: %s: \E[0m\E[1m%s\n", p_function, err_details); - print("\E[0;36m At: %s:%i.\E[0m\n", p_file, p_line); - break; + const char *err_details; + if (p_rationale && p_rationale[0]) + err_details = p_rationale; + else + err_details = p_code; + + switch (p_type) { + case ERR_WARNING: + os_log_info(OS_LOG_DEFAULT, + "WARNING: %{public}s: %{public}s\nAt: %{public}s:%i.", + p_function, err_details, p_file, p_line); + logf_error("\E[1;33mWARNING: %s: \E[0m\E[1m%s\n", p_function, + err_details); + logf_error("\E[0;33m At: %s:%i.\E[0m\n", p_file, p_line); + break; + case ERR_SCRIPT: + os_log_error(OS_LOG_DEFAULT, + "SCRIPT ERROR: %{public}s: %{public}s\nAt: %{public}s:%i.", + p_function, err_details, p_file, p_line); + logf_error("\E[1;35mSCRIPT ERROR: %s: \E[0m\E[1m%s\n", p_function, + err_details); + logf_error("\E[0;35m At: %s:%i.\E[0m\n", p_file, p_line); + break; + case ERR_SHADER: + os_log_error(OS_LOG_DEFAULT, + "SHADER ERROR: %{public}s: %{public}s\nAt: %{public}s:%i.", + p_function, err_details, p_file, p_line); + logf_error("\E[1;36mSHADER ERROR: %s: \E[0m\E[1m%s\n", p_function, + err_details); + logf_error("\E[0;36m At: %s:%i.\E[0m\n", p_file, p_line); + break; + case ERR_ERROR: + default: + os_log_error(OS_LOG_DEFAULT, + "ERROR: %{public}s: %{public}s\nAt: %{public}s:%i.", + p_function, err_details, p_file, p_line); + logf_error("\E[1;31mERROR: %s: \E[0m\E[1m%s\n", p_function, err_details); + logf_error("\E[0;31m At: %s:%i.\E[0m\n", p_file, p_line); + break; + } } +}; + #else - OS_Unix::print_error(p_function, p_file, p_line, p_code, p_rationale, p_type); + +typedef UnixTerminalLogger OSXTerminalLogger; #endif + +void OS_OSX::initialize_logger() { + Vector<Logger *> loggers; + loggers.push_back(memnew(OSXTerminalLogger)); + // FIXME: Reenable once we figure out how to get this properly in user:// + // instead of littering the user's working dirs (res:// + pwd) with log files (GH-12277) + //loggers.push_back(memnew(RotatedFileLogger("user://logs/log.txt"))); + _set_logger(memnew(CompositeLogger(loggers))); } void OS_OSX::alert(const String &p_alert, const String &p_title) { @@ -1243,7 +1268,7 @@ bool OS_OSX::is_mouse_grab_enabled() const { return mouse_grab; } -void OS_OSX::warp_mouse_pos(const Point2 &p_to) { +void OS_OSX::warp_mouse_position(const Point2 &p_to) { //copied from windows impl with osx native calls if (mouse_mode == MOUSE_MODE_CAPTURED) { @@ -1253,7 +1278,8 @@ void OS_OSX::warp_mouse_pos(const Point2 &p_to) { //local point in window coords const NSRect contentRect = [window_view frame]; - NSRect pointInWindowRect = NSMakeRect(p_to.x / display_scale, contentRect.size.height - (p_to.y / display_scale) - 1, 0, 0); + float displayScale = _display_scale(); + NSRect pointInWindowRect = NSMakeRect(p_to.x / displayScale, contentRect.size.height - (p_to.y / displayScale) - 1, 0, 0); NSPoint pointOnScreen = [[window_view window] convertRectToScreen:pointInWindowRect].origin; //point in scren coords @@ -1327,6 +1353,46 @@ MainLoop *OS_OSX::get_main_loop() const { return main_loop; } +String OS_OSX::get_system_dir(SystemDir p_dir) const { + + NSSearchPathDirectory id = 0; + + switch (p_dir) { + case SYSTEM_DIR_DESKTOP: { + id = NSDesktopDirectory; + } break; + case SYSTEM_DIR_DOCUMENTS: { + id = NSDocumentDirectory; + } break; + case SYSTEM_DIR_DOWNLOADS: { + id = NSDownloadsDirectory; + } break; + case SYSTEM_DIR_MOVIES: { + id = NSMoviesDirectory; + } break; + case SYSTEM_DIR_MUSIC: { + id = NSMusicDirectory; + } break; + case SYSTEM_DIR_PICTURES: { + id = NSPicturesDirectory; + } break; + } + + String ret; + if (id) { + + NSArray *paths = NSSearchPathForDirectoriesInDomains(id, NSUserDomainMask, YES); + if (paths && [paths count] >= 1) { + + char *utfs = strdup([[paths firstObject] UTF8String]); + ret.parse_utf8(utfs); + free(utfs); + } + } + + return ret; +} + bool OS_OSX::can_draw() const { return true; @@ -1414,17 +1480,17 @@ int OS_OSX::get_screen_count() const { return [screenArray count]; }; +static int get_screen_index(NSScreen *screen) { + const NSUInteger index = [[NSScreen screens] indexOfObject:screen]; + return index == NSNotFound ? 0 : index; +} + int OS_OSX::get_current_screen() const { - 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; + if (window_object) { + return get_screen_index([window_object screen]); + } else { + return get_screen_index([NSScreen mainScreen]); } - return 0; }; void OS_OSX::set_current_screen(int p_screen) { @@ -1439,12 +1505,7 @@ Point2 OS_OSX::get_screen_position(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]; - } - + float displayScale = _display_scale([screenArray objectAtIndex:p_screen]); NSRect nsrect = [[screenArray objectAtIndex:p_screen] frame]; return Point2(nsrect.origin.x, nsrect.origin.y) * displayScale; } @@ -1459,12 +1520,7 @@ 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]; - } - + float displayScale = _display_scale([screenArray objectAtIndex:p_screen]); NSDictionary *description = [[screenArray objectAtIndex:p_screen] deviceDescription]; NSSize displayPixelSize = [[description objectForKey:NSDeviceSize] sizeValue]; CGSize displayPhysicalSize = CGDisplayScreenSize( @@ -1483,12 +1539,7 @@ Size2 OS_OSX::get_screen_size(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]; - } - + float displayScale = _display_scale([screenArray objectAtIndex:p_screen]); // 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; @@ -1522,10 +1573,28 @@ void OS_OSX::_update_window() { } } +float OS_OSX::_display_scale() const { + if (window_object) { + return _display_scale([window_object screen]); + } else { + return _display_scale([NSScreen mainScreen]); + } +} + +float OS_OSX::_display_scale(id screen) const { + if (is_hidpi_allowed()) { + if ([screen respondsToSelector:@selector(backingScaleFactor)]) { + return fmax(1.0, [screen backingScaleFactor]); + } + } else { + return 1.0; + } +} + Point2 OS_OSX::get_window_position() const { Size2 wp([window_object frame].origin.x, [window_object frame].origin.y); - wp *= display_scale; + wp *= _display_scale(); return wp; }; @@ -1533,10 +1602,11 @@ void OS_OSX::set_window_position(const Point2 &p_position) { Size2 scr = get_screen_size(); NSPoint pos; + float displayScale = _display_scale(); - pos.x = p_position.x / display_scale; + pos.x = p_position.x / displayScale; // For OS X the y starts at the bottom - pos.y = (scr.height - p_position.y) / display_scale; + pos.y = (scr.height - p_position.y) / displayScale; [window_object setFrameTopLeftPoint:pos]; @@ -1764,6 +1834,8 @@ OS::LatinKeyboardVariant OS_OSX::get_latin_keyboard_variant() const { layout = LATIN_KEYBOARD_DVORAK; } else if ([test isEqualToString:@"xvlcwk"]) { layout = LATIN_KEYBOARD_NEO; + } else if ([test isEqualToString:@"qwfpgj"]) { + layout = LATIN_KEYBOARD_COLEMAK; } [test release]; @@ -1861,7 +1933,7 @@ String OS_OSX::get_joy_guid(int p_device) const { return input->get_joy_guid_remapped(p_device); } -PowerState OS_OSX::get_power_state() { +OS::PowerState OS_OSX::get_power_state() { return power_manager->get_power_state(); } @@ -1873,6 +1945,36 @@ int OS_OSX::get_power_percent_left() { return power_manager->get_power_percent_left(); } +Error OS_OSX::move_to_trash(const String &p_path) { + NSFileManager *fm = [NSFileManager defaultManager]; + NSURL *url = [NSURL fileURLWithPath:@(p_path.utf8().get_data())]; + NSError *err; + + if (![fm trashItemAtURL:url resultingItemURL:nil error:&err]) { + ERR_PRINTS("trashItemAtURL error: " + String(err.localizedDescription.UTF8String)); + return FAILED; + } + + return OK; +} + +void OS_OSX::set_use_vsync(bool p_enable) { + CGLContextObj ctx = CGLGetCurrentContext(); + if (ctx) { + GLint swapInterval = p_enable ? 1 : 0; + CGLSetParameter(ctx, kCGLCPSwapInterval, &swapInterval); + } +} + +bool OS_OSX::is_vsync_enabled() const { + GLint swapInterval = 0; + CGLContextObj ctx = CGLGetCurrentContext(); + if (ctx) { + CGLGetParameter(ctx, kCGLCPSwapInterval, &swapInterval); + } + return swapInterval ? true : false; +} + OS_OSX *OS_OSX::singleton = NULL; OS_OSX::OS_OSX() { @@ -1965,9 +2067,18 @@ OS_OSX::OS_OSX() { minimized = false; window_size = Vector2(1024, 600); zoomed = false; - display_scale = 1.0; + + _set_logger(memnew(OSXTerminalLogger)); } bool OS_OSX::_check_internal_feature_support(const String &p_feature) { return p_feature == "pc" || p_feature == "s3tc"; } + +void OS_OSX::disable_crash_handler() { + crash_handler.disable(); +} + +bool OS_OSX::is_disable_crash_handler() const { + return crash_handler.is_disabled(); +} diff --git a/platform/osx/power_osx.cpp b/platform/osx/power_osx.cpp index 24591e48b1..eed03e63c1 100644 --- a/platform/osx/power_osx.cpp +++ b/platform/osx/power_osx.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* power_osx.cpp */ +/* power_osx.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -174,7 +174,7 @@ bool power_osx::GetPowerInfo_MacOSX() { nsecs_left = -1; percent_left = -1; - power_state = POWERSTATE_UNKNOWN; + power_state = OS::POWERSTATE_UNKNOWN; if (blob != NULL) { CFArrayRef list = IOPSCopyPowerSourcesList(blob); @@ -194,13 +194,13 @@ bool power_osx::GetPowerInfo_MacOSX() { } if (!have_battery) { - power_state = POWERSTATE_NO_BATTERY; + power_state = OS::POWERSTATE_NO_BATTERY; } else if (charging) { - power_state = POWERSTATE_CHARGING; + power_state = OS::POWERSTATE_CHARGING; } else if (have_ac) { - power_state = POWERSTATE_CHARGED; + power_state = OS::POWERSTATE_CHARGED; } else { - power_state = POWERSTATE_ON_BATTERY; + power_state = OS::POWERSTATE_ON_BATTERY; } CFRelease(list); @@ -218,11 +218,11 @@ bool power_osx::UpdatePowerInfo() { return false; } -PowerState power_osx::get_power_state() { +OS::PowerState power_osx::get_power_state() { if (UpdatePowerInfo()) { return power_state; } else { - return POWERSTATE_UNKNOWN; + return OS::POWERSTATE_UNKNOWN; } } @@ -243,7 +243,7 @@ int power_osx::get_power_percent_left() { } power_osx::power_osx() - : nsecs_left(-1), percent_left(-1), power_state(POWERSTATE_UNKNOWN) { + : nsecs_left(-1), percent_left(-1), power_state(OS::POWERSTATE_UNKNOWN) { } power_osx::~power_osx() { diff --git a/platform/osx/power_osx.h b/platform/osx/power_osx.h index 692c850d7c..20e47e9cd9 100644 --- a/platform/osx/power_osx.h +++ b/platform/osx/power_osx.h @@ -33,7 +33,7 @@ #include "dir_access_osx.h" #include "os/file_access.h" -#include "os/power.h" +#include "os/os.h" #include <CoreFoundation/CoreFoundation.h> class power_osx { @@ -41,7 +41,7 @@ class power_osx { private: int nsecs_left; int percent_left; - PowerState power_state; + OS::PowerState power_state; void checkps(CFDictionaryRef dict, bool *have_ac, bool *have_battery, bool *charging); bool GetPowerInfo_MacOSX(/*PowerState * state, int *seconds, int *percent*/); bool UpdatePowerInfo(); @@ -50,7 +50,7 @@ public: power_osx(); virtual ~power_osx(); - PowerState get_power_state(); + OS::PowerState get_power_state(); int get_power_seconds_left(); int get_power_percent_left(); }; diff --git a/platform/server/detect.py b/platform/server/detect.py index 2bb4b59e94..ffec2af933 100644 --- a/platform/server/detect.py +++ b/platform/server/detect.py @@ -12,6 +12,9 @@ def get_name(): def can_build(): + # Doesn't build against Godot 3.0 for now, disable to avoid confusing users + return False + if (os.name != "posix" or sys.platform == "darwin"): return False @@ -19,9 +22,9 @@ def can_build(): def get_opts(): - + from SCons.Variables import BoolVariable return [ - ('use_llvm', 'Use the LLVM compiler', 'no'), + BoolVariable('use_llvm', 'Use the LLVM compiler', False), ] @@ -52,7 +55,7 @@ def configure(env): ## Compiler configuration - if (env["use_llvm"] == "yes"): + if env['use_llvm']: if ('clang++' not in env['CXX']): env["CC"] = "clang" env["CXX"] = "clang++" @@ -64,66 +67,60 @@ def configure(env): # FIXME: Check for existence of the libs before parsing their flags with pkg-config - if (env['builtin_openssl'] == 'no'): - # Currently not compatible with OpenSSL 1.1.0+ - # https://github.com/godotengine/godot/issues/8624 - import subprocess - openssl_version = subprocess.check_output(['pkg-config', 'openssl', '--modversion']).strip('\n') - if (openssl_version >= "1.1.0"): - print("Error: Found system-installed OpenSSL %s, currently only supporting version 1.0.x." % openssl_version) - print("Aborting.. You can compile with 'builtin_openssl=yes' to use the bundled version.\n") - sys.exit(255) - + if not env['builtin_openssl']: env.ParseConfig('pkg-config openssl --cflags --libs') - if (env['builtin_libwebp'] == 'no'): + if not env['builtin_libwebp']: env.ParseConfig('pkg-config libwebp --cflags --libs') # freetype depends on libpng and zlib, so bundling one of them while keeping others # as shared libraries leads to weird issues - if (env['builtin_freetype'] == 'yes' or env['builtin_libpng'] == 'yes' or env['builtin_zlib'] == 'yes'): - env['builtin_freetype'] = 'yes' - env['builtin_libpng'] = 'yes' - env['builtin_zlib'] = 'yes' + if env['builtin_freetype'] or env['builtin_libpng'] or env['builtin_zlib']: + env['builtin_freetype'] = True + env['builtin_libpng'] = True + env['builtin_zlib'] = True - if (env['builtin_freetype'] == 'no'): + if not env['builtin_freetype']: env.ParseConfig('pkg-config freetype2 --cflags --libs') - if (env['builtin_libpng'] == 'no'): + if not env['builtin_libpng']: env.ParseConfig('pkg-config libpng --cflags --libs') - if (env['builtin_enet'] == 'no'): + if not env['builtin_enet']: env.ParseConfig('pkg-config libenet --cflags --libs') - if (env['builtin_squish'] == 'no' and env["tools"] == "yes"): + if not env['builtin_squish'] and env['tools']: env.ParseConfig('pkg-config libsquish --cflags --libs') + if not env['builtin_zstd']: + env.ParseConfig('pkg-config libzstd --cflags --libs') + # Sound and video libraries # Keep the order as it triggers chained dependencies (ogg needed by others, etc.) - if (env['builtin_libtheora'] == 'no'): - env['builtin_libogg'] = 'no' # Needed to link against system libtheora - env['builtin_libvorbis'] = 'no' # Needed to link against system libtheora + if not env['builtin_libtheora']: + env['builtin_libogg'] = False # Needed to link against system libtheora + env['builtin_libvorbis'] = False # Needed to link against system libtheora env.ParseConfig('pkg-config theora theoradec --cflags --libs') - if (env['builtin_libvpx'] == 'no'): + if not env['builtin_libvpx']: env.ParseConfig('pkg-config vpx --cflags --libs') - if (env['builtin_libvorbis'] == 'no'): - env['builtin_libogg'] = 'no' # Needed to link against system libvorbis + if not env['builtin_libvorbis']: + env['builtin_libogg'] = False # Needed to link against system libvorbis env.ParseConfig('pkg-config vorbis vorbisfile --cflags --libs') - if (env['builtin_opus'] == 'no'): - env['builtin_libogg'] = 'no' # Needed to link against system opus + if not env['builtin_opus']: + env['builtin_libogg'] = False # Needed to link against system opus env.ParseConfig('pkg-config opus opusfile --cflags --libs') - if (env['builtin_libogg'] == 'no'): + if not env['builtin_libogg']: env.ParseConfig('pkg-config ogg --cflags --libs') ## Flags # Linkflags below this line should typically stay the last ones - if (env['builtin_zlib'] == 'no'): + if not env['builtin_zlib']: env.ParseConfig('pkg-config zlib --cflags --libs') env.Append(CPPPATH=['#platform/server']) diff --git a/platform/server/os_server.cpp b/platform/server/os_server.cpp index 44034e815d..300c5cffcc 100644 --- a/platform/server/os_server.cpp +++ b/platform/server/os_server.cpp @@ -62,12 +62,7 @@ void OS_Server::initialize(const VideoMode &p_desired, int p_video_driver, int p //visual_server = memnew( VisualServerRaster(rasterizer) ); - AudioDriverManager::get_driver(p_audio_driver)->set_singleton(); - - if (AudioDriverManager::get_driver(p_audio_driver)->init() != OK) { - - ERR_PRINT("Initializing audio failed."); - } + AudioDriverManager::initialize(p_audio_driver); sample_manager = memnew(SampleManagerMallocSW); audio_server = memnew(AudioServerSW(sample_manager)); @@ -200,7 +195,7 @@ void OS_Server::move_window_to_foreground() { void OS_Server::set_cursor_shape(CursorShape p_shape) { } -PowerState OS_Server::get_power_state() { +OS::PowerState OS_Server::get_power_state() { return power_manager->get_power_state(); } @@ -232,7 +227,6 @@ void OS_Server::run() { OS_Server::OS_Server() { - AudioDriverManager::add_driver(&driver_dummy); //adriver here grab = false; }; diff --git a/platform/server/os_server.h b/platform/server/os_server.h index f3db053be3..ba12f649be 100644 --- a/platform/server/os_server.h +++ b/platform/server/os_server.h @@ -34,7 +34,6 @@ #include "drivers/rtaudio/audio_driver_rtaudio.h" #include "drivers/unix/os_unix.h" #include "main/input_default.h" -#include "servers/audio/audio_driver_dummy.h" #include "servers/audio_server.h" #include "servers/physics_2d/physics_2d_server_sw.h" #include "servers/physics_server.h" @@ -55,7 +54,6 @@ class OS_Server : public OS_Unix { List<String> args; MainLoop *main_loop; - AudioDriverDummy driver_dummy; bool grab; PhysicsServer *physics_server; @@ -106,7 +104,7 @@ public: void run(); - virtual PowerState get_power_state(); + virtual OS::PowerState get_power_state(); virtual int get_power_seconds_left(); virtual int get_power_percent_left(); diff --git a/platform/uwp/SCsub b/platform/uwp/SCsub index 7ee5aa2ac3..bbd329a7e5 100644 --- a/platform/uwp/SCsub +++ b/platform/uwp/SCsub @@ -8,6 +8,7 @@ files = [ '#platform/windows/packet_peer_udp_winsock.cpp', '#platform/windows/stream_peer_winsock.cpp', '#platform/windows/key_mapping_win.cpp', + '#platform/windows/windows_terminal_logger.cpp', 'joypad_uwp.cpp', 'power_uwp.cpp', 'gl_context_egl.cpp', diff --git a/platform/uwp/detect.py b/platform/uwp/detect.py index 23929dd804..af53f97446 100644 --- a/platform/uwp/detect.py +++ b/platform/uwp/detect.py @@ -33,8 +33,8 @@ def get_opts(): def get_flags(): return [ - ('tools', 'no'), - ('xaudio2', 'yes'), + ('tools', False), + ('xaudio2', True), ] diff --git a/platform/uwp/export/export.cpp b/platform/uwp/export/export.cpp index c129743507..d66bcaa91c 100644 --- a/platform/uwp/export/export.cpp +++ b/platform/uwp/export/export.cpp @@ -466,14 +466,12 @@ void AppxPackager::add_file(String p_file_name, const uint8_t *p_buffer, size_t EditorNode::progress_task_step(progress_task, "File: " + p_file_name, (p_file_no * 100) / p_total_files); } - bool do_hash = p_file_name != "AppxSignature.p7x"; - FileMeta meta; meta.name = p_file_name; meta.uncompressed_size = p_len; meta.compressed_size = p_len; meta.compressed = p_compress; - meta.zip_offset = package->get_pos(); + meta.zip_offset = package->get_position(); Vector<uint8_t> file_buffer; @@ -501,7 +499,7 @@ void AppxPackager::add_file(String p_file_name, const uint8_t *p_buffer, size_t size_t block_size = (p_len - step) > BLOCK_SIZE ? BLOCK_SIZE : (p_len - step); - for (int i = 0; i < block_size; i++) { + for (uint32_t i = 0; i < block_size; i++) { strm_in[i] = p_buffer[step + i]; } @@ -523,14 +521,14 @@ void AppxPackager::add_file(String p_file_name, const uint8_t *p_buffer, size_t //package->store_buffer(strm_out.ptr(), strm.total_out - total_out_before); int start = file_buffer.size(); file_buffer.resize(file_buffer.size() + bh.compressed_size); - for (int i = 0; i < bh.compressed_size; i++) + for (uint32_t i = 0; i < bh.compressed_size; i++) file_buffer[start + i] = strm_out[i]; } else { bh.compressed_size = block_size; //package->store_buffer(strm_in.ptr(), block_size); int start = file_buffer.size(); file_buffer.resize(file_buffer.size() + block_size); - for (int i = 0; i < bh.compressed_size; i++) + for (uint32_t i = 0; i < bh.compressed_size; i++) file_buffer[start + i] = strm_in[i]; } @@ -553,7 +551,7 @@ void AppxPackager::add_file(String p_file_name, const uint8_t *p_buffer, size_t //package->store_buffer(strm_out.ptr(), strm.total_out - total_out_before); int start = file_buffer.size(); file_buffer.resize(file_buffer.size() + (strm.total_out - total_out_before)); - for (int i = 0; i < (strm.total_out - total_out_before); i++) + for (uint32_t i = 0; i < (strm.total_out - total_out_before); i++) file_buffer[start + i] = strm_out[i]; deflateEnd(&strm); @@ -621,11 +619,11 @@ void AppxPackager::finish() { // Write central directory EditorNode::progress_task_step("export", "Finishing package...", 6); - central_dir_offset = package->get_pos(); + central_dir_offset = package->get_position(); package->store_buffer(central_dir_data.ptr(), central_dir_data.size()); // End record - end_of_central_dir_offset = package->get_pos(); + end_of_central_dir_offset = package->get_position(); Vector<uint8_t> end_record = make_end_of_central_record(); package->store_buffer(end_record.ptr(), end_record.size()); @@ -866,7 +864,7 @@ class EditorExportUWP : public EditorExportPlatform { Vector<uint8_t> _get_image_data(const Ref<EditorExportPreset> &p_preset, const String &p_path) { Vector<uint8_t> data; - StreamTexture *image; + StreamTexture *image = NULL; if (p_path.find("StoreLogo") != -1) { image = p_preset->get("images/store_logo").is_zero() ? NULL : Object::cast_to<StreamTexture>(((Object *)p_preset->get("images/store_logo"))); @@ -882,6 +880,8 @@ class EditorExportUWP : public EditorExportPlatform { image = p_preset->get("images/wide310x150_logo").is_zero() ? NULL : Object::cast_to<StreamTexture>(((Object *)p_preset->get("images/wide310x150_logo"))); } else if (p_path.find("SplashScreen") != -1) { image = p_preset->get("images/splash_screen").is_zero() ? NULL : Object::cast_to<StreamTexture>(((Object *)p_preset->get("images/splash_screen"))); + } else { + ERR_PRINT("Unable to load logo"); } if (!image) return data; diff --git a/platform/uwp/gl_context_egl.cpp b/platform/uwp/gl_context_egl.cpp index dd186c97d6..ed3db65cdf 100644 --- a/platform/uwp/gl_context_egl.cpp +++ b/platform/uwp/gl_context_egl.cpp @@ -31,7 +31,7 @@ #include "EGL/eglext.h" -using namespace Platform; +using Platform::Exception; void ContextEGL::release_current() { @@ -103,23 +103,23 @@ Error ContextEGL::initialize() { const EGLint displayAttributes[] = { - /*EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, - EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, 9, - EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE, 3, - EGL_NONE,*/ - // These are the default display attributes, used to request ANGLE's D3D11 renderer. - // eglInitialize will only succeed with these attributes if the hardware supports D3D11 Feature Level 10_0+. - EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, - - // EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER is an optimization that can have large performance benefits on mobile devices. - // Its syntax is subject to change, though. Please update your Visual Studio templates if you experience compilation issues with it. - //EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER, EGL_TRUE, - - // EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE is an option that enables ANGLE to automatically call - // the IDXGIDevice3::Trim method on behalf of the application when it gets suspended. - // Calling IDXGIDevice3::Trim when an application is suspended is a Windows Store application certification requirement. - EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE, EGL_TRUE, - EGL_NONE, + /*EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, + EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, 9, + EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE, 3, + EGL_NONE,*/ + // These are the default display attributes, used to request ANGLE's D3D11 renderer. + // eglInitialize will only succeed with these attributes if the hardware supports D3D11 Feature Level 10_0+. + EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, + + // EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER is an optimization that can have large performance benefits on mobile devices. + // Its syntax is subject to change, though. Please update your Visual Studio templates if you experience compilation issues with it. + //EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER, EGL_TRUE, + + // EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE is an option that enables ANGLE to automatically call + // the IDXGIDevice3::Trim method on behalf of the application when it gets suspended. + // Calling IDXGIDevice3::Trim when an application is suspended is a Windows Store application certification requirement. + EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE, EGL_TRUE, + EGL_NONE, }; PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(eglGetProcAddress("eglGetPlatformDisplayEXT")); diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp index 3a8932aae2..c67e5bae05 100644 --- a/platform/uwp/os_uwp.cpp +++ b/platform/uwp/os_uwp.cpp @@ -40,6 +40,7 @@ #include "platform/windows/packet_peer_udp_winsock.h" #include "platform/windows/stream_peer_winsock.h" #include "platform/windows/tcp_server_winsock.h" +#include "platform/windows/windows_terminal_logger.h" #include "project_settings.h" #include "servers/audio_server.h" #include "servers/visual/visual_server_raster.h" @@ -182,6 +183,15 @@ void OSUWP::initialize_core() { cursor_shape = CURSOR_ARROW; } +void OSUWP::initialize_logger() { + Vector<Logger *> loggers; + loggers.push_back(memnew(WindowsTerminalLogger)); + // FIXME: Reenable once we figure out how to get this properly in user:// + // instead of littering the user's working dirs (res:// + pwd) with log files (GH-12277) + //loggers.push_back(memnew(RotatedFileLogger("user://logs/log.txt"))); + _set_logger(memnew(CompositeLogger(loggers))); +} + bool OSUWP::can_draw() const { return !minimized; @@ -268,12 +278,7 @@ void OSUWP::initialize(const VideoMode &p_desired, int p_video_driver, int p_aud joypad = ref new JoypadUWP(input); joypad->register_events(); - AudioDriverManager::get_driver(p_audio_driver)->set_singleton(); - - if (AudioDriverManager::get_driver(p_audio_driver)->init() != OK) { - - ERR_PRINT("Initializing audio failed."); - } + AudioDriverManager::initialize(p_audio_driver); power_manager = memnew(PowerUWP); @@ -376,32 +381,6 @@ void OSUWP::finalize() { void OSUWP::finalize_core() { } -void OSUWP::vprint(const char *p_format, va_list p_list, bool p_stderr) { - - char buf[16384 + 1]; - int len = vsnprintf(buf, 16384, p_format, p_list); - if (len <= 0) - return; - buf[len] = 0; - - int wlen = MultiByteToWideChar(CP_UTF8, 0, buf, len, NULL, 0); - if (wlen < 0) - return; - - wchar_t *wbuf = (wchar_t *)malloc((len + 1) * sizeof(wchar_t)); - MultiByteToWideChar(CP_UTF8, 0, buf, len, wbuf, wlen); - wbuf[wlen] = 0; - - if (p_stderr) - fwprintf(stderr, L"%s", wbuf); - else - wprintf(L"%s", wbuf); - - free(wbuf); - - fflush(stdout); -}; - void OSUWP::alert(const String &p_alert, const String &p_title) { Platform::String ^ alert = ref new Platform::String(p_alert.c_str()); @@ -525,30 +504,6 @@ OS::VideoMode OSUWP::get_video_mode(int p_screen) const { void OSUWP::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) const { } -void OSUWP::print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type) { - - const char *err_details; - if (p_rationale && p_rationale[0]) - err_details = p_rationale; - else - err_details = p_code; - - switch (p_type) { - case ERR_ERROR: - print("ERROR: %s: %s\n", p_function, err_details); - print(" At: %s:%i\n", p_file, p_line); - break; - case ERR_WARNING: - print("WARNING: %s: %s\n", p_function, err_details); - print(" At: %s:%i\n", p_file, p_line); - break; - case ERR_SCRIPT: - print("SCRIPT ERROR: %s: %s\n", p_function, err_details); - print(" At: %s:%i\n", p_file, p_line); - break; - } -} - String OSUWP::get_name() { return "UWP"; @@ -721,7 +676,7 @@ void OSUWP::set_cursor_shape(CursorShape p_shape) { cursor_shape = p_shape; } -Error OSUWP::execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id, String *r_pipe, int *r_exitcode) { +Error OSUWP::execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id, String *r_pipe, int *r_exitcode, bool read_stderr) { return FAILED; }; @@ -855,7 +810,7 @@ bool OSUWP::_check_internal_feature_support(const String &p_feature) { return p_feature == "pc" || p_feature == "s3tc"; } -PowerState OSUWP::get_power_state() { +OS::PowerState OSUWP::get_power_state() { return power_manager->get_power_state(); } @@ -895,6 +850,8 @@ OSUWP::OSUWP() { mouse_mode_changed = CreateEvent(NULL, TRUE, FALSE, L"os_mouse_mode_changed"); AudioDriverManager::add_driver(&audio_driver); + + _set_logger(memnew(WindowsTerminalLogger)); } OSUWP::~OSUWP() { diff --git a/platform/uwp/os_uwp.h b/platform/uwp/os_uwp.h index 5f36396017..22f8938049 100644 --- a/platform/uwp/os_uwp.h +++ b/platform/uwp/os_uwp.h @@ -163,6 +163,7 @@ protected: virtual int get_audio_driver_count() const; virtual const char *get_audio_driver_name(int p_driver) const; + virtual void initialize_logger(); virtual void initialize_core(); virtual void initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver); @@ -180,9 +181,6 @@ public: // Event to send to the app wrapper HANDLE mouse_mode_changed; - void print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type); - - virtual void vprint(const char *p_format, va_list p_list, bool p_stderr = false); virtual void alert(const String &p_alert, const String &p_title = "ALERT!"); String get_stdin_string(bool p_block); @@ -217,7 +215,7 @@ public: virtual void delay_usec(uint32_t p_usec) const; virtual uint64_t get_ticks_usec() const; - virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL); + virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false); virtual Error kill(const ProcessID &p_pid); virtual bool has_environment(const String &p_var) const; @@ -259,7 +257,7 @@ public: void input_event(const Ref<InputEvent> &p_event); - virtual PowerState get_power_state(); + virtual OS::PowerState get_power_state(); virtual int get_power_seconds_left(); virtual int get_power_percent_left(); diff --git a/platform/uwp/power_uwp.cpp b/platform/uwp/power_uwp.cpp index 07a726647d..81e97b1391 100644 --- a/platform/uwp/power_uwp.cpp +++ b/platform/uwp/power_uwp.cpp @@ -31,7 +31,7 @@ #include "power_uwp.h" PowerUWP::PowerUWP() - : nsecs_left(-1), percent_left(-1), power_state(POWERSTATE_UNKNOWN) { + : nsecs_left(-1), percent_left(-1), power_state(OS::POWERSTATE_UNKNOWN) { } PowerUWP::~PowerUWP() { @@ -47,12 +47,12 @@ bool PowerUWP::UpdatePowerInfo() { return false; } -PowerState PowerUWP::get_power_state() { +OS::PowerState PowerUWP::get_power_state() { if (UpdatePowerInfo()) { return power_state; } else { WARN_PRINT("Power management is not implemented on this platform, defaulting to POWERSTATE_UNKNOWN"); - return POWERSTATE_UNKNOWN; + return OS::POWERSTATE_UNKNOWN; } } diff --git a/platform/uwp/power_uwp.h b/platform/uwp/power_uwp.h index 9a9811a4f5..0c57689c50 100644 --- a/platform/uwp/power_uwp.h +++ b/platform/uwp/power_uwp.h @@ -33,14 +33,14 @@ #include "os/dir_access.h" #include "os/file_access.h" -#include "os/power.h" +#include "os/os.h" class PowerUWP { private: int nsecs_left; int percent_left; - PowerState power_state; + OS::PowerState power_state; bool UpdatePowerInfo(); @@ -48,7 +48,7 @@ public: PowerUWP(); virtual ~PowerUWP(); - PowerState get_power_state(); + OS::PowerState get_power_state(); int get_power_seconds_left(); int get_power_percent_left(); }; diff --git a/platform/windows/SCsub b/platform/windows/SCsub index befbe00183..aa9eb3e69b 100644 --- a/platform/windows/SCsub +++ b/platform/windows/SCsub @@ -1,10 +1,16 @@ #!/usr/bin/env python +import os Import('env') +def make_debug_mingw(target, source, env): + os.system('objcopy --only-keep-debug %s %s.debug' % (target[0], target[0])) + os.system('strip --strip-debug --strip-unneeded %s' % (target[0])) + os.system('objcopy --add-gnu-debuglink=%s.debug %s' % (target[0], target[0])) common_win = [ "context_gl_win.cpp", + "crash_handler_win.cpp", "os_windows.cpp", "ctxgl_procaddr.cpp", "key_mapping_win.cpp", @@ -12,7 +18,8 @@ common_win = [ "packet_peer_udp_winsock.cpp", "stream_peer_winsock.cpp", "joypad.cpp", - "power_windows.cpp", + "power_windows.cpp", + "windows_terminal_logger.cpp" ] restarget = "godot_res" + env["OBJSUFFIX"] @@ -21,10 +28,14 @@ obj = env.RES(restarget, 'godot_res.rc') common_win.append(obj) -env.Program('#bin/godot', ['godot_win.cpp'] + common_win, PROGSUFFIX=env["PROGSUFFIX"]) +binary = env.Program('#bin/godot', ['godot_win.cpp'] + common_win, PROGSUFFIX=env["PROGSUFFIX"]) # Microsoft Visual Studio Project Generation -if (env['vsproj']) == "yes": +if env['vsproj']: env.vs_srcs = env.vs_srcs + ["platform/windows/godot_win.cpp"] for x in common_win: env.vs_srcs = env.vs_srcs + ["platform/windows/" + str(x)] + +if not os.getenv("VCINSTALLDIR"): + if env["debug_symbols"] == "full" or env["debug_symbols"] == "yes": + env.AddPostAction(binary, make_debug_mingw) diff --git a/platform/windows/context_gl_win.cpp b/platform/windows/context_gl_win.cpp index 328b987f1f..64b6d202a1 100644 --- a/platform/windows/context_gl_win.cpp +++ b/platform/windows/context_gl_win.cpp @@ -27,7 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED) || defined(GLES2_ENABLED) +#if defined(OPENGL_ENABLED) || defined(GLES2_ENABLED) // // C++ Implementation: context_gl_x11 @@ -130,24 +130,28 @@ Error ContextGL_Win::initialize() { 0, 0, 0 // Layer Masks Ignored }; - if (!(hDC = GetDC(hWnd))) { + hDC = GetDC(hWnd); + if (!hDC) { MessageBox(NULL, "Can't Create A GL Device Context.", "ERROR", MB_OK | MB_ICONEXCLAMATION); return ERR_CANT_CREATE; // Return FALSE } - if (!(pixel_format = ChoosePixelFormat(hDC, &pfd))) // Did Windows Find A Matching Pixel Format? + pixel_format = ChoosePixelFormat(hDC, &pfd); + if (!pixel_format) // Did Windows Find A Matching Pixel Format? { MessageBox(NULL, "Can't Find A Suitable pixel_format.", "ERROR", MB_OK | MB_ICONEXCLAMATION); return ERR_CANT_CREATE; // Return FALSE } - if (!SetPixelFormat(hDC, pixel_format, &pfd)) // Are We Able To Set The Pixel Format? + BOOL ret = SetPixelFormat(hDC, pixel_format, &pfd); + if (!ret) // Are We Able To Set The Pixel Format? { MessageBox(NULL, "Can't Set The pixel_format.", "ERROR", MB_OK | MB_ICONEXCLAMATION); return ERR_CANT_CREATE; // Return FALSE } - if (!(hRC = wglCreateContext(hDC))) // Are We Able To Get A Rendering Context? + hRC = wglCreateContext(hDC); + if (!hRC) // Are We Able To Get A Rendering Context? { MessageBox(NULL, "Can't Create A Temporary GL Rendering Context.", "ERROR", MB_OK | MB_ICONEXCLAMATION); return ERR_CANT_CREATE; // Return FALSE @@ -175,8 +179,8 @@ Error ContextGL_Win::initialize() { return ERR_CANT_CREATE; } - HGLRC new_hRC; - if (!(new_hRC = wglCreateContextAttribsARB(hDC, 0, attribs))) { + HGLRC new_hRC = wglCreateContextAttribsARB(hDC, 0, attribs); + if (!new_hRC) { wglDeleteContext(hRC); MessageBox(NULL, "Can't Create An OpenGL 3.3 Rendering Context.", "ERROR", MB_OK | MB_ICONEXCLAMATION); return ERR_CANT_CREATE; // Return false diff --git a/platform/windows/context_gl_win.h b/platform/windows/context_gl_win.h index 912d4d0133..0059cbc311 100644 --- a/platform/windows/context_gl_win.h +++ b/platform/windows/context_gl_win.h @@ -27,7 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED) || defined(GLES2_ENABLED) +#if defined(OPENGL_ENABLED) || defined(GLES2_ENABLED) // // C++ Interface: context_gl_x11 // diff --git a/platform/windows/crash_handler_win.cpp b/platform/windows/crash_handler_win.cpp new file mode 100644 index 0000000000..2f5ee7956e --- /dev/null +++ b/platform/windows/crash_handler_win.cpp @@ -0,0 +1,211 @@ +/*************************************************************************/ +/* crash_handler_win.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "main/main.h" +#include "os_windows.h" + +#ifdef CRASH_HANDLER_EXCEPTION + +// Backtrace code code based on: https://stackoverflow.com/questions/6205981/windows-c-stack-trace-from-a-running-app + +#include <psapi.h> +#include <algorithm> +#include <iterator> + +#pragma comment(lib, "psapi.lib") +#pragma comment(lib, "dbghelp.lib") + +// Some versions of imagehlp.dll lack the proper packing directives themselves +// so we need to do it. +#pragma pack(push, before_imagehlp, 8) +#include <imagehlp.h> +#pragma pack(pop, before_imagehlp) + +struct module_data { + std::string image_name; + std::string module_name; + void *base_address; + DWORD load_size; +}; + +class symbol { + typedef IMAGEHLP_SYMBOL64 sym_type; + sym_type *sym; + static const int max_name_len = 1024; + +public: + symbol(HANDLE process, DWORD64 address) + : sym((sym_type *)::operator new(sizeof(*sym) + max_name_len)) { + memset(sym, '\0', sizeof(*sym) + max_name_len); + sym->SizeOfStruct = sizeof(*sym); + sym->MaxNameLength = max_name_len; + DWORD64 displacement; + + SymGetSymFromAddr64(process, address, &displacement, sym); + } + + std::string name() { return std::string(sym->Name); } + std::string undecorated_name() { + if (*sym->Name == '\0') + return "<couldn't map PC to fn name>"; + std::vector<char> und_name(max_name_len); + UnDecorateSymbolName(sym->Name, &und_name[0], max_name_len, UNDNAME_COMPLETE); + return std::string(&und_name[0], strlen(&und_name[0])); + } +}; + +class get_mod_info { + HANDLE process; + +public: + get_mod_info(HANDLE h) + : process(h) {} + + module_data operator()(HMODULE module) { + module_data ret; + char temp[4096]; + MODULEINFO mi; + + GetModuleInformation(process, module, &mi, sizeof(mi)); + ret.base_address = mi.lpBaseOfDll; + ret.load_size = mi.SizeOfImage; + + GetModuleFileNameEx(process, module, temp, sizeof(temp)); + ret.image_name = temp; + GetModuleBaseName(process, module, temp, sizeof(temp)); + ret.module_name = temp; + std::vector<char> img(ret.image_name.begin(), ret.image_name.end()); + std::vector<char> mod(ret.module_name.begin(), ret.module_name.end()); + SymLoadModule64(process, 0, &img[0], &mod[0], (DWORD64)ret.base_address, ret.load_size); + return ret; + } +}; + +DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) { + HANDLE process = GetCurrentProcess(); + HANDLE hThread = GetCurrentThread(); + DWORD offset_from_symbol = 0; + IMAGEHLP_LINE64 line = { 0 }; + std::vector<module_data> modules; + DWORD cbNeeded; + std::vector<HMODULE> module_handles(1); + + if (OS::get_singleton() == NULL || OS::get_singleton()->is_disable_crash_handler() || IsDebuggerPresent()) { + return EXCEPTION_CONTINUE_SEARCH; + } + + fprintf(stderr, "%s: Program crashed\n", __FUNCTION__); + + // Load the symbols: + if (!SymInitialize(process, NULL, false)) + return EXCEPTION_CONTINUE_SEARCH; + + SymSetOptions(SymGetOptions() | SYMOPT_LOAD_LINES | SYMOPT_UNDNAME); + EnumProcessModules(process, &module_handles[0], module_handles.size() * sizeof(HMODULE), &cbNeeded); + module_handles.resize(cbNeeded / sizeof(HMODULE)); + EnumProcessModules(process, &module_handles[0], module_handles.size() * sizeof(HMODULE), &cbNeeded); + std::transform(module_handles.begin(), module_handles.end(), std::back_inserter(modules), get_mod_info(process)); + void *base = modules[0].base_address; + + // Setup stuff: + CONTEXT *context = ep->ContextRecord; + STACKFRAME64 frame; + bool skip_first = false; + + frame.AddrPC.Mode = AddrModeFlat; + frame.AddrStack.Mode = AddrModeFlat; + frame.AddrFrame.Mode = AddrModeFlat; + +#ifdef _M_X64 + frame.AddrPC.Offset = context->Rip; + frame.AddrStack.Offset = context->Rsp; + frame.AddrFrame.Offset = context->Rbp; +#else + frame.AddrPC.Offset = context->Eip; + frame.AddrStack.Offset = context->Esp; + frame.AddrFrame.Offset = context->Ebp; + + // Skip the first one to avoid a duplicate on 32-bit mode + skip_first = true; +#endif + + line.SizeOfStruct = sizeof(line); + IMAGE_NT_HEADERS *h = ImageNtHeader(base); + DWORD image_type = h->FileHeader.Machine; + int n = 0; + String msg = GLOBAL_GET("debug/settings/crash_handler/message"); + + fprintf(stderr, "Dumping the backtrace. %ls\n", msg.c_str()); + + do { + if (skip_first) { + skip_first = false; + } else { + if (frame.AddrPC.Offset != 0) { + std::string fnName = symbol(process, frame.AddrPC.Offset).undecorated_name(); + + if (SymGetLineFromAddr64(process, frame.AddrPC.Offset, &offset_from_symbol, &line)) + fprintf(stderr, "[%d] %s (%s:%d)\n", n, fnName.c_str(), line.FileName, line.LineNumber); + else + fprintf(stderr, "[%d] %s\n", n, fnName.c_str()); + } else + fprintf(stderr, "[%d] ???\n", n); + + n++; + } + + if (!StackWalk64(image_type, process, hThread, &frame, context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL)) + break; + } while (frame.AddrReturn.Offset != 0 && n < 256); + + fprintf(stderr, "-- END OF BACKTRACE --\n"); + + SymCleanup(process); + + // Pass the exception to the OS + return EXCEPTION_CONTINUE_SEARCH; +} +#endif + +CrashHandler::CrashHandler() { + disabled = false; +} + +CrashHandler::~CrashHandler() { +} + +void CrashHandler::disable() { + if (disabled) + return; + + disabled = true; +} + +void CrashHandler::initialize() { +} diff --git a/platform/iphone/audio_driver_iphone.h b/platform/windows/crash_handler_win.h index 930ed168f7..0b1889e4fe 100644 --- a/platform/iphone/audio_driver_iphone.h +++ b/platform/windows/crash_handler_win.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* audio_driver_iphone.h */ +/* crash_handler_win.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -27,40 +27,30 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#ifndef CRASH_HANDLER_WIN_H +#define CRASH_HANDLER_WIN_H -#include "servers/audio_server.h" +#include <windows.h> -#include <AudioUnit/AudioUnit.h> +// Crash handler exception only enabled with MSVC +#if defined(DEBUG_ENABLED) && defined(MSVC) +#define CRASH_HANDLER_EXCEPTION 1 -class AudioDriverIphone : public AudioDriver { +extern DWORD CrashHandlerException(EXCEPTION_POINTERS *ep); +#endif - AudioComponentInstance audio_unit; - bool active; - Mutex *mutex; +class CrashHandler { - int channels; - int32_t *samples_in; - int buffer_frames; - - static OSStatus output_callback(void *inRefCon, - AudioUnitRenderActionFlags *ioActionFlags, - const AudioTimeStamp *inTimeStamp, - UInt32 inBusNumber, UInt32 inNumberFrames, - AudioBufferList *ioData); + bool disabled; public: - const char *get_name() const { - return "IPhone"; - }; + void initialize(); - virtual Error init(); - virtual void start(); - virtual int get_mix_rate() const; - virtual SpeakerMode get_speaker_mode() const; - virtual void lock(); - virtual void unlock(); - virtual void finish(); + void disable(); + bool is_disabled() const { return disabled; }; - AudioDriverIphone(); - ~AudioDriverIphone(); + CrashHandler(); + ~CrashHandler(); }; + +#endif diff --git a/platform/windows/detect.py b/platform/windows/detect.py index d239ccf7d2..bac5df5668 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -44,11 +44,11 @@ def can_build(): if (os.system(mingw64 + test) == 0 or os.system(mingw32 + test) == 0): return True - print("Could not detect MinGW. Ensure its binaries are in your PATH or that MINGW32_PREFIX or MINGW64_PREFIX are properly defined.") return False def get_opts(): + from SCons.Variables import BoolVariable, EnumVariable mingw32 = "" mingw64 = "" @@ -64,6 +64,8 @@ def get_opts(): return [ ('mingw_prefix_32', 'MinGW prefix (Win32)', mingw32), ('mingw_prefix_64', 'MinGW prefix (Win64)', mingw64), + BoolVariable('use_lto', 'Use link time optimization (when using MingW)', False), + EnumVariable('debug_symbols', 'Add debug symbols to release version', 'yes', ('yes', 'no', 'full')), ] @@ -123,7 +125,7 @@ def configure(env): env.Append(LINKFLAGS=['/ENTRY:mainCRTStartup']) elif (env["target"] == "debug"): - env.Append(CCFLAGS=['/Z7', '/DDEBUG_ENABLED', '/DDEBUG_MEMORY_ENABLED', '/DD3D_DEBUG_INFO', '/Od']) + env.Append(CCFLAGS=['/Z7', '/DDEBUG_ENABLED', '/DDEBUG_MEMORY_ENABLED', '/DD3D_DEBUG_INFO', '/Od', '/EHsc']) env.Append(LINKFLAGS=['/SUBSYSTEM:CONSOLE']) env.Append(LINKFLAGS=['/DEBUG']) @@ -213,11 +215,20 @@ def configure(env): env.Append(LINKFLAGS=['-Wl,--subsystem,windows']) + if (env["debug_symbols"] == "yes"): + env.Prepend(CCFLAGS=['-g1']) + if (env["debug_symbols"] == "full"): + env.Prepend(CCFLAGS=['-g2']) + elif (env["target"] == "release_debug"): env.Append(CCFLAGS=['-O2', '-DDEBUG_ENABLED']) + if (env["debug_symbols"] == "yes"): + env.Prepend(CCFLAGS=['-g1']) + if (env["debug_symbols"] == "full"): + env.Prepend(CCFLAGS=['-g2']) elif (env["target"] == "debug"): - env.Append(CCFLAGS=['-g', '-DDEBUG_ENABLED', '-DDEBUG_MEMORY_ENABLED']) + env.Append(CCFLAGS=['-g3', '-DDEBUG_ENABLED', '-DDEBUG_MEMORY_ENABLED']) ## Compiler configuration @@ -226,10 +237,13 @@ def configure(env): else: env["PROGSUFFIX"] = env["PROGSUFFIX"] + ".exe" # for linux cross-compilation - mingw_prefix = "" - if (env["bits"] == "default"): - env["bits"] = "64" if "PROGRAMFILES(X86)" in os.environ else "32" + if (os.name == "nt"): + env["bits"] = "64" if "PROGRAMFILES(X86)" in os.environ else "32" + else: # default to 64-bit on Linux + env["bits"] = "64" + + mingw_prefix = "" if (env["bits"] == "32"): env.Append(LINKFLAGS=['-static']) @@ -243,11 +257,15 @@ def configure(env): env["CC"] = mingw_prefix + "gcc" env['AS'] = mingw_prefix + "as" env['CXX'] = mingw_prefix + "g++" - env['AR'] = mingw_prefix + "ar" - env['RANLIB'] = mingw_prefix + "ranlib" + env['AR'] = mingw_prefix + "gcc-ar" + env['RANLIB'] = mingw_prefix + "gcc-ranlib" env['LD'] = mingw_prefix + "g++" env["x86_libtheora_opt_gcc"] = True + if env['use_lto']: + env.Append(CCFLAGS=['-flto']) + env.Append(LINKFLAGS=['-flto=' + str(env.GetOption("num_jobs"))]) + ## Compile flags env.Append(CCFLAGS=['-DWINDOWS_ENABLED', '-mwindows']) diff --git a/platform/windows/godot_win.cpp b/platform/windows/godot_win.cpp index d2ac6ecb50..cff2cbad42 100644 --- a/platform/windows/godot_win.cpp +++ b/platform/windows/godot_win.cpp @@ -156,10 +156,7 @@ int widechar_main(int argc, wchar_t **argv) { return os.get_exit_code(); }; -int main(int _argc, char **_argv) { - // _argc and _argv are ignored - // we are going to use the WideChar version of them instead - +int _main() { LPWSTR *wc_argv; int argc; int result; @@ -177,6 +174,21 @@ int main(int _argc, char **_argv) { return result; } +int main(int _argc, char **_argv) { +// _argc and _argv are ignored +// we are going to use the WideChar version of them instead + +#ifdef CRASH_HANDLER_EXCEPTION + __try { + return _main(); + } __except (CrashHandlerException(GetExceptionInformation())) { + return 1; + } +#else + return _main(); +#endif +} + HINSTANCE godot_hinstance = NULL; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { diff --git a/platform/windows/joypad.cpp b/platform/windows/joypad.cpp index cde72c98c9..0c7358f499 100644 --- a/platform/windows/joypad.cpp +++ b/platform/windows/joypad.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* joypad.cpp */ +/* joypad.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -379,7 +379,9 @@ void JoypadWindows::process_joypads() { IDirectInputDevice8_Acquire(joy->di_joy); joy->di_joy->Poll(); } - if (FAILED(hr = joy->di_joy->GetDeviceState(sizeof(DIJOYSTATE2), &js))) { + + hr = joy->di_joy->GetDeviceState(sizeof(DIJOYSTATE2), &js); + if (FAILED(hr)) { //printf("failed to read joy #%d\n", i); continue; diff --git a/platform/windows/joypad.h b/platform/windows/joypad.h index 059c577bb6..a76caf6fac 100644 --- a/platform/windows/joypad.h +++ b/platform/windows/joypad.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* joypad.h */ +/* joypad.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ diff --git a/platform/windows/key_mapping_win.cpp b/platform/windows/key_mapping_win.cpp index 57f8e965de..76bb5d5723 100644 --- a/platform/windows/key_mapping_win.cpp +++ b/platform/windows/key_mapping_win.cpp @@ -50,7 +50,7 @@ static _WinTranslatePair _vk_to_keycode[] = { { KEY_CONTROL, VK_CONTROL }, //(0x11) - { KEY_MENU, VK_MENU }, //(0x12) + { KEY_ALT, VK_MENU }, //(0x12) { KEY_PAUSE, VK_PAUSE }, //(0x13) diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index deb9c25576..ac78dddf0c 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -48,6 +48,7 @@ #include "servers/visual/visual_server_wrap_mt.h" #include "stream_peer_winsock.h" #include "tcp_server_winsock.h" +#include "windows_terminal_logger.h" #include <process.h> #include <regstr.h> @@ -164,6 +165,8 @@ const char *OS_Windows::get_audio_driver_name(int p_driver) const { void OS_Windows::initialize_core() { + crash_handler.initialize(); + last_button_state = 0; //RedirectIOToConsole(); @@ -203,6 +206,15 @@ void OS_Windows::initialize_core() { cursor_shape = CURSOR_ARROW; } +void OS_Windows::initialize_logger() { + Vector<Logger *> loggers; + loggers.push_back(memnew(WindowsTerminalLogger)); + // FIXME: Reenable once we figure out how to get this properly in user:// + // instead of littering the user's working dirs (res:// + pwd) with log files (GH-12277) + //loggers.push_back(memnew(RotatedFileLogger("user://logs/log.txt"))); + _set_logger(memnew(CompositeLogger(loggers))); +} + bool OS_Windows::can_draw() const { return !minimized; @@ -887,6 +899,12 @@ static int QueryDpiForMonitor(HMONITOR hmon, _MonitorDpiType dpiType = MDT_Defau return (dpiX + dpiY) / 2; } +typedef enum _SHC_PROCESS_DPI_AWARENESS { + SHC_PROCESS_DPI_UNAWARE = 0, + SHC_PROCESS_SYSTEM_DPI_AWARE = 1, + SHC_PROCESS_PER_MONITOR_DPI_AWARE = 2 +} SHC_PROCESS_DPI_AWARENESS; + void OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { main_loop = NULL; @@ -894,6 +912,20 @@ void OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int window_has_focus = true; WNDCLASSEXW wc; + if (is_hidpi_allowed()) { + HMODULE Shcore = LoadLibraryW(L"Shcore.dll"); + + if (Shcore != NULL) { + typedef HRESULT(WINAPI * SetProcessDpiAwareness_t)(SHC_PROCESS_DPI_AWARENESS); + + SetProcessDpiAwareness_t SetProcessDpiAwareness = (SetProcessDpiAwareness_t)GetProcAddress(Shcore, "SetProcessDpiAwareness"); + + if (SetProcessDpiAwareness) { + SetProcessDpiAwareness(SHC_PROCESS_SYSTEM_DPI_AWARE); + } + } + } + video_mode = p_desired; //printf("**************** desired %s, mode %s\n", p_desired.fullscreen?"true":"false", video_mode.fullscreen?"true":"false"); RECT WindowRect; @@ -996,7 +1028,16 @@ void OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int video_mode.fullscreen = false; } else { - if (!(hWnd = CreateWindowExW(dwExStyle, L"Engine", L"", dwStyle | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, (GetSystemMetrics(SM_CXSCREEN) - WindowRect.right) / 2, (GetSystemMetrics(SM_CYSCREEN) - WindowRect.bottom) / 2, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top, NULL, NULL, hInstance, NULL))) { + hWnd = CreateWindowExW( + dwExStyle, + L"Engine", L"", + dwStyle | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, + (GetSystemMetrics(SM_CXSCREEN) - WindowRect.right) / 2, + (GetSystemMetrics(SM_CYSCREEN) - WindowRect.bottom) / 2, + WindowRect.right - WindowRect.left, + WindowRect.bottom - WindowRect.top, + NULL, NULL, hInstance, NULL); + if (!hWnd) { MessageBoxW(NULL, L"Window Creation Error.", L"ERROR", MB_OK | MB_ICONEXCLAMATION); return; // Return FALSE } @@ -1050,12 +1091,7 @@ void OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int power_manager = memnew(PowerWindows); - AudioDriverManager::get_driver(p_audio_driver)->set_singleton(); - - if (AudioDriverManager::get_driver(p_audio_driver)->init() != OK) { - - ERR_PRINT("Initializing audio failed."); - } + AudioDriverManager::initialize(p_audio_driver); TRACKMOUSEEVENT tme; tme.cbSize = sizeof(TRACKMOUSEEVENT); @@ -1205,38 +1241,6 @@ void OS_Windows::finalize_core() { StreamPeerWinsock::cleanup(); } -void OS_Windows::vprint(const char *p_format, va_list p_list, bool p_stderr) { - - const unsigned int BUFFER_SIZE = 16384; - char buf[BUFFER_SIZE + 1]; // +1 for the terminating character - int len = vsnprintf(buf, BUFFER_SIZE, p_format, p_list); - if (len <= 0) - return; - if (len >= BUFFER_SIZE) - len = BUFFER_SIZE; // Output is too big, will be truncated - buf[len] = 0; - - int wlen = MultiByteToWideChar(CP_UTF8, 0, buf, len, NULL, 0); - if (wlen < 0) - return; - - wchar_t *wbuf = (wchar_t *)malloc((len + 1) * sizeof(wchar_t)); - MultiByteToWideChar(CP_UTF8, 0, buf, len, wbuf, wlen); - wbuf[wlen] = 0; - - if (p_stderr) - fwprintf(stderr, L"%ls", wbuf); - else - wprintf(L"%ls", wbuf); - -#ifdef STDOUT_FILE -//vwfprintf(stdo,p_format,p_list); -#endif - free(wbuf); - - fflush(stdout); -}; - void OS_Windows::alert(const String &p_alert, const String &p_title) { if (!is_no_window_mode_enabled()) @@ -1278,7 +1282,7 @@ OS_Windows::MouseMode OS_Windows::get_mouse_mode() const { return mouse_mode; } -void OS_Windows::warp_mouse_pos(const Point2 &p_to) { +void OS_Windows::warp_mouse_position(const Point2 &p_to) { if (mouse_mode == MOUSE_MODE_CAPTURED) { @@ -1650,107 +1654,6 @@ void OS_Windows::request_attention() { FlashWindowEx(&info); } -void OS_Windows::print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type) { - - HANDLE hCon = GetStdHandle(STD_OUTPUT_HANDLE); - if (!hCon || hCon == INVALID_HANDLE_VALUE) { - - const char *err_details; - if (p_rationale && p_rationale[0]) - err_details = p_rationale; - else - err_details = p_code; - - switch (p_type) { - case ERR_ERROR: - print("ERROR: %s: %s\n", p_function, err_details); - print(" At: %s:%i\n", p_file, p_line); - break; - case ERR_WARNING: - print("WARNING: %s: %s\n", p_function, err_details); - print(" At: %s:%i\n", p_file, p_line); - break; - case ERR_SCRIPT: - print("SCRIPT ERROR: %s: %s\n", p_function, err_details); - print(" At: %s:%i\n", p_file, p_line); - break; - case ERR_SHADER: - print("SHADER ERROR: %s: %s\n", p_function, err_details); - print(" At: %s:%i\n", p_file, p_line); - break; - } - - } else { - - CONSOLE_SCREEN_BUFFER_INFO sbi; //original - GetConsoleScreenBufferInfo(hCon, &sbi); - - WORD current_fg = sbi.wAttributes & (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY); - WORD current_bg = sbi.wAttributes & (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY); - - uint32_t basecol = 0; - switch (p_type) { - case ERR_ERROR: basecol = FOREGROUND_RED; break; - case ERR_WARNING: basecol = FOREGROUND_RED | FOREGROUND_GREEN; break; - case ERR_SCRIPT: basecol = FOREGROUND_RED | FOREGROUND_BLUE; break; - case ERR_SHADER: basecol = FOREGROUND_GREEN | FOREGROUND_BLUE; break; - } - - basecol |= current_bg; - - if (p_rationale && p_rationale[0]) { - - SetConsoleTextAttribute(hCon, basecol | FOREGROUND_INTENSITY); - switch (p_type) { - case ERR_ERROR: print("ERROR: "); break; - case ERR_WARNING: print("WARNING: "); break; - case ERR_SCRIPT: print("SCRIPT ERROR: "); break; - case ERR_SHADER: print("SHADER ERROR: "); break; - } - - SetConsoleTextAttribute(hCon, current_fg | current_bg | FOREGROUND_INTENSITY); - print("%s\n", p_rationale); - - SetConsoleTextAttribute(hCon, basecol); - switch (p_type) { - case ERR_ERROR: print(" At: "); break; - case ERR_WARNING: print(" At: "); break; - case ERR_SCRIPT: print(" At: "); break; - case ERR_SHADER: print(" At: "); break; - } - - SetConsoleTextAttribute(hCon, current_fg | current_bg); - print("%s:%i\n", p_file, p_line); - - } else { - - SetConsoleTextAttribute(hCon, basecol | FOREGROUND_INTENSITY); - switch (p_type) { - case ERR_ERROR: print("ERROR: %s: ", p_function); break; - case ERR_WARNING: print("WARNING: %s: ", p_function); break; - case ERR_SCRIPT: print("SCRIPT ERROR: %s: ", p_function); break; - case ERR_SHADER: print("SCRIPT ERROR: %s: ", p_function); break; - } - - SetConsoleTextAttribute(hCon, current_fg | current_bg | FOREGROUND_INTENSITY); - print("%s\n", p_code); - - SetConsoleTextAttribute(hCon, basecol); - switch (p_type) { - case ERR_ERROR: print(" At: "); break; - case ERR_WARNING: print(" At: "); break; - case ERR_SCRIPT: print(" At: "); break; - case ERR_SHADER: print(" At: "); break; - } - - SetConsoleTextAttribute(hCon, current_fg | current_bg); - print("%s:%i\n", p_file, p_line); - } - - SetConsoleTextAttribute(hCon, sbi.wAttributes); - } -} - String OS_Windows::get_name() { return "Windows"; @@ -1911,7 +1814,7 @@ void OS_Windows::set_cursor_shape(CursorShape p_shape) { cursor_shape = p_shape; } -Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id, String *r_pipe, int *r_exitcode) { +Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id, String *r_pipe, int *r_exitcode, bool read_stderr) { if (p_blocking && r_pipe) { @@ -2322,7 +2225,7 @@ bool OS_Windows::is_vsync_enabled() const { return true; } -PowerState OS_Windows::get_power_state() { +OS::PowerState OS_Windows::get_power_state() { return power_manager->get_power_state(); } @@ -2339,6 +2242,41 @@ bool OS_Windows::_check_internal_feature_support(const String &p_feature) { return p_feature == "pc" || p_feature == "s3tc"; } +void OS_Windows::disable_crash_handler() { + crash_handler.disable(); +} + +bool OS_Windows::is_disable_crash_handler() const { + return crash_handler.is_disabled(); +} + +Error OS_Windows::move_to_trash(const String &p_path) { + SHFILEOPSTRUCTA sf; + TCHAR *from = new TCHAR[p_path.length() + 2]; + strcpy(from, p_path.utf8().get_data()); + from[p_path.length()] = 0; + from[p_path.length() + 1] = 0; + + sf.hwnd = hWnd; + sf.wFunc = FO_DELETE; + sf.pFrom = from; + sf.pTo = NULL; + sf.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION; + sf.fAnyOperationsAborted = FALSE; + sf.hNameMappings = NULL; + sf.lpszProgressTitle = NULL; + + int ret = SHFileOperation(&sf); + delete[] from; + + if (ret) { + ERR_PRINTS("SHFileOperation error: " + itos(ret)); + return FAILED; + } + + return OK; +} + OS_Windows::OS_Windows(HINSTANCE _hInstance) { key_event_pos = 0; @@ -2368,6 +2306,8 @@ OS_Windows::OS_Windows(HINSTANCE _hInstance) { #ifdef XAUDIO2_ENABLED AudioDriverManager::add_driver(&driver_xaudio2); #endif + + _set_logger(memnew(WindowsTerminalLogger)); } OS_Windows::~OS_Windows() { diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index 0c5965bf51..c0b8dfc691 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -31,6 +31,7 @@ #define OS_WINDOWS_H #include "context_gl_win.h" +#include "crash_handler_win.h" #include "drivers/rtaudio/audio_driver_rtaudio.h" #include "drivers/wasapi/audio_driver_wasapi.h" #include "os/input.h" @@ -134,6 +135,8 @@ class OS_Windows : public OS { AudioDriverXAudio2 driver_xaudio2; #endif + CrashHandler crash_handler; + void _drag_event(int p_x, int p_y, int idx); void _touch_event(bool p_pressed, int p_x, int p_y, int idx); @@ -149,6 +152,7 @@ protected: virtual int get_audio_driver_count() const; virtual const char *get_audio_driver_name(int p_driver) const; + virtual void initialize_logger(); virtual void initialize_core(); virtual void initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver); @@ -177,16 +181,13 @@ protected: public: LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); - void print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type); - - virtual void vprint(const char *p_format, va_list p_list, bool p_stderr = false); virtual void alert(const String &p_alert, const String &p_title = "ALERT!"); String get_stdin_string(bool p_block); void set_mouse_mode(MouseMode p_mode); MouseMode get_mouse_mode() const; - virtual void warp_mouse_pos(const Point2 &p_to); + virtual void warp_mouse_position(const Point2 &p_to); virtual Point2 get_mouse_position() const; virtual int get_mouse_button_state() const; virtual void set_window_title(const String &p_title); @@ -239,7 +240,7 @@ public: virtual void delay_usec(uint32_t p_usec) const; virtual uint64_t get_ticks_usec() const; - virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL); + virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false); virtual Error kill(const ProcessID &p_pid); virtual int get_process_id() const; @@ -278,12 +279,17 @@ public: virtual void set_use_vsync(bool p_enable); virtual bool is_vsync_enabled() const; - virtual PowerState get_power_state(); + virtual OS::PowerState get_power_state(); virtual int get_power_seconds_left(); virtual int get_power_percent_left(); virtual bool _check_internal_feature_support(const String &p_feature); + void disable_crash_handler(); + bool is_disable_crash_handler() const; + + virtual Error move_to_trash(const String &p_path); + OS_Windows(HINSTANCE _hInstance); ~OS_Windows(); }; diff --git a/platform/windows/power_windows.cpp b/platform/windows/power_windows.cpp index b37e189a3a..8d86f160f1 100644 --- a/platform/windows/power_windows.cpp +++ b/platform/windows/power_windows.cpp @@ -64,19 +64,19 @@ bool PowerWindows::GetPowerInfo_Windows() { /* This API should exist back to Win95. */ if (!GetSystemPowerStatus(&status)) { /* !!! FIXME: push GetLastError() into GetError() */ - power_state = POWERSTATE_UNKNOWN; + power_state = OS::POWERSTATE_UNKNOWN; } else if (status.BatteryFlag == 0xFF) { /* unknown state */ - power_state = POWERSTATE_UNKNOWN; + power_state = OS::POWERSTATE_UNKNOWN; } else if (status.BatteryFlag & (1 << 7)) { /* no battery */ - power_state = POWERSTATE_NO_BATTERY; + power_state = OS::POWERSTATE_NO_BATTERY; } else if (status.BatteryFlag & (1 << 3)) { /* charging */ - power_state = POWERSTATE_CHARGING; + power_state = OS::POWERSTATE_CHARGING; need_details = TRUE; } else if (status.ACLineStatus == 1) { - power_state = POWERSTATE_CHARGED; /* on AC, not charging. */ + power_state = OS::POWERSTATE_CHARGED; /* on AC, not charging. */ need_details = TRUE; } else { - power_state = POWERSTATE_ON_BATTERY; /* not on AC. */ + power_state = OS::POWERSTATE_ON_BATTERY; /* not on AC. */ need_details = TRUE; } @@ -97,11 +97,11 @@ bool PowerWindows::GetPowerInfo_Windows() { return TRUE; /* always the definitive answer on Windows. */ } -PowerState PowerWindows::get_power_state() { +OS::PowerState PowerWindows::get_power_state() { if (GetPowerInfo_Windows()) { return power_state; } else { - return POWERSTATE_UNKNOWN; + return OS::POWERSTATE_UNKNOWN; } } @@ -122,7 +122,7 @@ int PowerWindows::get_power_percent_left() { } PowerWindows::PowerWindows() - : nsecs_left(-1), percent_left(-1), power_state(POWERSTATE_UNKNOWN) { + : nsecs_left(-1), percent_left(-1), power_state(OS::POWERSTATE_UNKNOWN) { } PowerWindows::~PowerWindows() { diff --git a/platform/windows/power_windows.h b/platform/windows/power_windows.h index 9da9841f48..0745615195 100644 --- a/platform/windows/power_windows.h +++ b/platform/windows/power_windows.h @@ -33,7 +33,7 @@ #include "os/dir_access.h" #include "os/file_access.h" -#include "os/power.h" +#include "os/os.h" #include <windows.h> @@ -42,7 +42,7 @@ class PowerWindows { private: int nsecs_left; int percent_left; - PowerState power_state; + OS::PowerState power_state; bool GetPowerInfo_Windows(); @@ -50,7 +50,7 @@ public: PowerWindows(); virtual ~PowerWindows(); - PowerState get_power_state(); + OS::PowerState get_power_state(); int get_power_seconds_left(); int get_power_percent_left(); }; diff --git a/platform/windows/windows_terminal_logger.cpp b/platform/windows/windows_terminal_logger.cpp new file mode 100644 index 0000000000..ef8140ffa7 --- /dev/null +++ b/platform/windows/windows_terminal_logger.cpp @@ -0,0 +1,157 @@ +/*************************************************************************/ +/* windows_terminal_logger.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "windows_terminal_logger.h" + +#ifdef WINDOWS_ENABLED + +#include <stdio.h> +#include <windows.h> + +void WindowsTerminalLogger::logv(const char *p_format, va_list p_list, bool p_err) { + if (!should_log(p_err)) { + return; + } + + const unsigned int BUFFER_SIZE = 16384; + char buf[BUFFER_SIZE + 1]; // +1 for the terminating character + int len = vsnprintf(buf, BUFFER_SIZE, p_format, p_list); + if (len <= 0) + return; + if (len >= BUFFER_SIZE) + len = BUFFER_SIZE; // Output is too big, will be truncated + buf[len] = 0; + + int wlen = MultiByteToWideChar(CP_UTF8, 0, buf, len, NULL, 0); + if (wlen < 0) + return; + + wchar_t *wbuf = (wchar_t *)malloc((len + 1) * sizeof(wchar_t)); + MultiByteToWideChar(CP_UTF8, 0, buf, len, wbuf, wlen); + wbuf[wlen] = 0; + + if (p_err) + fwprintf(stderr, L"%ls", wbuf); + else + wprintf(L"%ls", wbuf); + + free(wbuf); + +#ifdef DEBUG_ENABLED + fflush(stdout); +#endif +} + +void WindowsTerminalLogger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type) { + if (!should_log(true)) { + return; + } + +#ifndef UWP_ENABLED + HANDLE hCon = GetStdHandle(STD_OUTPUT_HANDLE); + if (!hCon || hCon == INVALID_HANDLE_VALUE) { +#endif + StdLogger::log_error(p_function, p_file, p_line, p_code, p_rationale, p_type); +#ifndef UWP_ENABLED + } else { + + CONSOLE_SCREEN_BUFFER_INFO sbi; //original + GetConsoleScreenBufferInfo(hCon, &sbi); + + WORD current_fg = sbi.wAttributes & (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY); + WORD current_bg = sbi.wAttributes & (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY); + + uint32_t basecol = 0; + switch (p_type) { + case ERR_ERROR: basecol = FOREGROUND_RED; break; + case ERR_WARNING: basecol = FOREGROUND_RED | FOREGROUND_GREEN; break; + case ERR_SCRIPT: basecol = FOREGROUND_RED | FOREGROUND_BLUE; break; + case ERR_SHADER: basecol = FOREGROUND_GREEN | FOREGROUND_BLUE; break; + } + + basecol |= current_bg; + + if (p_rationale && p_rationale[0]) { + + SetConsoleTextAttribute(hCon, basecol | FOREGROUND_INTENSITY); + switch (p_type) { + case ERR_ERROR: logf("ERROR: "); break; + case ERR_WARNING: logf("WARNING: "); break; + case ERR_SCRIPT: logf("SCRIPT ERROR: "); break; + case ERR_SHADER: logf("SHADER ERROR: "); break; + } + + SetConsoleTextAttribute(hCon, current_fg | current_bg | FOREGROUND_INTENSITY); + logf("%s\n", p_rationale); + + SetConsoleTextAttribute(hCon, basecol); + switch (p_type) { + case ERR_ERROR: logf(" At: "); break; + case ERR_WARNING: logf(" At: "); break; + case ERR_SCRIPT: logf(" At: "); break; + case ERR_SHADER: logf(" At: "); break; + } + + SetConsoleTextAttribute(hCon, current_fg | current_bg); + logf("%s:%i\n", p_file, p_line); + + } else { + + SetConsoleTextAttribute(hCon, basecol | FOREGROUND_INTENSITY); + switch (p_type) { + case ERR_ERROR: logf("ERROR: %s: ", p_function); break; + case ERR_WARNING: logf("WARNING: %s: ", p_function); break; + case ERR_SCRIPT: logf("SCRIPT ERROR: %s: ", p_function); break; + case ERR_SHADER: logf("SCRIPT ERROR: %s: ", p_function); break; + } + + SetConsoleTextAttribute(hCon, current_fg | current_bg | FOREGROUND_INTENSITY); + logf("%s\n", p_code); + + SetConsoleTextAttribute(hCon, basecol); + switch (p_type) { + case ERR_ERROR: logf(" At: "); break; + case ERR_WARNING: logf(" At: "); break; + case ERR_SCRIPT: logf(" At: "); break; + case ERR_SHADER: logf(" At: "); break; + } + + SetConsoleTextAttribute(hCon, current_fg | current_bg); + logf("%s:%i\n", p_file, p_line); + } + + SetConsoleTextAttribute(hCon, sbi.wAttributes); + } +#endif +} + +WindowsTerminalLogger::~WindowsTerminalLogger() {} + +#endif
\ No newline at end of file diff --git a/platform/windows/windows_terminal_logger.h b/platform/windows/windows_terminal_logger.h new file mode 100644 index 0000000000..f6b1a68d18 --- /dev/null +++ b/platform/windows/windows_terminal_logger.h @@ -0,0 +1,47 @@ +/*************************************************************************/ +/* windows_terminal_logger.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef WINDOWS_TERMINAL_LOGGER_H +#define WINDOWS_TERMINAL_LOGGER_H + +#ifdef WINDOWS_ENABLED + +#include "io/logger.h" + +class WindowsTerminalLogger : public StdLogger { +public: + virtual void logv(const char *p_format, va_list p_list, bool p_err); + virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type = ERR_ERROR); + virtual ~WindowsTerminalLogger(); +}; + +#endif + +#endif
\ No newline at end of file diff --git a/platform/x11/SCsub b/platform/x11/SCsub index fc9208c563..aabc49149f 100644 --- a/platform/x11/SCsub +++ b/platform/x11/SCsub @@ -1,14 +1,22 @@ #!/usr/bin/env python +import os Import('env') +def make_debug(target, source, env): + os.system('objcopy --only-keep-debug %s %s.debug' % (target[0], target[0])) + os.system('strip --strip-debug --strip-unneeded %s' % (target[0])) + os.system('objcopy --add-gnu-debuglink=%s.debug %s' % (target[0], target[0])) -common_x11 = [\ - "context_gl_x11.cpp",\ - "os_x11.cpp",\ - "key_mapping_x11.cpp",\ - "joypad_linux.cpp",\ - "power_x11.cpp",\ +common_x11 = [ + "context_gl_x11.cpp", + "crash_handler_x11.cpp", + "os_x11.cpp", + "key_mapping_x11.cpp", + "joypad_linux.cpp", + "power_x11.cpp", ] -env.Program('#bin/godot', ['godot_x11.cpp'] + common_x11) +binary = env.Program('#bin/godot', ['godot_x11.cpp'] + common_x11) +if env["debug_symbols"] == "full" or env["debug_symbols"] == "yes": + env.AddPostAction(binary, make_debug) diff --git a/platform/x11/context_gl_x11.cpp b/platform/x11/context_gl_x11.cpp index 0cc9734119..4f9d4a84b9 100644 --- a/platform/x11/context_gl_x11.cpp +++ b/platform/x11/context_gl_x11.cpp @@ -30,7 +30,7 @@ #include "context_gl_x11.h" #ifdef X11_ENABLED -#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED) +#if defined(OPENGL_ENABLED) #include <stdio.h> #include <stdlib.h> #include <unistd.h> diff --git a/platform/x11/context_gl_x11.h b/platform/x11/context_gl_x11.h index ba01b51d59..c37bac5e9b 100644 --- a/platform/x11/context_gl_x11.h +++ b/platform/x11/context_gl_x11.h @@ -35,7 +35,7 @@ */ #ifdef X11_ENABLED -#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED) +#if defined(OPENGL_ENABLED) #include "drivers/gl_context/context_gl.h" #include "os/os.h" diff --git a/platform/x11/crash_handler_x11.cpp b/platform/x11/crash_handler_x11.cpp new file mode 100644 index 0000000000..3c54d5cbc2 --- /dev/null +++ b/platform/x11/crash_handler_x11.cpp @@ -0,0 +1,136 @@ +/*************************************************************************/ +/* crash_handler_x11.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifdef DEBUG_ENABLED +#define CRASH_HANDLER_ENABLED 1 +#endif + +#include "main/main.h" +#include "os_x11.h" + +#ifdef CRASH_HANDLER_ENABLED +#include <cxxabi.h> +#include <dlfcn.h> +#include <execinfo.h> +#include <signal.h> +#include <stdlib.h> + +static void handle_crash(int sig) { + if (OS::get_singleton() == NULL) + return; + + void *bt_buffer[256]; + size_t size = backtrace(bt_buffer, 256); + String _execpath = OS::get_singleton()->get_executable_path(); + String msg = GLOBAL_GET("debug/settings/crash_handler/message"); + + // Dump the backtrace to stderr with a message to the user + fprintf(stderr, "%s: Program crashed with signal %d\n", __FUNCTION__, sig); + fprintf(stderr, "Dumping the backtrace. %ls\n", msg.c_str()); + char **strings = backtrace_symbols(bt_buffer, size); + if (strings) { + for (size_t i = 1; i < size; i++) { + char fname[1024]; + Dl_info info; + + snprintf(fname, 1024, "%s", strings[i]); + + // Try to demangle the function name to provide a more readable one + if (dladdr(bt_buffer[i], &info) && info.dli_sname) { + if (info.dli_sname[0] == '_') { + int status; + char *demangled = abi::__cxa_demangle(info.dli_sname, NULL, 0, &status); + + if (status == 0 && demangled) { + snprintf(fname, 1024, "%s", demangled); + } + + if (demangled) + free(demangled); + } + } + + List<String> args; + + char str[1024]; + snprintf(str, 1024, "%p", bt_buffer[i]); + args.push_back(str); + args.push_back("-e"); + args.push_back(_execpath); + + String output = ""; + + // Try to get the file/line number using addr2line + if (OS::get_singleton()) { + int ret; + Error err = OS::get_singleton()->execute(String("addr2line"), args, true, NULL, &output, &ret); + if (err == OK) { + output.erase(output.length() - 1, 1); + } + } + + fprintf(stderr, "[%ld] %s (%ls)\n", i, fname, output.c_str()); + } + + free(strings); + } + fprintf(stderr, "-- END OF BACKTRACE --\n"); + + // Abort to pass the error to the OS + abort(); +} +#endif + +CrashHandler::CrashHandler() { + disabled = false; +} + +CrashHandler::~CrashHandler() { +} + +void CrashHandler::disable() { + if (disabled) + return; + +#ifdef CRASH_HANDLER_ENABLED + signal(SIGSEGV, NULL); + signal(SIGFPE, NULL); + signal(SIGILL, NULL); +#endif + + disabled = true; +} + +void CrashHandler::initialize() { +#ifdef CRASH_HANDLER_ENABLED + signal(SIGSEGV, handle_crash); + signal(SIGFPE, handle_crash); + signal(SIGILL, handle_crash); +#endif +} diff --git a/platform/x11/crash_handler_x11.h b/platform/x11/crash_handler_x11.h new file mode 100644 index 0000000000..e01334cbf2 --- /dev/null +++ b/platform/x11/crash_handler_x11.h @@ -0,0 +1,47 @@ +/*************************************************************************/ +/* crash_handler_x11.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef CRASH_HANDLER_X11_H +#define CRASH_HANDLER_X11_H + +class CrashHandler { + + bool disabled; + +public: + void initialize(); + + void disable(); + bool is_disabled() const { return disabled; }; + + CrashHandler(); + ~CrashHandler(); +}; + +#endif diff --git a/platform/x11/detect.py b/platform/x11/detect.py index 79778136ad..56bc1d4c59 100644 --- a/platform/x11/detect.py +++ b/platform/x11/detect.py @@ -44,28 +44,28 @@ def can_build(): return True - def get_opts(): + from SCons.Variables import BoolVariable, EnumVariable return [ - ('use_llvm', 'Use the LLVM compiler', 'no'), - ('use_static_cpp', 'Link stdc++ statically', 'no'), - ('use_sanitizer', 'Use LLVM compiler address sanitizer', 'no'), - ('use_leak_sanitizer', 'Use LLVM compiler memory leaks sanitizer (implies use_sanitizer)', 'no'), - ('use_lto', 'Use link time optimization', 'no'), - ('pulseaudio', 'Detect & use pulseaudio', 'yes'), - ('udev', 'Use udev for gamepad connection callbacks', 'no'), - ('debug_release', 'Add debug symbols to release version', 'no'), + BoolVariable('use_llvm', 'Use the LLVM compiler', False), + BoolVariable('use_static_cpp', 'Link stdc++ statically', False), + BoolVariable('use_sanitizer', 'Use LLVM compiler address sanitizer', False), + BoolVariable('use_leak_sanitizer', 'Use LLVM compiler memory leaks sanitizer (implies use_sanitizer)', False), + BoolVariable('use_lto', 'Use link time optimization', False), + BoolVariable('pulseaudio', 'Detect & use pulseaudio', True), + BoolVariable('udev', 'Use udev for gamepad connection callbacks', False), + EnumVariable('debug_symbols', 'Add debug symbols to release version', 'yes', ('yes', 'no', 'full')), ] def get_flags(): return [ - ('builtin_freetype', 'no'), - ('builtin_libpng', 'no'), - ('builtin_openssl', 'no'), - ('builtin_zlib', 'no'), + ('builtin_freetype', False), + ('builtin_libpng', False), + ('builtin_openssl', False), + ('builtin_zlib', False), ] @@ -74,17 +74,24 @@ def configure(env): ## Build type if (env["target"] == "release"): - env.Prepend(CCFLAGS=['-Ofast']) - if (env["debug_release"] == "yes"): + # -O3 -ffast-math is identical to -Ofast. We need to split it out so we can selectively disable + # -ffast-math in code for which it generates wrong results. + env.Prepend(CCFLAGS=['-O3', '-ffast-math']) + if (env["debug_symbols"] == "yes"): + env.Prepend(CCFLAGS=['-g1']) + if (env["debug_symbols"] == "full"): env.Prepend(CCFLAGS=['-g2']) elif (env["target"] == "release_debug"): env.Prepend(CCFLAGS=['-O2', '-ffast-math', '-DDEBUG_ENABLED']) - if (env["debug_release"] == "yes"): + if (env["debug_symbols"] == "yes"): + env.Prepend(CCFLAGS=['-g1']) + if (env["debug_symbols"] == "full"): env.Prepend(CCFLAGS=['-g2']) elif (env["target"] == "debug"): - env.Prepend(CCFLAGS=['-g2', '-DDEBUG_ENABLED', '-DDEBUG_MEMORY_ENABLED']) + env.Prepend(CCFLAGS=['-g3', '-DDEBUG_ENABLED', '-DDEBUG_MEMORY_ENABLED']) + env.Append(LINKFLAGS=['-rdynamic']) ## Architecture @@ -94,7 +101,11 @@ def configure(env): ## Compiler configuration - if (env["use_llvm"] == "yes"): + if 'CXX' in env and 'clang' in env['CXX']: + # Convenience check to enforce the use_llvm overrides when CXX is clang(++) + env['use_llvm'] = True + + if env['use_llvm']: if ('clang++' not in env['CXX']): env["CC"] = "clang" env["CXX"] = "clang++" @@ -103,17 +114,23 @@ def configure(env): env.extra_suffix = ".llvm" + env.extra_suffix # leak sanitizer requires (address) sanitizer - if (env["use_sanitizer"] == "yes" or env["use_leak_sanitizer"] == "yes"): + if env['use_sanitizer'] or env['use_leak_sanitizer']: env.Append(CCFLAGS=['-fsanitize=address', '-fno-omit-frame-pointer']) env.Append(LINKFLAGS=['-fsanitize=address']) env.extra_suffix += "s" - if (env["use_leak_sanitizer"] == "yes"): + if env['use_leak_sanitizer']: env.Append(CCFLAGS=['-fsanitize=leak']) env.Append(LINKFLAGS=['-fsanitize=leak']) - if (env["use_lto"] == "yes"): + if env['use_lto']: env.Append(CCFLAGS=['-flto']) - env.Append(LINKFLAGS=['-flto']) + if not env['use_llvm'] and env.GetOption("num_jobs") > 1: + env.Append(LINKFLAGS=['-flto=' + str(env.GetOption("num_jobs"))]) + else: + env.Append(LINKFLAGS=['-flto']) + if not env['use_llvm']: + env['RANLIB'] = 'gcc-ranlib' + env['AR'] = 'gcc-ar' env.Append(CCFLAGS=['-pipe']) env.Append(LINKFLAGS=['-pipe']) @@ -127,67 +144,66 @@ def configure(env): # FIXME: Check for existence of the libs before parsing their flags with pkg-config - if (env['builtin_openssl'] == 'no'): - # Currently not compatible with OpenSSL 1.1.0+ - # https://github.com/godotengine/godot/issues/8624 - import subprocess - openssl_version = subprocess.check_output(['pkg-config', 'openssl', '--modversion']).strip('\n') - if (openssl_version >= "1.1.0"): - print("Error: Found system-installed OpenSSL %s, currently only supporting version 1.0.x." % openssl_version) - print("Aborting.. You can compile with 'builtin_openssl=yes' to use the bundled version.\n") - sys.exit(255) - + if not env['builtin_openssl']: env.ParseConfig('pkg-config openssl --cflags --libs') - if (env['builtin_libwebp'] == 'no'): + if not env['builtin_libwebp']: env.ParseConfig('pkg-config libwebp --cflags --libs') # freetype depends on libpng and zlib, so bundling one of them while keeping others # as shared libraries leads to weird issues - if (env['builtin_freetype'] == 'yes' or env['builtin_libpng'] == 'yes' or env['builtin_zlib'] == 'yes'): - env['builtin_freetype'] = 'yes' - env['builtin_libpng'] = 'yes' - env['builtin_zlib'] = 'yes' + if env['builtin_freetype'] or env['builtin_libpng'] or env['builtin_zlib']: + env['builtin_freetype'] = True + env['builtin_libpng'] = True + env['builtin_zlib'] = True - if (env['builtin_freetype'] == 'no'): + if not env['builtin_freetype']: env.ParseConfig('pkg-config freetype2 --cflags --libs') - if (env['builtin_libpng'] == 'no'): + if not env['builtin_libpng']: env.ParseConfig('pkg-config libpng --cflags --libs') - if (env['builtin_enet'] == 'no'): + if not env['builtin_enet']: env.ParseConfig('pkg-config libenet --cflags --libs') - if (env['builtin_squish'] == 'no' and env["tools"] == "yes"): + if not env['builtin_squish'] and env['tools']: env.ParseConfig('pkg-config libsquish --cflags --libs') + if not env['builtin_zstd']: + env.ParseConfig('pkg-config libzstd --cflags --libs') + # Sound and video libraries # Keep the order as it triggers chained dependencies (ogg needed by others, etc.) - if (env['builtin_libtheora'] == 'no'): - env['builtin_libogg'] = 'no' # Needed to link against system libtheora - env['builtin_libvorbis'] = 'no' # Needed to link against system libtheora + if not env['builtin_libtheora']: + env['builtin_libogg'] = False # Needed to link against system libtheora + env['builtin_libvorbis'] = False # Needed to link against system libtheora env.ParseConfig('pkg-config theora theoradec --cflags --libs') - if (env['builtin_libvpx'] == 'no'): + if not env['builtin_libvpx']: env.ParseConfig('pkg-config vpx --cflags --libs') - if (env['builtin_libvorbis'] == 'no'): - env['builtin_libogg'] = 'no' # Needed to link against system libvorbis + if not env['builtin_libvorbis']: + env['builtin_libogg'] = False # Needed to link against system libvorbis env.ParseConfig('pkg-config vorbis vorbisfile --cflags --libs') - if (env['builtin_opus'] == 'no'): - env['builtin_libogg'] = 'no' # Needed to link against system opus + if not env['builtin_opus']: + env['builtin_libogg'] = False # Needed to link against system opus env.ParseConfig('pkg-config opus opusfile --cflags --libs') - if (env['builtin_libogg'] == 'no'): + if not env['builtin_libogg']: env.ParseConfig('pkg-config ogg --cflags --libs') - if (env['builtin_libtheora'] != 'no'): + if env['builtin_libtheora']: list_of_x86 = ['x86_64', 'x86', 'i386', 'i586'] if any(platform.machine() in s for s in list_of_x86): env["x86_libtheora_opt_gcc"] = True + # On Linux wchar_t should be 32-bits + # 16-bit library shouldn't be required due to compiler optimisations + if not env['builtin_pcre2']: + env.ParseConfig('pkg-config libpcre2-32 --cflags --libs') + ## Flags if (os.system("pkg-config --exists alsa") == 0): # 0 means found @@ -197,7 +213,7 @@ def configure(env): else: print("ALSA libraries not found, disabling driver") - if (env["pulseaudio"] == "yes"): + if env['pulseaudio']: if (os.system("pkg-config --exists libpulse-simple") == 0): # 0 means found print("Enabling PulseAudio") env.Append(CPPFLAGS=["-DPULSEAUDIO_ENABLED"]) @@ -208,7 +224,7 @@ def configure(env): if (platform.system() == "Linux"): env.Append(CPPFLAGS=["-DJOYDEV_ENABLED"]) - if (env["udev"] == "yes"): + if env['udev']: if (os.system("pkg-config --exists libudev") == 0): # 0 means found print("Enabling udev support") env.Append(CPPFLAGS=["-DUDEV_ENABLED"]) @@ -217,7 +233,7 @@ def configure(env): print("libudev development libraries not found, disabling udev support") # Linkflags below this line should typically stay the last ones - if (env['builtin_zlib'] == 'no'): + if not env['builtin_zlib']: env.ParseConfig('pkg-config zlib --cflags --libs') env.Append(CPPPATH=['#platform/x11']) @@ -227,6 +243,9 @@ def configure(env): if (platform.system() == "Linux"): env.Append(LIBS=['dl']) + if (platform.system().find("BSD") >= 0): + env.Append(LIBS=['execinfo']) + ## Cross-compilation if (is64 and env["bits"] == "32"): @@ -236,5 +255,5 @@ def configure(env): env.Append(CPPFLAGS=['-m64']) env.Append(LINKFLAGS=['-m64', '-L/usr/lib/i686-linux-gnu']) - if (env["use_static_cpp"] == "yes"): + if env['use_static_cpp']: env.Append(LINKFLAGS=['-static-libstdc++']) diff --git a/platform/x11/export/export.cpp b/platform/x11/export/export.cpp index 59b1a44247..fdb43c9ae0 100644 --- a/platform/x11/export/export.cpp +++ b/platform/x11/export/export.cpp @@ -50,6 +50,7 @@ void register_x11_exporter() { platform->set_release_64("linux_x11_64_release"); platform->set_debug_64("linux_x11_64_debug"); platform->set_os_name("X11"); + platform->set_chmod_flags(0755); EditorExport::get_singleton()->add_export_platform(platform); } diff --git a/platform/x11/joypad_linux.cpp b/platform/x11/joypad_linux.cpp index 3453297716..428385f7cb 100644 --- a/platform/x11/joypad_linux.cpp +++ b/platform/x11/joypad_linux.cpp @@ -125,7 +125,6 @@ void JoypadLinux::enumerate_joypads(udev *p_udev) { enumerate = udev_enumerate_new(p_udev); udev_enumerate_add_match_subsystem(enumerate, "input"); - udev_enumerate_add_match_property(enumerate, "ID_INPUT_JOYPAD", "1"); udev_enumerate_scan_devices(enumerate); devices = udev_enumerate_get_list_entry(enumerate); diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index 2d33d25773..09193e0a2b 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -35,6 +35,11 @@ #include "servers/physics/physics_server_sw.h" #include "servers/visual/visual_server_raster.h" #include "servers/visual/visual_server_wrap_mt.h" + +#ifdef HAVE_MNTENT +#include <mntent.h> +#endif + #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -93,6 +98,13 @@ const char *OS_X11::get_audio_driver_name(int p_driver) const { return AudioDriverManager::get_driver(p_driver)->get_name(); } +void OS_X11::initialize_core() { + + crash_handler.initialize(); + + OS_Unix::initialize_core(); +} + void OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { long im_event_mask = 0; @@ -227,7 +239,7 @@ void OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_au // maybe contextgl wants to be in charge of creating the window //print_line("def videomode "+itos(current_videomode.width)+","+itos(current_videomode.height)); -#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED) +#if defined(OPENGL_ENABLED) context_gl = memnew(ContextGL_X11(x11_display, x11_window, current_videomode, true)); context_gl->initialize(); @@ -246,6 +258,11 @@ void OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_au // borderless fullscreen window mode if (current_videomode.fullscreen) { + // set bypass compositor hint + Atom bypass_compositor = XInternAtom(x11_display, "_NET_WM_BYPASS_COMPOSITOR", False); + unsigned long compositing_disable_on = 1; + XChangeProperty(x11_display, x11_window, bypass_compositor, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&compositing_disable_on, 1); + // needed for lxde/openbox, possibly others Hints hints; Atom property; @@ -301,29 +318,7 @@ void OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_au XFree(xsh); } - AudioDriverManager::get_driver(p_audio_driver)->set_singleton(); - - audio_driver_index = p_audio_driver; - if (AudioDriverManager::get_driver(p_audio_driver)->init() != OK) { - - bool success = false; - audio_driver_index = -1; - for (int i = 0; i < AudioDriverManager::get_driver_count(); i++) { - if (i == p_audio_driver) - continue; - AudioDriverManager::get_driver(i)->set_singleton(); - if (AudioDriverManager::get_driver(i)->init() == OK) { - success = true; - print_line("Audio Driver Failed: " + String(AudioDriverManager::get_driver(p_audio_driver)->get_name())); - print_line("Using alternate audio driver: " + String(AudioDriverManager::get_driver(i)->get_name())); - audio_driver_index = i; - break; - } - } - if (!success) { - ERR_PRINT("Initializing audio failed."); - } - } + AudioDriverManager::initialize(p_audio_driver); ERR_FAIL_COND(!visual_server); ERR_FAIL_COND(x11_window == 0); @@ -542,7 +537,7 @@ void OS_X11::finalize() { XUnmapWindow(x11_display, x11_window); XDestroyWindow(x11_display, x11_window); -#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED) +#if defined(OPENGL_ENABLED) memdelete(context_gl); #endif for (int i = 0; i < CURSOR_MAX; i++) { @@ -623,7 +618,7 @@ void OS_X11::set_mouse_mode(MouseMode p_mode) { XFlush(x11_display); } -void OS_X11::warp_mouse_pos(const Point2 &p_to) { +void OS_X11::warp_mouse_position(const Point2 &p_to) { if (mouse_mode == MOUSE_MODE_CAPTURED) { @@ -695,6 +690,12 @@ void OS_X11::set_wm_fullscreen(bool p_enabled) { xev.xclient.data.l[2] = 0; XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); + + // set bypass compositor hint + Atom bypass_compositor = XInternAtom(x11_display, "_NET_WM_BYPASS_COMPOSITOR", False); + unsigned long compositing_disable_on = p_enabled ? 1 : 0; + XChangeProperty(x11_display, x11_window, bypass_compositor, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&compositing_disable_on, 1); + XFlush(x11_display); if (!p_enabled && !is_window_resizable()) { @@ -712,6 +713,16 @@ void OS_X11::set_wm_fullscreen(bool p_enabled) { XSetWMNormalHints(x11_display, x11_window, xsh); XFree(xsh); } + + if (!p_enabled && !get_borderless_window()) { + // put decorations back if the window wasn't suppoesed to be borderless + Hints hints; + Atom property; + hints.flags = 2; + hints.decorations = 1; + property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True); + XChangeProperty(x11_display, x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5); + } } int OS_X11::get_screen_count() const { @@ -1237,7 +1248,7 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) { unsigned int keycode = KeyMappingX11::get_keycode(keysym_keycode); - /* Phase 3, obtain an unicode character from the keysym */ + /* Phase 3, obtain a unicode character from the keysym */ // KeyMappingX11 also translates keysym to unicode. // It does a binary search on a table to translate @@ -1932,7 +1943,7 @@ Error OS_X11::shell_open(String p_uri) { Error ok; List<String> args; args.push_back(p_uri); - ok = execute("/usr/bin/xdg-open", args, false); + ok = execute("xdg-open", args, false); if (ok == OK) return OK; ok = execute("gnome-open", args, false); @@ -1996,7 +2007,7 @@ String OS_X11::get_system_dir(SystemDir p_dir) const { String pipe; List<String> arg; arg.push_back(xdgparam); - Error err = const_cast<OS_X11 *>(this)->execute("/usr/bin/xdg-user-dir", arg, true, NULL, &pipe); + Error err = const_cast<OS_X11 *>(this)->execute("xdg-user-dir", arg, true, NULL, &pipe); if (err != OK) return "."; return pipe.strip_edges(); @@ -2046,7 +2057,7 @@ void OS_X11::alert(const String &p_alert, const String &p_title) { args.push_back(p_title); args.push_back(p_alert); - execute("/usr/bin/xmessage", args, true); + execute("xmessage", args, true); } void OS_X11::set_icon(const Ref<Image> &p_icon) { @@ -2149,7 +2160,7 @@ void OS_X11::set_context(int p_context) { } } -PowerState OS_X11::get_power_state() { +OS::PowerState OS_X11::get_power_state() { return power_manager->get_power_state(); } @@ -2161,11 +2172,120 @@ int OS_X11::get_power_percent_left() { return power_manager->get_power_percent_left(); } -OS_X11::OS_X11() { +void OS_X11::disable_crash_handler() { + crash_handler.disable(); +} + +bool OS_X11::is_disable_crash_handler() const { + return crash_handler.is_disabled(); +} + +static String get_mountpoint(const String &p_path) { + struct stat s; + if (stat(p_path.utf8().get_data(), &s)) { + return ""; + } + +#ifdef HAVE_MNTENT + dev_t dev = s.st_dev; + FILE *fd = setmntent("/proc/mounts", "r"); + if (!fd) { + return ""; + } + + struct mntent mnt; + char buf[1024]; + size_t buflen = 1024; + while (getmntent_r(fd, &mnt, buf, buflen)) { + if (!stat(mnt.mnt_dir, &s) && s.st_dev == dev) { + endmntent(fd); + return String(mnt.mnt_dir); + } + } -#ifdef RTAUDIO_ENABLED - AudioDriverManager::add_driver(&driver_rtaudio); + endmntent(fd); #endif + return ""; +} + +Error OS_X11::move_to_trash(const String &p_path) { + String trashcan = ""; + String mnt = get_mountpoint(p_path); + + if (mnt != "") { + String path(mnt + "/.Trash-" + itos(getuid()) + "/files"); + struct stat s; + if (!stat(path.utf8().get_data(), &s)) { + trashcan = path; + } + } + + if (trashcan == "") { + char *dhome = getenv("XDG_DATA_HOME"); + if (dhome) { + trashcan = String(dhome) + "/Trash/files"; + } + } + + if (trashcan == "") { + char *home = getenv("HOME"); + if (home) { + trashcan = String(home) + "/.local/share/Trash/files"; + } + } + + if (trashcan == "") { + ERR_PRINTS("move_to_trash: Could not determine trashcan location"); + return FAILED; + } + + List<String> args; + args.push_back("-p"); + args.push_back(trashcan); + Error err = execute("mkdir", args, true); + if (err == OK) { + List<String> args2; + args2.push_back(p_path); + args2.push_back(trashcan); + err = execute("mv", args2, true); + } + + return err; +} + +OS::LatinKeyboardVariant OS_X11::get_latin_keyboard_variant() const { + + XkbDescRec *xkbdesc = XkbAllocKeyboard(); + ERR_FAIL_COND_V(!xkbdesc, LATIN_KEYBOARD_QWERTY); + + XkbGetNames(x11_display, XkbSymbolsNameMask, xkbdesc); + ERR_FAIL_COND_V(!xkbdesc->names, LATIN_KEYBOARD_QWERTY); + ERR_FAIL_COND_V(!xkbdesc->names->symbols, LATIN_KEYBOARD_QWERTY); + + char *layout = XGetAtomName(x11_display, xkbdesc->names->symbols); + ERR_FAIL_COND_V(!layout, LATIN_KEYBOARD_QWERTY); + + Vector<String> info = String(layout).split("+"); + ERR_FAIL_INDEX_V(1, info.size(), LATIN_KEYBOARD_QWERTY); + + if (info[1].find("colemak") != -1) { + return LATIN_KEYBOARD_COLEMAK; + } else if (info[1].find("qwertz") != -1) { + return LATIN_KEYBOARD_QWERTZ; + } else if (info[1].find("azerty") != -1) { + return LATIN_KEYBOARD_AZERTY; + } else if (info[1].find("qzerty") != -1) { + return LATIN_KEYBOARD_QZERTY; + } else if (info[1].find("dvorak") != -1) { + return LATIN_KEYBOARD_DVORAK; + } else if (info[1].find("neo") != -1) { + return LATIN_KEYBOARD_NEO; + } + + return LATIN_KEYBOARD_QWERTY; +} + +OS_X11::OS_X11() { #ifdef PULSEAUDIO_ENABLED AudioDriverManager::add_driver(&driver_pulseaudio); @@ -2175,11 +2295,6 @@ OS_X11::OS_X11() { AudioDriverManager::add_driver(&driver_alsa); #endif - if (AudioDriverManager::get_driver_count() == 0) { - WARN_PRINT("No sound driver found... Defaulting to dummy driver"); - AudioDriverManager::add_driver(&driver_dummy); - } - minimized = false; xim_style = 0L; mouse_mode = MOUSE_MODE_VISIBLE; diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h index 51240fa023..b71b456d49 100644 --- a/platform/x11/os_x11.h +++ b/platform/x11/os_x11.h @@ -31,17 +31,16 @@ #define OS_X11_H #include "context_gl_x11.h" +#include "crash_handler_x11.h" #include "drivers/unix/os_unix.h" #include "os/input.h" #include "servers/visual_server.h" //#include "servers/visual/visual_server_wrap_mt.h" #include "drivers/alsa/audio_driver_alsa.h" #include "drivers/pulseaudio/audio_driver_pulseaudio.h" -#include "drivers/rtaudio/audio_driver_rtaudio.h" #include "joypad_linux.h" #include "main/input_default.h" #include "power_x11.h" -#include "servers/audio/audio_driver_dummy.h" #include "servers/audio_server.h" #include "servers/physics_2d/physics_2d_server_sw.h" #include "servers/physics_2d/physics_2d_server_wrap_mt.h" @@ -95,7 +94,7 @@ class OS_X11 : public OS_Unix { int xdnd_version; -#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED) +#if defined(OPENGL_ENABLED) ContextGL_X11 *context_gl; #endif //Rasterizer *rasterizer; @@ -153,10 +152,6 @@ class OS_X11 : public OS_Unix { JoypadLinux *joypad; #endif -#ifdef RTAUDIO_ENABLED - AudioDriverRtAudio driver_rtaudio; -#endif - #ifdef ALSA_ENABLED AudioDriverALSA driver_alsa; #endif @@ -164,12 +159,13 @@ class OS_X11 : public OS_Unix { #ifdef PULSEAUDIO_ENABLED AudioDriverPulseAudio driver_pulseaudio; #endif - AudioDriverDummy driver_dummy; Atom net_wm_icon; PowerX11 *power_manager; + CrashHandler crash_handler; + int audio_driver_index; unsigned int capture_idle; bool maximized; @@ -191,6 +187,7 @@ protected: virtual int get_audio_driver_count() const; virtual const char *get_audio_driver_name(int p_driver) const; + virtual void initialize_core(); virtual void initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver); virtual void finalize(); @@ -204,7 +201,7 @@ public: void set_mouse_mode(MouseMode p_mode); MouseMode get_mouse_mode() const; - virtual void warp_mouse_pos(const Point2 &p_to); + virtual void warp_mouse_position(const Point2 &p_to); virtual Point2 get_mouse_position() const; virtual int get_mouse_button_state() const; virtual void set_window_title(const String &p_title); @@ -265,7 +262,7 @@ public: virtual void set_use_vsync(bool p_enable); virtual bool is_vsync_enabled() const; - virtual PowerState get_power_state(); + virtual OS::PowerState get_power_state(); virtual int get_power_seconds_left(); virtual int get_power_percent_left(); @@ -273,6 +270,13 @@ public: void run(); + void disable_crash_handler(); + bool is_disable_crash_handler() const; + + virtual Error move_to_trash(const String &p_path); + + virtual LatinKeyboardVariant get_latin_keyboard_variant() const; + OS_X11(); }; diff --git a/platform/x11/power_x11.cpp b/platform/x11/power_x11.cpp index 32100354a6..76ff7f91fb 100644 --- a/platform/x11/power_x11.cpp +++ b/platform/x11/power_x11.cpp @@ -252,7 +252,7 @@ bool PowerX11::GetPowerInfo_Linux_proc_acpi() { this->nsecs_left = -1; this->percent_left = -1; - this->power_state = POWERSTATE_UNKNOWN; + this->power_state = OS::POWERSTATE_UNKNOWN; dirp->change_dir(proc_acpi_battery_path); Error err = dirp->list_dir_begin(); @@ -282,13 +282,13 @@ bool PowerX11::GetPowerInfo_Linux_proc_acpi() { } if (!have_battery) { - this->power_state = POWERSTATE_NO_BATTERY; + this->power_state = OS::POWERSTATE_NO_BATTERY; } else if (charging) { - this->power_state = POWERSTATE_CHARGING; + this->power_state = OS::POWERSTATE_CHARGING; } else if (have_ac) { - this->power_state = POWERSTATE_CHARGED; + this->power_state = OS::POWERSTATE_CHARGED; } else { - this->power_state = POWERSTATE_ON_BATTERY; + this->power_state = OS::POWERSTATE_ON_BATTERY; } return true; /* definitive answer. */ @@ -400,17 +400,17 @@ bool PowerX11::GetPowerInfo_Linux_proc_apm() { } if (battery_flag == 0xFF) { /* unknown state */ - this->power_state = POWERSTATE_UNKNOWN; + this->power_state = OS::POWERSTATE_UNKNOWN; } else if (battery_flag & (1 << 7)) { /* no battery */ - this->power_state = POWERSTATE_NO_BATTERY; + this->power_state = OS::POWERSTATE_NO_BATTERY; } else if (battery_flag & (1 << 3)) { /* charging */ - this->power_state = POWERSTATE_CHARGING; + this->power_state = OS::POWERSTATE_CHARGING; need_details = true; } else if (ac_status == 1) { - this->power_state = POWERSTATE_CHARGED; /* on AC, not charging. */ + this->power_state = OS::POWERSTATE_CHARGED; /* on AC, not charging. */ need_details = true; } else { - this->power_state = POWERSTATE_ON_BATTERY; + this->power_state = OS::POWERSTATE_ON_BATTERY; need_details = true; } @@ -445,7 +445,7 @@ bool PowerX11::GetPowerInfo_Linux_sys_class_power_supply(/*PowerState *state, in return false; } - this->power_state = POWERSTATE_NO_BATTERY; /* assume we're just plugged in. */ + this->power_state = OS::POWERSTATE_NO_BATTERY; /* assume we're just plugged in. */ this->nsecs_left = -1; this->percent_left = -1; @@ -454,7 +454,7 @@ bool PowerX11::GetPowerInfo_Linux_sys_class_power_supply(/*PowerState *state, in while (name != "") { bool choose = false; char str[64]; - PowerState st; + OS::PowerState st; int secs; int pct; @@ -475,17 +475,17 @@ bool PowerX11::GetPowerInfo_Linux_sys_class_power_supply(/*PowerState *state, in /* some drivers don't offer this, so if it's not explicitly reported assume it's present. */ if (read_power_file(base, name.utf8().get_data(), "present", str, sizeof(str)) && (String(str) == "0\n")) { - st = POWERSTATE_NO_BATTERY; + st = OS::POWERSTATE_NO_BATTERY; } else if (!read_power_file(base, name.utf8().get_data(), "status", str, sizeof(str))) { - st = POWERSTATE_UNKNOWN; /* uh oh */ + st = OS::POWERSTATE_UNKNOWN; /* uh oh */ } else if (String(str) == "Charging\n") { - st = POWERSTATE_CHARGING; + st = OS::POWERSTATE_CHARGING; } else if (String(str) == "Discharging\n") { - st = POWERSTATE_ON_BATTERY; + st = OS::POWERSTATE_ON_BATTERY; } else if ((String(str) == "Full\n") || (String(str) == "Not charging\n")) { - st = POWERSTATE_CHARGED; + st = OS::POWERSTATE_CHARGED; } else { - st = POWERSTATE_UNKNOWN; /* uh oh */ + st = OS::POWERSTATE_UNKNOWN; /* uh oh */ } if (!read_power_file(base, name.utf8().get_data(), "capacity", str, sizeof(str))) { @@ -543,17 +543,17 @@ bool PowerX11::UpdatePowerInfo() { } PowerX11::PowerX11() - : nsecs_left(-1), percent_left(-1), power_state(POWERSTATE_UNKNOWN) { + : nsecs_left(-1), percent_left(-1), power_state(OS::POWERSTATE_UNKNOWN) { } PowerX11::~PowerX11() { } -PowerState PowerX11::get_power_state() { +OS::PowerState PowerX11::get_power_state() { if (UpdatePowerInfo()) { return power_state; } else { - return POWERSTATE_UNKNOWN; + return OS::POWERSTATE_UNKNOWN; } } diff --git a/platform/x11/power_x11.h b/platform/x11/power_x11.h index e34223036d..7fc258bc0d 100644 --- a/platform/x11/power_x11.h +++ b/platform/x11/power_x11.h @@ -33,14 +33,14 @@ #include "os/dir_access.h" #include "os/file_access.h" -#include "os/power.h" +#include "os/os.h" class PowerX11 { private: int nsecs_left; int percent_left; - PowerState power_state; + OS::PowerState power_state; FileAccessRef open_power_file(const char *base, const char *node, const char *key); bool read_power_file(const char *base, const char *node, const char *key, char *buf, size_t buflen); @@ -58,7 +58,7 @@ public: PowerX11(); virtual ~PowerX11(); - PowerState get_power_state(); + OS::PowerState get_power_state(); int get_power_seconds_left(); int get_power_percent_left(); }; |