diff options
Diffstat (limited to 'platform')
51 files changed, 2694 insertions, 2691 deletions
diff --git a/platform/android/audio_driver_jandroid.cpp b/platform/android/audio_driver_jandroid.cpp index 7b2127a21..d293f3ed3 100644 --- a/platform/android/audio_driver_jandroid.cpp +++ b/platform/android/audio_driver_jandroid.cpp @@ -29,8 +29,8 @@ /*************************************************************************/ #include "audio_driver_jandroid.h" -#include "global_config.h" #include "os/os.h" +#include "project_settings.h" #include "thread_jandroid.h" #ifndef ANDROID_NATIVE_ACTIVITY diff --git a/platform/android/detect.py b/platform/android/detect.py index ce44ffbf7..fae1df3f2 100644 --- a/platform/android/detect.py +++ b/platform/android/detect.py @@ -14,22 +14,17 @@ def get_name(): def can_build(): - import os - if (not os.environ.has_key("ANDROID_NDK_ROOT")): - return False - - return True + return (os.environ.has_key("ANDROID_NDK_ROOT")) def get_opts(): return [ - ('ANDROID_NDK_ROOT', 'the path to Android NDK', - os.environ.get("ANDROID_NDK_ROOT", 0)), - ('ndk_platform', 'compile for platform: (android-<api> , example: android-18)', "android-18"), - ('android_arch', 'select compiler architecture: (armv7/armv6/x86)', "armv7"), - ('android_neon', 'enable neon (armv7 only)', "yes"), - ('android_stl', 'enable STL support in android port (for modules)', "no") + ('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/x86)', "armv7"), + ('android_neon', 'Enable NEON support (armv7 only)', "yes"), + ('android_stl', 'Enable Android STL support (for modules)', "no") ] @@ -41,6 +36,7 @@ def get_flags(): def create(env): + tools = env['TOOLS'] if "mingw" in tools: tools.remove('mingw') @@ -54,7 +50,6 @@ def configure(env): # Workaround for MinGW. See: # http://www.scons.org/wiki/LongCmdLinesOnWin32 - import os if (os.name == "nt"): import subprocess @@ -92,35 +87,29 @@ def configure(env): env['SPAWN'] = mySpawn - ndk_platform = env['ndk_platform'] + ## Architecture if env['android_arch'] not in ['armv7', 'armv6', 'x86']: env['android_arch'] = 'armv7' - if env['android_arch'] == 'x86': - env["x86_libtheora_opt_gcc"] = True - - if env['PLATFORM'] == 'win32': - env.Tool('gcc') - env['SHLIBSUFFIX'] = '.so' - neon_text = "" if env["android_arch"] == "armv7" and env['android_neon'] == 'yes': - neon_text = " (with neon)" - print("Godot Android!!!!! (" + env['android_arch'] + ")" + neon_text) - - env.Append(CPPPATH=['#platform/android']) + neon_text = " (with NEON)" + print("Building for Android (" + env['android_arch'] + ")" + neon_text) + can_vectorize = True if env['android_arch'] == 'x86': env.extra_suffix = ".x86" + env.extra_suffix target_subpath = "x86-4.9" abi_subpath = "i686-linux-android" arch_subpath = "x86" + env["x86_libtheora_opt_gcc"] = True elif env['android_arch'] == 'armv6': env.extra_suffix = ".armv6" + env.extra_suffix target_subpath = "arm-linux-androideabi-4.9" abi_subpath = "arm-linux-androideabi" arch_subpath = "armeabi" + can_vectorize = False elif env["android_arch"] == "armv7": target_subpath = "arm-linux-androideabi-4.9" abi_subpath = "arm-linux-androideabi" @@ -130,6 +119,28 @@ def configure(env): else: env.extra_suffix = ".armv7" + env.extra_suffix + ## Build type + + if (env["target"].startswith("release")): + env.Append(LINKFLAGS=['-O2']) + env.Append(CPPFLAGS=['-O2', '-DNDEBUG', '-ffast-math', '-funsafe-math-optimizations', '-fomit-frame-pointer']) + if (can_vectorize): + env.Append(CPPFLAGS=['-ftree-vectorize']) + if (env["target"] == "release_debug"): + env.Append(CPPFLAGS=['-DDEBUG_ENABLED']) + elif (env["target"] == "debug"): + env.Append(LINKFLAGS=['-O0']) + env.Append(CPPFLAGS=['-O0', '-D_DEBUG', '-UNDEBUG', '-DDEBUG_ENABLED', + '-DDEBUG_MEMORY_ENABLED', '-g', '-fno-limit-debug-info']) + + ## Compiler configuration + + env['SHLIBSUFFIX'] = '.so' + + if env['PLATFORM'] == 'win32': + env.Tool('gcc') + env.use_windows_spawn_fix() + mt_link = True if (sys.platform.startswith("linux")): host_subpath = "linux-x86_64" @@ -142,10 +153,8 @@ def configure(env): mt_link = False host_subpath = "windows" - compiler_path = env["ANDROID_NDK_ROOT"] + \ - "/toolchains/llvm/prebuilt/" + host_subpath + "/bin" - gcc_toolchain_path = env["ANDROID_NDK_ROOT"] + \ - "/toolchains/" + target_subpath + "/prebuilt/" + host_subpath + compiler_path = env["ANDROID_NDK_ROOT"] + "/toolchains/llvm/prebuilt/" + host_subpath + "/bin" + gcc_toolchain_path = env["ANDROID_NDK_ROOT"] + "/toolchains/" + target_subpath + "/prebuilt/" + host_subpath tools_path = gcc_toolchain_path + "/" + abi_subpath + "/bin" # For Clang to find NDK tools in preference of those system-wide @@ -162,31 +171,28 @@ def configure(env): else: env['ARCH'] = 'arch-arm' - sysroot = env["ANDROID_NDK_ROOT"] + \ - "/platforms/" + ndk_platform + "/" + env['ARCH'] + sysroot = env["ANDROID_NDK_ROOT"] + "/platforms/" + env['ndk_platform'] + "/" + env['ARCH'] common_opts = ['-fno-integrated-as', '-gcc-toolchain', gcc_toolchain_path] + ## Compile flags + env.Append(CPPFLAGS=["-isystem", sysroot + "/usr/include"]) - env.Append(CPPFLAGS=string.split( - '-fpic -ffunction-sections -funwind-tables -fstack-protector-strong -fvisibility=hidden -fno-strict-aliasing')) - env.Append(CPPFLAGS=string.split('-DANDROID -DNO_STATVFS -DGLES2_ENABLED')) + env.Append(CPPFLAGS=string.split('-fpic -ffunction-sections -funwind-tables -fstack-protector-strong -fvisibility=hidden -fno-strict-aliasing')) + env.Append(CPPFLAGS=string.split('-DNO_STATVFS -DGLES2_ENABLED')) env['neon_enabled'] = False if env['android_arch'] == 'x86': - can_vectorize = True target_opts = ['-target', 'i686-none-linux-android'] # The NDK adds this if targeting API < 21, so we can drop it when Godot targets it at least env.Append(CPPFLAGS=['-mstackrealign']) + elif env["android_arch"] == "armv6": - can_vectorize = False target_opts = ['-target', 'armv6-none-linux-androideabi'] - env.Append(CPPFLAGS=string.split( - '-D__ARM_ARCH_6__ -march=armv6 -mfpu=vfp -mfloat-abi=softfp')) + env.Append(CPPFLAGS=string.split('-D__ARM_ARCH_6__ -march=armv6 -mfpu=vfp -mfloat-abi=softfp')) + elif env["android_arch"] == "armv7": - can_vectorize = True target_opts = ['-target', 'armv7-none-linux-androideabi'] - env.Append(CPPFLAGS=string.split( - '-D__ARM_ARCH_7__ -D__ARM_ARCH_7A__ -march=armv7-a -mfloat-abi=softfp')) + env.Append(CPPFLAGS=string.split('-D__ARM_ARCH_7__ -D__ARM_ARCH_7A__ -march=armv7-a -mfloat-abi=softfp')) if env['android_neon'] == 'yes': env['neon_enabled'] = True env.Append(CPPFLAGS=['-mfpu=neon', '-D__ARM_NEON__']) @@ -196,21 +202,20 @@ def configure(env): env.Append(CPPFLAGS=target_opts) env.Append(CPPFLAGS=common_opts) - env.Append(LIBS=['OpenSLES']) - env.Append(LIBS=['EGL', 'OpenSLES', 'android']) - env.Append(LIBS=['log', 'GLESv1_CM', 'GLESv2', 'GLESv3','z']) + if (env['android_stl'] == 'yes'): + 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]) + env.Append(LIBS=["gnustl_static"]) + else: + env.Append(CXXFLAGS=['-fno-rtti', '-fno-exceptions', '-DNO_SAFE_CAST']) - if (sys.platform.startswith("darwin")): - env['SHLIBSUFFIX'] = '.so' + ## Link flags - env['LINKFLAGS'] = ['-shared', '--sysroot=' + - sysroot, '-Wl,--warn-shared-textrel'] - env.Append(LINKFLAGS=string.split( - '-Wl,--fix-cortex-a8')) - env.Append(LINKFLAGS=string.split( - '-Wl,--no-undefined -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now')) - env.Append(LINKFLAGS=string.split( - '-Wl,-soname,libgodot_android.so -Wl,--gc-sections')) + env['LINKFLAGS'] = ['-shared', '--sysroot=' + sysroot, '-Wl,--warn-shared-textrel'] + env.Append(LINKFLAGS=string.split('-Wl,--fix-cortex-a8')) + env.Append(LINKFLAGS=string.split('-Wl,--no-undefined -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now')) + env.Append(LINKFLAGS=string.split('-Wl,-soname,libgodot_android.so -Wl,--gc-sections')) if mt_link: env.Append(LINKFLAGS=['-Wl,--threads']) env.Append(LINKFLAGS=target_opts) @@ -221,45 +226,12 @@ def configure(env): env.Append(LIBPATH=[env["ANDROID_NDK_ROOT"] + '/toolchains/arm-linux-androideabi-4.9/prebuilt/' + host_subpath + '/' + abi_subpath + '/lib']) - if (env["target"].startswith("release")): - env.Append(LINKFLAGS=['-O2']) - env.Append(CPPFLAGS=['-O2', '-DNDEBUG', '-ffast-math', - '-funsafe-math-optimizations', '-fomit-frame-pointer']) - if (can_vectorize): - env.Append(CPPFLAGS=['-ftree-vectorize']) - if (env["target"] == "release_debug"): - env.Append(CPPFLAGS=['-DDEBUG_ENABLED']) - elif (env["target"] == "debug"): - env.Append(LINKFLAGS=['-O0']) - env.Append(CPPFLAGS=['-O0', '-D_DEBUG', '-UNDEBUG', '-DDEBUG_ENABLED', - '-DDEBUG_MEMORY_ENABLED', '-g', '-fno-limit-debug-info']) - - env.Append(CPPFLAGS=['-DANDROID_ENABLED', - '-DUNIX_ENABLED', '-DNO_FCNTL', '-DMPC_FIXED_POINT']) + 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']) # TODO: Move that to opus module's config if("module_opus_enabled" in env and env["module_opus_enabled"] != "no"): if (env["android_arch"] == "armv6" or env["android_arch"] == "armv7"): env.Append(CFLAGS=["-DOPUS_ARM_OPT"]) env.opus_fixed_point = "yes" - - if (env['android_stl'] == 'yes'): - 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]) - env.Append(LIBS=["gnustl_static"]) - else: - env.Append(CXXFLAGS=['-fno-rtti', '-fno-exceptions', '-DNO_SAFE_CAST']) - - import methods - env.Append(BUILDERS={'GLSL120': env.Builder( - action=methods.build_legacygl_headers, suffix='glsl.h', src_suffix='.glsl')}) - env.Append(BUILDERS={'GLSL': env.Builder( - action=methods.build_glsl_headers, suffix='glsl.h', src_suffix='.glsl')}) - env.Append(BUILDERS={'GLSL120GLES': env.Builder( - action=methods.build_gles2_headers, suffix='glsl.h', src_suffix='.glsl')}) - - env.use_windows_spawn_fix() diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index a72e8aa90..a722cd1b8 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -31,13 +31,13 @@ #include "editor/editor_export.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" -#include "global_config.h" #include "io/marshalls.h" #include "io/zip_io.h" #include "os/file_access.h" #include "os/os.h" #include "platform/android/logo.gen.h" #include "platform/android/run_icon.gen.h" +#include "project_settings.h" #include "version.h" #include <string.h> #if 0 @@ -554,9 +554,9 @@ void EditorExportPlatformAndroid::_fix_resources(Vector<uint8_t>& p_manifest) { } else { String lang = str.substr(str.find_last("-")+1,str.length()).replace("-","_"); - String prop = "application/name_"+lang; - if (GlobalConfig::get_singleton()->has(prop)) { - str = GlobalConfig::get_singleton()->get(prop); + String prop = "application/config/name_"+lang; + if (ProjectSettings::get_singleton()->has(prop)) { + str = ProjectSettings::get_singleton()->get(prop); } else { str = get_project_name(); } @@ -628,7 +628,7 @@ String EditorExportPlatformAndroid::get_project_name() const { if (this->name!="") { aname=this->name; } else { - aname = GlobalConfig::get_singleton()->get("application/name"); + aname = ProjectSettings::get_singleton()->get("application/config/name"); } @@ -1144,7 +1144,7 @@ Error EditorExportPlatformAndroid::export_project(const String& p_path, bool p_d if (!found) { - String appicon = GlobalConfig::get_singleton()->get("application/icon"); + String appicon = ProjectSettings::get_singleton()->get("application/config/icon"); if (appicon!="" && appicon.ends_with(".png")) { FileAccess*f = FileAccess::open(appicon,FileAccess::READ); if (f) { @@ -1763,7 +1763,7 @@ Error EditorExportPlatformAndroid::run(int p_device, int p_flags) { String EditorExportPlatformAndroid::get_package_name() { String pname = package; - String basename = GlobalConfig::get_singleton()->get("application/name"); + String basename = ProjectSettings::get_singleton()->get("application/config/name"); basename=basename.to_lower(); String name; @@ -2050,6 +2050,7 @@ class EditorExportAndroid : public EditorExportPlatform { String id; String name; String description; + int release; }; struct APKExportData { @@ -2123,6 +2124,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; } } @@ -2143,6 +2145,7 @@ class EditorExportAndroid : public EditorExportPlatform { String vendor; String device; d.description + "Device ID: " + d.id + "\n"; + d.release = 0; for (int j = 0; j < props.size(); j++) { String p = props[j]; @@ -2153,7 +2156,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=")) { - d.description += "Release: " + p.get_slice("=", 1).strip_edges() + "\n"; + const String release_str = p.get_slice("=", 1).strip_edges(); + d.description += "Release: " + release_str + "\n"; + d.release = release_str.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=")) { @@ -2208,7 +2213,7 @@ class EditorExportAndroid : public EditorExportPlatform { if (p_name != "") { aname = p_name; } else { - aname = GlobalConfig::get_singleton()->get("application/name"); + aname = ProjectSettings::get_singleton()->get("application/config/name"); } if (aname == "") { @@ -2221,7 +2226,7 @@ class EditorExportAndroid : public EditorExportPlatform { String get_package_name(const String &p_package) { String pname = p_package; - String basename = GlobalConfig::get_singleton()->get("application/name"); + String basename = ProjectSettings::get_singleton()->get("application/config/name"); basename = basename.to_lower(); String name; @@ -2537,6 +2542,10 @@ class EditorExportAndroid : public EditorExportPlatform { }*/ } + if (tname == "uses-feature" && /*nspace=="android" &&*/ attrname == "glEsVersion") { + print_line("version number: " + itos(decode_uint32(&p_manifest[iofs + 16]))); + } + if (tname == "uses-permission" && /*nspace=="android" &&*/ attrname == "name") { if (value.begins_with("godot.custom")) { @@ -2750,9 +2759,9 @@ class EditorExportAndroid : public EditorExportPlatform { } else { String lang = str.substr(str.find_last("-") + 1, str.length()).replace("-", "_"); - String prop = "application/name_" + lang; - if (GlobalConfig::get_singleton()->has(prop)) { - str = GlobalConfig::get_singleton()->get(prop); + String prop = "application/config/name_" + lang; + if (ProjectSettings::get_singleton()->has(prop)) { + str = ProjectSettings::get_singleton()->get(prop); } else { str = get_project_name(package_name); } @@ -2826,11 +2835,17 @@ public: public: virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) { - r_features->push_back("etc"); + int api = p_preset->get("graphics/api"); + if (api == 0) + r_features->push_back("etc"); + else + r_features->push_back("etc2"); } virtual void get_export_options(List<ExportOption> *r_options) { + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "graphics/api", PROPERTY_HINT_ENUM, "OpenGL ES 2.0,OpenGL ES 3.0"), 1)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "graphics/32_bits_framebuffer"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "one_click_deploy/clear_previous_install"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_package/debug", PROPERTY_HINT_GLOBAL_FILE, "apk"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_package/release", PROPERTY_HINT_GLOBAL_FILE, "apk"), "")); @@ -2843,7 +2858,6 @@ public: r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/signed"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "architecture/arm"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "architecture/x86"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/use_32_bits_view"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/immersive_mode"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "screen/orientation", PROPERTY_HINT_ENUM, "Landscape,Portrait"), 0)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_small"), true)); @@ -2875,6 +2889,11 @@ public: virtual String get_name() const { return "Android"; } + + virtual String get_os_name() const { + return "Android"; + } + virtual Ref<Texture> get_logo() const { return logo; } @@ -2991,15 +3010,19 @@ public: if (use_adb_over_usb) { args.clear(); + args.push_back("-s"); + args.push_back(devices[p_device].id); args.push_back("reverse"); args.push_back("--remove-all"); err = OS::get_singleton()->execute(adb, args, true, NULL, NULL, &rv); - int port = (int)EditorSettings::get_singleton()->get("network/debug/remote_port"); + 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(port)); - args.push_back("tcp:" + itos(port)); + args.push_back("tcp:" + itos(dbg_port)); + args.push_back("tcp:" + itos(dbg_port)); err = OS::get_singleton()->execute(adb, args, true, NULL, NULL, &rv); print_line("Reverse result: " + itos(rv)); @@ -3007,6 +3030,8 @@ public: 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)); @@ -3022,7 +3047,10 @@ public: args.push_back("shell"); args.push_back("am"); args.push_back("start"); - args.push_back("--user 0"); + if ((bool)EditorSettings::get_singleton()->get("export/android/force_system_user") && devices[p_device].release >= 17) { // Multi-user introduced in Android 17 + args.push_back("--user"); + args.push_back("0"); + } args.push_back("-a"); args.push_back("android.intent.action.MAIN"); args.push_back("-n"); @@ -3030,7 +3058,7 @@ public: err = OS::get_singleton()->execute(adb, args, true, NULL, NULL, &rv); if (err || rv != 0) { - EditorNode::add_io_error("Could not execute ondevice."); + EditorNode::add_io_error("Could not execute on device."); device_lock->unlock(); return ERR_CANT_CREATE; } @@ -3219,7 +3247,7 @@ public: if (!found) { - String appicon = GlobalConfig::get_singleton()->get("application/icon"); + String appicon = ProjectSettings::get_singleton()->get("application/config/icon"); if (appicon != "" && appicon.ends_with(".png")) { FileAccess *f = FileAccess::open(appicon, FileAccess::READ); if (f) { @@ -3527,6 +3555,12 @@ public: return OK; } + virtual void get_platform_features(List<String> *r_features) { + + r_features->push_back("mobile"); + r_features->push_back("Android"); + } + EditorExportAndroid() { Ref<Image> img = memnew(Image(_android_logo)); @@ -3566,6 +3600,7 @@ void register_android_exporter() { EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/debug_keystore", PROPERTY_HINT_GLOBAL_FILE, "keystore")); EDITOR_DEF("export/android/debug_keystore_user", "androiddebugkey"); EDITOR_DEF("export/android/debug_keystore_pass", "android"); + EDITOR_DEF("export/android/force_system_user", false); EDITOR_DEF("export/android/timestamping_authority_url", ""); EDITOR_DEF("export/android/use_remote_debug_over_adb", false); diff --git a/platform/android/globals/global_defaults.cpp b/platform/android/globals/global_defaults.cpp index f708ad5dd..6bdc6b337 100644 --- a/platform/android/globals/global_defaults.cpp +++ b/platform/android/globals/global_defaults.cpp @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "global_defaults.h" -#include "global_config.h" +#include "project_settings.h" void register_android_global_defaults() { @@ -37,6 +37,6 @@ void register_android_global_defaults() { GLOBAL_DEF("display.Android/driver","GLES2"); //GLOBAL_DEF("rasterizer.Android/trilinear_mipmap_filter",false); - GlobalConfig::get_singleton()->set_custom_property_info("display.Android/driver",PropertyInfo(Variant::STRING,"display.Android/driver",PROPERTY_HINT_ENUM,"GLES2")); + ProjectSettings::get_singleton()->set_custom_property_info("display.Android/driver",PropertyInfo(Variant::STRING,"display.Android/driver",PROPERTY_HINT_ENUM,"GLES2")); */ } diff --git a/platform/android/godot_android.cpp b/platform/android/godot_android.cpp index 3a21f9212..71db03049 100644 --- a/platform/android/godot_android.cpp +++ b/platform/android/godot_android.cpp @@ -36,9 +36,9 @@ #include <GLES2/gl2.h> #include "file_access_android.h" -#include "global_config.h" #include "main/main.h" #include "os_android.h" +#include "project_settings.h" #include <android/log.h> #include <android/sensor.h> #include <android/window.h> @@ -623,7 +623,7 @@ static void engine_handle_cmd(struct android_app *app, int32_t cmd) { #else Error err = Main::setup("apk", 0, NULL); - String modules = GlobalConfig::get_singleton()->get("android/modules"); + String modules = ProjectSettings::get_singleton()->get("android/modules"); Vector<String> mods = modules.split(",", false); mods.push_back("GodotOS"); __android_log_print(ANDROID_LOG_INFO, "godot", "mod count: %i", mods.size()); @@ -859,7 +859,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_Godot_registerSingleton(JNIEnv s->set_instance(env->NewGlobalRef(p_object)); jni_singletons[singname] = s; - GlobalConfig::get_singleton()->add_singleton(GlobalConfig::Singleton(singname, s)); + ProjectSettings::get_singleton()->add_singleton(ProjectSettings::Singleton(singname, s)); } static Variant::Type get_jni_type(const String &p_type) { @@ -926,7 +926,7 @@ JNIEXPORT jstring JNICALL Java_org_godotengine_godot_Godot_getGlobal(JNIEnv *env String js = env->GetStringUTFChars(path, NULL); - return env->NewStringUTF(GlobalConfig::get_singleton()->get(js).operator String().utf8().get_data()); + return env->NewStringUTF(ProjectSettings::get_singleton()->get(js).operator String().utf8().get_data()); } JNIEXPORT void JNICALL Java_org_godotengine_godot_Godot_registerMethod(JNIEnv *env, jobject obj, jstring sname, jstring name, jstring ret, jobjectArray args) { diff --git a/platform/android/java/src/org/godotengine/godot/Godot.java b/platform/android/java/src/org/godotengine/godot/Godot.java index 88928089b..d620b2b9c 100644 --- a/platform/android/java/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/src/org/godotengine/godot/Godot.java @@ -273,7 +273,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC 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/keep_screen_on").equals("True")); + setKeepScreenOn(GodotLib.getGlobal("display/driver/keep_screen_on").equals("True")); edittext.setView(mView); io.setEdit(edittext); diff --git a/platform/android/java_glue.cpp b/platform/android/java_glue.cpp index d4bd44368..0508989d2 100644 --- a/platform/android/java_glue.cpp +++ b/platform/android/java_glue.cpp @@ -36,11 +36,11 @@ #include "dir_access_jandroid.h" #include "file_access_android.h" #include "file_access_jandroid.h" -#include "global_config.h" #include "java_class_wrapper.h" #include "main/input_default.h" #include "main/main.h" #include "os_android.h" +#include "project_settings.h" #include "thread_jandroid.h" #include <unistd.h> @@ -883,7 +883,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *en __android_log_print(ANDROID_LOG_INFO, "godot", "*****SETUP OK"); //video driver is determined here, because once initialized, it can't be changed - String vd = GlobalConfig::get_singleton()->get("display/driver"); + String vd = ProjectSettings::get_singleton()->get("display/driver"); env->CallVoidMethod(_godot_instance, _on_video_init, (jboolean) true); @@ -930,12 +930,12 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, job static void _initialize_java_modules() { - if (!GlobalConfig::get_singleton()->has("android/modules")) { + if (!ProjectSettings::get_singleton()->has("android/modules")) { print_line("ANDROID MODULES: Nothing to load, aborting"); return; } - String modules = GlobalConfig::get_singleton()->get("android/modules"); + String modules = ProjectSettings::get_singleton()->get("android/modules"); modules = modules.strip_edges(); if (modules == String()) { return; @@ -1005,7 +1005,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, job // because of the way android forces you to do everything with threads java_class_wrapper = memnew(JavaClassWrapper(_godot_instance)); - GlobalConfig::get_singleton()->add_singleton(GlobalConfig::Singleton("JavaClassWrapper", java_class_wrapper)); + ProjectSettings::get_singleton()->add_singleton(ProjectSettings::Singleton("JavaClassWrapper", java_class_wrapper)); _initialize_java_modules(); Main::setup2(); @@ -1504,8 +1504,8 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_singleton(JNIEnv *env s->set_instance(env->NewGlobalRef(p_object)); jni_singletons[singname] = s; - GlobalConfig::get_singleton()->add_singleton(GlobalConfig::Singleton(singname, s)); - GlobalConfig::get_singleton()->set(singname, s); + ProjectSettings::get_singleton()->add_singleton(ProjectSettings::Singleton(singname, s)); + ProjectSettings::get_singleton()->set(singname, s); } static Variant::Type get_jni_type(const String &p_type) { @@ -1578,7 +1578,7 @@ JNIEXPORT jstring JNICALL Java_org_godotengine_godot_GodotLib_getGlobal(JNIEnv * String js = env->GetStringUTFChars(path, NULL); - return env->NewStringUTF(GlobalConfig::get_singleton()->get(js).operator String().utf8().get_data()); + return env->NewStringUTF(ProjectSettings::get_singleton()->get(js).operator String().utf8().get_data()); } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_method(JNIEnv *env, jobject obj, jstring sname, jstring name, jstring ret, jobjectArray args) { diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index 9010b9e7d..ad46ceb43 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -29,8 +29,8 @@ /*************************************************************************/ #include "os_android.h" -#include "core/global_config.h" #include "core/io/file_access_buffered_fa.h" +#include "core/project_settings.h" #include "drivers/gles3/rasterizer_gles3.h" #include "drivers/unix/dir_access_unix.h" #include "drivers/unix/file_access_unix.h" @@ -677,7 +677,7 @@ String OS_Android::get_data_dir() const { } return "."; - //return GlobalConfig::get_singleton()->get_singleton_object("GodotOS")->call("get_data_dir"); + //return ProjectSettings::get_singleton()->get_singleton_object("GodotOS")->call("get_data_dir"); } void OS_Android::set_screen_orientation(ScreenOrientation p_orientation) { @@ -741,6 +741,10 @@ String OS_Android::get_joy_guid(int p_device) const { return input->get_joy_guid_remapped(p_device); } +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) { use_apk_expansion = p_use_apk_expansion; diff --git a/platform/android/os_android.h b/platform/android/os_android.h index 897c71a7d..393bc68d8 100644 --- a/platform/android/os_android.h +++ b/platform/android/os_android.h @@ -243,6 +243,7 @@ public: virtual String get_joy_guid(int p_device) const; 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(); }; diff --git a/platform/haiku/audio_driver_media_kit.cpp b/platform/haiku/audio_driver_media_kit.cpp index 9c4f6d3ab..93351e079 100644 --- a/platform/haiku/audio_driver_media_kit.cpp +++ b/platform/haiku/audio_driver_media_kit.cpp @@ -31,7 +31,7 @@ #ifdef MEDIA_KIT_ENABLED -#include "global_config.h" +#include "project_settings.h" int32_t *AudioDriverMediaKit::samples_in = NULL; diff --git a/platform/haiku/detect.py b/platform/haiku/detect.py index 54e227cd1..c0e003a3d 100644 --- a/platform/haiku/detect.py +++ b/platform/haiku/detect.py @@ -11,57 +11,58 @@ def get_name(): def can_build(): - if (os.name != "posix"): - return False - if (sys.platform == "darwin"): + if (os.name != "posix" or sys.platform == "darwin"): return False return True def get_opts(): + return [ ('debug_release', 'Add debug symbols to release version', 'no') ] def get_flags(): + return [ ] def configure(env): - is64 = sys.maxsize > 2**32 - - if (env["bits"] == "default"): - if (is64): - env["bits"] = "64" - else: - env["bits"] = "32" - - env.Append(CPPPATH=['#platform/haiku']) - env["CC"] = "gcc-x86" - env["CXX"] = "g++-x86" + ## Build type if (env["target"] == "release"): if (env["debug_release"] == "yes"): - env.Append(CCFLAGS=['-g2']) + env.Prepend(CCFLAGS=['-g2']) else: - env.Append(CCFLAGS=['-O3', '-ffast-math']) + env.Prepend(CCFLAGS=['-O3', '-ffast-math']) + elif (env["target"] == "release_debug"): - env.Append(CCFLAGS=['-O2', '-ffast-math', '-DDEBUG_ENABLED']) + env.Prepend(CCFLAGS=['-O2', '-ffast-math', '-DDEBUG_ENABLED']) + elif (env["target"] == "debug"): - env.Append(CCFLAGS=['-g2', '-DDEBUG_ENABLED', '-DDEBUG_MEMORY_ENABLED']) + env.Prepend(CCFLAGS=['-g2', '-DDEBUG_ENABLED', '-DDEBUG_MEMORY_ENABLED']) + + ## Architecture + + is64 = sys.maxsize > 2**32 + if (env["bits"] == "default"): + env["bits"] = "64" if is64 else "32" + + ## Compiler configuration + env["CC"] = "gcc-x86" + env["CXX"] = "g++-x86" + + ## Flags + + env.Append(CPPPATH=['#platform/haiku']) + env.Append(CPPFLAGS=['-DUNIX_ENABLED', '-DOPENGL_ENABLED', '-DGLES2_ENABLED', '-DGLES_OVER_GL']) + env.Append(CPPFLAGS=['-DMEDIA_KIT_ENABLED']) # env.Append(CCFLAGS=['-DFREETYPE_ENABLED']) env.Append(CPPFLAGS=['-DPTHREAD_NO_RENAME']) # TODO: enable when we have pthread_setname_np - env.Append(CPPFLAGS=['-DOPENGL_ENABLED', '-DMEDIA_KIT_ENABLED']) - env.Append(CPPFLAGS=['-DUNIX_ENABLED', '-DGLES2_ENABLED', '-DGLES_OVER_GL']) env.Append(LIBS=['be', 'game', 'media', 'network', 'bnetapi', 'z', 'GL']) - - import methods - env.Append(BUILDERS={'GLSL120': env.Builder(action=methods.build_legacygl_headers, suffix='glsl.h', src_suffix='.glsl')}) - env.Append(BUILDERS={'GLSL': env.Builder(action=methods.build_glsl_headers, suffix='glsl.h', src_suffix='.glsl')}) - env.Append(BUILDERS={'GLSL120GLES': env.Builder(action=methods.build_gles2_headers, suffix='glsl.h', src_suffix='.glsl')}) diff --git a/platform/haiku/os_haiku.cpp b/platform/haiku/os_haiku.cpp index 3131f2bf1..e897d4c38 100644 --- a/platform/haiku/os_haiku.cpp +++ b/platform/haiku/os_haiku.cpp @@ -331,3 +331,8 @@ void OS_Haiku::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) c String OS_Haiku::get_executable_path() const { return OS::get_executable_path(); } + +bool OS_Haiku::_check_internal_feature_support(const String &p_feature) { + + return p_feature == "pc" || p_feature == "s3tc"; +} diff --git a/platform/haiku/os_haiku.h b/platform/haiku/os_haiku.h index 83e44734a..256c9eecf 100644 --- a/platform/haiku/os_haiku.h +++ b/platform/haiku/os_haiku.h @@ -120,6 +120,8 @@ public: virtual 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); }; #endif diff --git a/platform/iphone/SCsub b/platform/iphone/SCsub index 466b8241d..998d0a3f0 100644 --- a/platform/iphone/SCsub +++ b/platform/iphone/SCsub @@ -17,17 +17,8 @@ iphone_lib = [ 'ios.mm', ] -# env.Depends('#core/math/vector3.h', 'vector3_psp.h') - -#iphone_lib = env.Library('iphone', iphone_lib) - env_ios = env.Clone() - -if env['ios_gles22_override'] == "yes": - env_ios.Append(CPPFLAGS=['-DGLES2_OVERRIDE']) - - obj = env_ios.Object('godot_iphone.cpp') prog = None diff --git a/platform/iphone/app_delegate.mm b/platform/iphone/app_delegate.mm index 1f81f0f86..da6dfcf53 100644 --- a/platform/iphone/app_delegate.mm +++ b/platform/iphone/app_delegate.mm @@ -29,11 +29,11 @@ /*************************************************************************/ #import "app_delegate.h" -#include "core/global_config.h" +#include "audio_driver_iphone.h" +#include "core/project_settings.h" #import "gl_view.h" #include "main/main.h" #include "os_iphone.h" -#include "audio_driver_iphone.h" #ifdef MODULE_FACEBOOKSCORER_IOS_ENABLED #include "modules/FacebookScorer_ios/FacebookScorer.h" @@ -449,14 +449,14 @@ static int frame_count = 0; NSString *str = (NSString *)value; String uval = String::utf8([str UTF8String]); - GlobalConfig::get_singleton()->set("Info.plist/" + ukey, uval); + ProjectSettings::get_singleton()->set("Info.plist/" + ukey, uval); } else if ([value isKindOfClass:[NSNumber class]]) { NSNumber *n = (NSNumber *)value; double dval = [n doubleValue]; - GlobalConfig::get_singleton()->set("Info.plist/" + ukey, dval); + ProjectSettings::get_singleton()->set("Info.plist/" + ukey, dval); }; // do stuff } @@ -615,7 +615,7 @@ static int frame_count = 0; view_controller.view = glView; window.rootViewController = view_controller; - _set_keep_screen_on(bool(GLOBAL_DEF("display/keep_screen_on", true)) ? YES : NO); + _set_keep_screen_on(bool(GLOBAL_DEF("display/window/keep_screen_on", true)) ? YES : NO); glView.useCADisplayLink = bool(GLOBAL_DEF("display.iOS/use_cadisplaylink", true)) ? YES : NO; printf("cadisaplylink: %d", glView.useCADisplayLink); @@ -645,10 +645,10 @@ static int frame_count = 0; #ifdef MODULE_GAME_ANALYTICS_ENABLED printf("********************* didFinishLaunchingWithOptions\n"); - if (!GlobalConfig::get_singleton()->has("mobileapptracker/advertiser_id")) { + if (!ProjectSettings::get_singleton()->has("mobileapptracker/advertiser_id")) { return; } - if (!GlobalConfig::get_singleton()->has("mobileapptracker/conversion_key")) { + if (!ProjectSettings::get_singleton()->has("mobileapptracker/conversion_key")) { return; } @@ -673,7 +673,6 @@ static int frame_count = 0; isAdvertisingTrackingEnabled]]; #endif - }; - (void)applicationWillTerminate:(UIApplication *)application { @@ -737,7 +736,7 @@ 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) + if (AudioDriverIphone::get_singleton() != NULL) AudioDriverIphone::get_singleton()->start(); } diff --git a/platform/iphone/detect.py b/platform/iphone/detect.py index c20d8e90f..1d802ff28 100644 --- a/platform/iphone/detect.py +++ b/platform/iphone/detect.py @@ -1,4 +1,5 @@ import os +import string import sys @@ -12,8 +13,6 @@ def get_name(): def can_build(): - import sys - import os if sys.platform == 'darwin' or os.environ.has_key("OSXCROSS_IOS"): return True @@ -23,13 +22,12 @@ def can_build(): def get_opts(): return [ - ('IPHONEPLATFORM', 'name of the iphone platform', 'iPhoneOS'), - ('IPHONEPATH', 'the 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/'), + ('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_gles22_override', 'Force GLES2.0 on iOS', 'yes'), ('ios_exceptions', 'Enable exceptions', 'no'), ('ios_triple', 'Triple for ios toolchain', ''), ('ios_sim', 'Build simulator binary', 'no'), @@ -45,95 +43,92 @@ def get_flags(): def configure(env): - env.Append(CPPPATH=['#platform/iphone']) + ## Build type - env['ENV']['PATH'] = env['IPHONEPATH'] + "/Developer/usr/bin/:" + env['ENV']['PATH'] + if (env["target"].startswith("release")): + env.Append(CPPFLAGS=['-DNDEBUG', '-DNS_BLOCK_ASSERTIONS=1']) + env.Append(CPPFLAGS=['-O2', '-flto', '-ftree-vectorize', '-fomit-frame-pointer', '-ffast-math', '-funsafe-math-optimizations']) + env.Append(LINKFLAGS=['-O2', '-flto']) - env['CC'] = '$IPHONEPATH/usr/bin/${ios_triple}clang' - env['CXX'] = '$IPHONEPATH/usr/bin/${ios_triple}clang++' - env['AR'] = '$IPHONEPATH/usr/bin/${ios_triple}ar' - env['RANLIB'] = '$IPHONEPATH/usr/bin/${ios_triple}ranlib' + if env["target"] == "release_debug": + env.Append(CPPFLAGS=['-DDEBUG_ENABLED']) + + elif (env["target"] == "debug"): + env.Append(CPPFLAGS=['-D_DEBUG', '-DDEBUG=1', '-gdwarf-2', '-O0', '-DDEBUG_ENABLED', '-DDEBUG_MEMORY_ENABLED']) + + ## Architecture - import string if (env["ios_sim"] == "yes" or env["arch"] == "x86"): # i386, simulator env["arch"] = "x86" env["bits"] = "32" - env.Append(CCFLAGS=string.split('-arch i386 -fobjc-abi-version=2 -fobjc-legacy-dispatch -fmessage-length=0 -fpascal-strings -fblocks -fasm-blocks -D__IPHONE_OS_VERSION_MIN_REQUIRED=40100 -isysroot $IPHONESDK -mios-simulator-version-min=4.3 -DCUSTOM_MATRIX_TRANSFORM_H=\\\"build/iphone/matrix4_iphone.h\\\" -DCUSTOM_VECTOR3_TRANSFORM_H=\\\"build/iphone/vector3_iphone.h\\\"')) elif (env["arch"] == "arm" or env["arch"] == "arm32" or env["arch"] == "armv7" or env["bits"] == "32"): # arm env["arch"] = "arm" env["bits"] = "32" - env.Append(CCFLAGS=string.split('-fno-objc-arc -arch armv7 -fmessage-length=0 -fno-strict-aliasing -fdiagnostics-print-source-range-info -fdiagnostics-show-category=id -fdiagnostics-parseable-fixits -fpascal-strings -fblocks -isysroot $IPHONESDK -fvisibility=hidden -mthumb "-DIBOutlet=__attribute__((iboutlet))" "-DIBOutletCollection(ClassName)=__attribute__((iboutletcollection(ClassName)))" "-DIBAction=void)__attribute__((ibaction)" -miphoneos-version-min=9.0 -MMD -MT dependencies')) else: # armv64 env["arch"] = "arm64" env["bits"] = "64" + + ## Compiler configuration + + env['ENV']['PATH'] = env['IPHONEPATH'] + "/Developer/usr/bin/:" + env['ENV']['PATH'] + + env['CC'] = '$IPHONEPATH/usr/bin/${ios_triple}clang' + env['CXX'] = '$IPHONEPATH/usr/bin/${ios_triple}clang++' + env['AR'] = '$IPHONEPATH/usr/bin/${ios_triple}ar' + env['RANLIB'] = '$IPHONEPATH/usr/bin/${ios_triple}ranlib' + env['S_compiler'] = '$IPHONEPATH/Developer/usr/bin/gcc' + + ## Compile flags + + if (env["arch"] == "x86"): + env['IPHONEPLATFORM'] = 'iPhoneSimulator' + env['ENV']['MACOSX_DEPLOYMENT_TARGET'] = '10.6' + env.Append(CCFLAGS=string.split('-arch i386 -fobjc-abi-version=2 -fobjc-legacy-dispatch -fmessage-length=0 -fpascal-strings -fblocks -fasm-blocks -D__IPHONE_OS_VERSION_MIN_REQUIRED=40100 -isysroot $IPHONESDK -mios-simulator-version-min=4.3 -DCUSTOM_MATRIX_TRANSFORM_H=\\\"build/iphone/matrix4_iphone.h\\\" -DCUSTOM_VECTOR3_TRANSFORM_H=\\\"build/iphone/vector3_iphone.h\\\"')) + elif (env["arch"] == "arm"): + env.Append(CCFLAGS=string.split('-fno-objc-arc -arch armv7 -fmessage-length=0 -fno-strict-aliasing -fdiagnostics-print-source-range-info -fdiagnostics-show-category=id -fdiagnostics-parseable-fixits -fpascal-strings -fblocks -isysroot $IPHONESDK -fvisibility=hidden -mthumb "-DIBOutlet=__attribute__((iboutlet))" "-DIBOutletCollection(ClassName)=__attribute__((iboutletcollection(ClassName)))" "-DIBAction=void)__attribute__((ibaction)" -miphoneos-version-min=9.0 -MMD -MT dependencies')) + elif (env["arch"] == "arm64"): env.Append(CCFLAGS=string.split('-fno-objc-arc -arch arm64 -fmessage-length=0 -fno-strict-aliasing -fdiagnostics-print-source-range-info -fdiagnostics-show-category=id -fdiagnostics-parseable-fixits -fpascal-strings -fblocks -fvisibility=hidden -MMD -MT dependencies -miphoneos-version-min=9.0 -isysroot $IPHONESDK')) env.Append(CPPFLAGS=['-DNEED_LONG_INT']) env.Append(CPPFLAGS=['-DLIBYUV_DISABLE_NEON']) + if env['ios_exceptions'] == 'yes': + env.Append(CPPFLAGS=['-fexceptions']) + else: + env.Append(CPPFLAGS=['-fno-exceptions']) + + ## Link flags + if (env["arch"] == "x86"): - env['IPHONEPLATFORM'] = 'iPhoneSimulator' env.Append(LINKFLAGS=['-arch', 'i386', '-mios-simulator-version-min=4.3', '-isysroot', '$IPHONESDK', - #'-mmacosx-version-min=10.6', '-Xlinker', '-objc_abi_version', '-Xlinker', '2', - '-framework', 'AudioToolbox', - '-framework', 'AVFoundation', - '-framework', 'CoreAudio', - '-framework', 'CoreGraphics', - '-framework', 'CoreMedia', - '-framework', 'CoreMotion', - '-framework', 'Foundation', - '-framework', 'Security', - '-framework', 'UIKit', - '-framework', 'MediaPlayer', - '-framework', 'OpenGLES', - '-framework', 'QuartzCore', - '-framework', 'SystemConfiguration', - '-framework', 'GameController', '-F$IPHONESDK', ]) - elif (env["arch"] == "arm64"): - env.Append(LINKFLAGS=['-arch', 'arm64', '-Wl,-dead_strip', '-miphoneos-version-min=9.0', - '-isysroot', '$IPHONESDK', - #'-stdlib=libc++', - '-framework', 'Foundation', - '-framework', 'UIKit', - '-framework', 'CoreGraphics', - '-framework', 'OpenGLES', - '-framework', 'QuartzCore', - '-framework', 'CoreAudio', - '-framework', 'AudioToolbox', - '-framework', 'SystemConfiguration', - '-framework', 'Security', - #'-framework', 'AdSupport', - '-framework', 'MediaPlayer', - '-framework', 'AVFoundation', - '-framework', 'CoreMedia', - '-framework', 'CoreMotion', - '-framework', 'GameController', - ]) - else: - env.Append(LINKFLAGS=['-arch', 'armv7', '-Wl,-dead_strip', '-miphoneos-version-min=9.0', - '-isysroot', '$IPHONESDK', - '-framework', 'Foundation', - '-framework', 'UIKit', - '-framework', 'CoreGraphics', - '-framework', 'OpenGLES', - '-framework', 'QuartzCore', - '-framework', 'CoreAudio', - '-framework', 'AudioToolbox', - '-framework', 'SystemConfiguration', - '-framework', 'Security', - #'-framework', 'AdSupport', - '-framework', 'MediaPlayer', - '-framework', 'AVFoundation', - '-framework', 'CoreMedia', - '-framework', 'CoreMotion', - '-framework', 'GameController', - ]) + elif (env["arch"] == "arm"): + env.Append(LINKFLAGS=['-arch', 'armv7', '-Wl,-dead_strip', '-miphoneos-version-min=9.0']) + if (env["arch"] == "arm64"): + env.Append(LINKFLAGS=['-arch', 'arm64', '-Wl,-dead_strip', '-miphoneos-version-min=9.0']) + + env.Append(LINKFLAGS=['-isysroot', '$IPHONESDK', + '-framework', 'AudioToolbox', + '-framework', 'AVFoundation', + '-framework', 'CoreAudio', + '-framework', 'CoreGraphics', + '-framework', 'CoreMedia', + '-framework', 'CoreMotion', + '-framework', 'Foundation', + '-framework', 'GameController', + '-framework', 'MediaPlayer', + '-framework', 'OpenGLES', + '-framework', 'QuartzCore', + '-framework', 'Security', + '-framework', 'SystemConfiguration', + '-framework', 'UIKit', + ]) + # Feature options if env['game_center'] == 'yes': env.Append(CPPFLAGS=['-DGAME_CENTER_ENABLED']) env.Append(LINKFLAGS=['-framework', 'GameKit']) @@ -145,45 +140,20 @@ def configure(env): if env['icloud'] == 'yes': env.Append(CPPFLAGS=['-DICLOUD_ENABLED']) - env.Append(CPPPATH=['$IPHONESDK/usr/include', '$IPHONESDK/System/Library/Frameworks/OpenGLES.framework/Headers', '$IPHONESDK/System/Library/Frameworks/AudioUnit.framework/Headers']) + env.Append(CPPPATH=['$IPHONESDK/usr/include', + '$IPHONESDK/System/Library/Frameworks/OpenGLES.framework/Headers', + '$IPHONESDK/System/Library/Frameworks/AudioUnit.framework/Headers', + ]) - if (env["target"].startswith("release")): - - env.Append(CPPFLAGS=['-DNDEBUG', '-DNS_BLOCK_ASSERTIONS=1']) - env.Append(CPPFLAGS=['-O2', '-flto', '-ftree-vectorize', '-fomit-frame-pointer', '-ffast-math', '-funsafe-math-optimizations']) - env.Append(LINKFLAGS=['-O2', '-flto']) - - if env["target"] == "release_debug": - env.Append(CPPFLAGS=['-DDEBUG_ENABLED']) - - elif (env["target"] == "debug"): - - env.Append(CPPFLAGS=['-D_DEBUG', '-DDEBUG=1', '-gdwarf-2', '-O0', '-DDEBUG_ENABLED']) - env.Append(CPPFLAGS=['-DDEBUG_MEMORY_ENABLED']) - - if (env["ios_sim"] == "yes"): # TODO: Check if needed? - env['ENV']['MACOSX_DEPLOYMENT_TARGET'] = '10.6' 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']) # TODO: Move that to opus module's config if("module_opus_enabled" in env and env["module_opus_enabled"] != "no"): env.opus_fixed_point = "yes" - if env["arch"] == "x86": - pass - elif(env["arch"] == "arm64"): - env.Append(CFLAGS=["-DOPUS_ARM64_OPT"]) - else: + if (env["arch"] == "arm"): env.Append(CFLAGS=["-DOPUS_ARM_OPT"]) - - if env['ios_exceptions'] == 'yes': - env.Append(CPPFLAGS=['-fexceptions']) - else: - env.Append(CPPFLAGS=['-fno-exceptions']) - # env['neon_enabled']=True - env['S_compiler'] = '$IPHONEPATH/Developer/usr/bin/gcc' - - import methods - env.Append(BUILDERS={'GLSL120': env.Builder(action=methods.build_legacygl_headers, suffix='glsl.h', src_suffix='.glsl')}) - env.Append(BUILDERS={'GLSL': env.Builder(action=methods.build_glsl_headers, suffix='glsl.h', src_suffix='.glsl')}) - env.Append(BUILDERS={'GLSL120GLES': env.Builder(action=methods.build_gles2_headers, suffix='glsl.h', src_suffix='.glsl')}) + elif (env["arch"] == "arm64"): + env.Append(CFLAGS=["-DOPUS_ARM64_OPT"]) diff --git a/platform/iphone/export/export.cpp b/platform/iphone/export/export.cpp new file mode 100644 index 000000000..6ae2a0692 --- /dev/null +++ b/platform/iphone/export/export.cpp @@ -0,0 +1,366 @@ +/*************************************************************************/ +/* export.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.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 "export.h" +#include "editor/editor_export.h" +#include "editor/editor_node.h" +#include "editor/editor_settings.h" +#include "io/marshalls.h" +#include "io/resource_saver.h" +#include "io/zip_io.h" +#include "os/file_access.h" +#include "os/os.h" +#include "platform/osx/logo.gen.h" +#include "project_settings.h" +#include "string.h" +#include "version.h" + +#include <sys/stat.h> + +class EditorExportPlatformIOS : public EditorExportPlatform { + + GDCLASS(EditorExportPlatformIOS, EditorExportPlatform); + + int version_code; + + Ref<ImageTexture> logo; + + void _fix_config_file(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &pfile, const String &p_name, const String &p_binary); + +protected: + virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features); + virtual void get_export_options(List<ExportOption> *r_options); + +public: + virtual String get_name() const { return "iOS"; } + 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 Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0); + + virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const; + + virtual void get_platform_features(List<String> *r_features) { + + r_features->push_back("mobile"); + r_features->push_back("iOS"); + } + + EditorExportPlatformIOS(); + ~EditorExportPlatformIOS(); +}; + +void EditorExportPlatformIOS::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"); + } +} + +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/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/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::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)); + + /* 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) { + + String str; + String strnew; + str.parse_utf8((const char *)pfile.ptr(), pfile.size()); + Vector<String> lines = str.split("\n"); + for (int i = 0; i < lines.size(); i++) { + if (lines[i].find("$binary") != -1) { + strnew += lines[i].replace("$binary", p_binary) + "\n"; + } else if (lines[i].find("$name") != -1) { + strnew += lines[i].replace("$name", p_name) + "\n"; + } else if (lines[i].find("$info") != -1) { + strnew += lines[i].replace("$info", p_preset->get("application/info")) + "\n"; + } else if (lines[i].find("$identifier") != -1) { + strnew += lines[i].replace("$identifier", p_preset->get("application/identifier")) + "\n"; + } else if (lines[i].find("$short_version") != -1) { + strnew += lines[i].replace("$short_version", p_preset->get("application/short_version")) + "\n"; + } else if (lines[i].find("$version") != -1) { + strnew += lines[i].replace("$version", p_preset->get("application/version")) + "\n"; + } else if (lines[i].find("$signature") != -1) { + 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 { + strnew += lines[i] + "\n"; + } + } + + // !BAS! I'm assuming the 9 in the original code was a typo. I've added -1 or else it seems to also be adding our terminating zero... + // should apply the same fix in our OSX export. + CharString cs = strnew.utf8(); + pfile.resize(cs.size() - 1); + for (int i = 0; i < cs.size() - 1; i++) { + pfile[i] = cs[i]; + } +} + +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); + + 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("iphone.zip", &err); + if (src_pkg_name == "") { + EditorNode::add_io_error(err); + return ERR_FILE_NOT_FOUND; + } + } + + FileAccess *src_f = NULL; + zlib_filefunc_def io = zipio_create_io_from_file(&src_f); + + ep.step("Creating app", 0); + + unzFile src_pkg_zip = unzOpen2(src_pkg_name.utf8().get_data(), &io); + if (!src_pkg_zip) { + + EditorNode::add_io_error("Could not find template app to export:\n" + src_pkg_name); + return ERR_FILE_NOT_FOUND; + } + + ERR_FAIL_COND_V(!src_pkg_zip, ERR_CANT_OPEN); + int ret = unzGoToFirstFile(src_pkg_zip); + + String binary_to_use = "godot.iphone." + 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 ? "arm64" : "armv7"); + + 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"; + + DirAccess *tmp_app_path = DirAccess::create_for_path(dest_dir); + ERR_FAIL_COND_V(!tmp_app_path, ERR_CANT_CREATE) + + /* Now process our template */ + bool found_binary = false; + int total_size = 0; + + while (ret == UNZ_OK) { + bool is_execute = false; + + //get filename + unz_file_info info; + char fname[16384]; + ret = unzGetCurrentFileInfo(src_pkg_zip, &info, fname, 16384, NULL, 0, NULL, 0); + + String file = fname; + + print_line("READ: " + file); + Vector<uint8_t> data; + data.resize(info.uncompressed_size); + + //read + unzOpenCurrentFile(src_pkg_zip); + unzReadCurrentFile(src_pkg_zip, data.ptr(), data.size()); + unzCloseCurrentFile(src_pkg_zip); + + //write + + file = file.replace_first("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); + } else if (file.begins_with("godot.iphone")) { + if (file != binary_to_use) { + ret = unzGoToNextFile(src_pkg_zip); + continue; //ignore! + } + found_binary = true; + is_execute = true; + file = "godot_ios.iphone"; + } + + ///@TODO need to parse logo files + + if (data.size() > 0) { + file = file.replace("godot_ios", binary_name); + + print_line("ADDING: " + file + " size: " + itos(data.size())); + total_size += data.size(); + + /* write it into our folder structure */ + file = dest_dir + file; + + /* make sure this folder exists */ + String dir_name = file.get_base_dir(); + if (!tmp_app_path->dir_exists(dir_name)) { + print_line("Creating " + dir_name); + Error dir_err = tmp_app_path->make_dir_recursive(dir_name); + if (dir_err) { + ERR_PRINTS("Can't create '" + dir_name + "'."); + unzClose(src_pkg_zip); + return ERR_CANT_CREATE; + } + } + + /* write the file */ + FileAccess *f = FileAccess::open(file, FileAccess::WRITE); + if (!f) { + ERR_PRINTS("Can't write '" + file + "'."); + unzClose(src_pkg_zip); + return ERR_CANT_CREATE; + }; + f->store_buffer(data.ptr(), data.size()); + f->close(); + memdelete(f); + +#ifdef OSX_ENABLED + if (is_execute) { + // we need execute rights on this file + chmod(file.utf8().get_data(), 0755); + } +#endif + } + + ret = unzGoToNextFile(src_pkg_zip); + } + + /* we're done with our source zip */ + unzClose(src_pkg_zip); + + if (!found_binary) { + ERR_PRINTS("Requested template binary '" + binary_to_use + "' not found. It might be missing from your template archive."); + unzClose(src_pkg_zip); + return ERR_FILE_NOT_FOUND; + } + + ep.step("Making PKG", 1); + + String pack_path = dest_dir + binary_name + ".pck"; + Error err = save_pack(p_preset, pack_path); + + 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); + ERR_FAIL_COND_V(err, err); + +#endif + + return OK; +} + +bool EditorExportPlatformIOS::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { + + bool valid = true; + String err; + + if (!exists_export_template("iphone.zip", &err)) { + valid = false; + } + + if (p_preset->get("custom_package/debug") != "" && !FileAccess::exists(p_preset->get("custom_package/debug"))) { + valid = false; + err += "Custom debug package not found.\n"; + } + + if (p_preset->get("custom_package/release") != "" && !FileAccess::exists(p_preset->get("custom_package/release"))) { + valid = false; + err += "Custom release package not found.\n"; + } + + if (!err.empty()) + r_error = err; + + return valid; +} + +EditorExportPlatformIOS::EditorExportPlatformIOS() { + + ///@TODO need to create the correct logo + // Ref<Image> img = memnew(Image(_iphone_logo)); + Ref<Image> img = memnew(Image(_osx_logo)); + logo.instance(); + logo->create_from_image(img); +} + +EditorExportPlatformIOS::~EditorExportPlatformIOS() { +} + +void register_iphone_exporter() { + + Ref<EditorExportPlatformIOS> platform; + platform.instance(); + + EditorExport::get_singleton()->add_export_platform(platform); +} diff --git a/platform/iphone/export/export.h b/platform/iphone/export/export.h new file mode 100644 index 000000000..6e9324aed --- /dev/null +++ b/platform/iphone/export/export.h @@ -0,0 +1,30 @@ +/*************************************************************************/ +/* export.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.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. */ +/*************************************************************************/ +void register_iphone_exporter(); diff --git a/platform/iphone/gl_view.mm b/platform/iphone/gl_view.mm index 6270fa85f..f2778e816 100644 --- a/platform/iphone/gl_view.mm +++ b/platform/iphone/gl_view.mm @@ -29,8 +29,8 @@ /*************************************************************************/ #import "gl_view.h" -#include "core/global_config.h" #include "core/os/keyboard.h" +#include "core/project_settings.h" #include "os_iphone.h" #include "servers/audio_server.h" @@ -77,7 +77,7 @@ void _hide_keyboard() { }; bool _play_video(String p_path, float p_volume, String p_audio_track, String p_subtitle_track) { - p_path = GlobalConfig::get_singleton()->globalize_path(p_path); + p_path = ProjectSettings::get_singleton()->globalize_path(p_path); NSString *file_path = [[[NSString alloc] initWithUTF8String:p_path.utf8().get_data()] autorelease]; diff --git a/platform/iphone/globals/global_defaults.cpp b/platform/iphone/globals/global_defaults.cpp index b320be2f8..aa4662302 100644 --- a/platform/iphone/globals/global_defaults.cpp +++ b/platform/iphone/globals/global_defaults.cpp @@ -28,14 +28,14 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "global_defaults.h" -#include "global_config.h" +#include "project_settings.h" void register_iphone_global_defaults() { /*GLOBAL_DEF("rasterizer.iOS/use_fragment_lighting",false); GLOBAL_DEF("rasterizer.iOS/fp16_framebuffer",false); GLOBAL_DEF("display.iOS/driver","GLES2"); - GlobalConfig::get_singleton()->set_custom_property_info("display.iOS/driver",PropertyInfo(Variant::STRING,"display.iOS/driver",PROPERTY_HINT_ENUM,"GLES1,GLES2")); + ProjectSettings::get_singleton()->set_custom_property_info("display.iOS/driver",PropertyInfo(Variant::STRING,"display.iOS/driver",PROPERTY_HINT_ENUM,"GLES1,GLES2")); GLOBAL_DEF("display.iOS/use_cadisplaylink",true); */ } diff --git a/platform/iphone/ios.mm b/platform/iphone/ios.mm index e9c164393..6c9590324 100644 --- a/platform/iphone/ios.mm +++ b/platform/iphone/ios.mm @@ -36,7 +36,7 @@ void iOS::_bind_methods() { ClassDB::bind_method(D_METHOD("get_rate_url", "app_id"), &iOS::get_rate_url); }; -void iOS::alert(const char* p_alert, const char* p_title) { +void iOS::alert(const char *p_alert, const char *p_title) { UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:[NSString stringWithUTF8String:p_title] message:[NSString stringWithUTF8String:p_alert] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil] autorelease]; [alert show]; } diff --git a/platform/iphone/os_iphone.cpp b/platform/iphone/os_iphone.cpp index b244edbff..cb5c02276 100644 --- a/platform/iphone/os_iphone.cpp +++ b/platform/iphone/os_iphone.cpp @@ -38,10 +38,10 @@ #include "audio_driver_iphone.h" #include "main/main.h" -#include "core/global_config.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 "sem_iphone.h" @@ -109,7 +109,6 @@ void OSIPhone::initialize(const VideoMode &p_desired, int p_video_driver, int p_ RasterizerGLES3::register_config(); RasterizerGLES3::make_current(); - RasterizerStorageGLES3::system_fbo = gl_view_base_fb; visual_server = memnew(VisualServerRaster()); /* @@ -122,6 +121,9 @@ void OSIPhone::initialize(const VideoMode &p_desired, int p_video_driver, int p_ visual_server->init(); visual_server->cursor_set_visible(false, 0); + // 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(); @@ -138,28 +140,28 @@ void OSIPhone::initialize(const VideoMode &p_desired, int p_video_driver, int p_ /* #ifdef IOS_SCORELOOP_ENABLED scoreloop = memnew(ScoreloopIOS); - GlobalConfig::get_singleton()->add_singleton(GlobalConfig::Singleton("Scoreloop", scoreloop)); + ProjectSettings::get_singleton()->add_singleton(ProjectSettings::Singleton("Scoreloop", scoreloop)); scoreloop->connect(); #endif */ #ifdef GAME_CENTER_ENABLED game_center = memnew(GameCenter); - GlobalConfig::get_singleton()->add_singleton(GlobalConfig::Singleton("GameCenter", game_center)); + ProjectSettings::get_singleton()->add_singleton(ProjectSettings::Singleton("GameCenter", game_center)); game_center->connect(); #endif #ifdef STOREKIT_ENABLED store_kit = memnew(InAppStore); - GlobalConfig::get_singleton()->add_singleton(GlobalConfig::Singleton("InAppStore", store_kit)); + ProjectSettings::get_singleton()->add_singleton(ProjectSettings::Singleton("InAppStore", store_kit)); #endif #ifdef ICLOUD_ENABLED icloud = memnew(ICloud); - GlobalConfig::get_singleton()->add_singleton(GlobalConfig::Singleton("ICloud", icloud)); + ProjectSettings::get_singleton()->add_singleton(ProjectSettings::Singleton("ICloud", icloud)); //icloud->connect(); #endif - GlobalConfig::get_singleton()->add_singleton(GlobalConfig::Singleton("iOS", memnew(iOS))); + ProjectSettings::get_singleton()->add_singleton(ProjectSettings::Singleton("iOS", memnew(iOS))); }; MainLoop *OSIPhone::get_main_loop() const { @@ -434,7 +436,8 @@ bool OSIPhone::can_draw() const { int OSIPhone::set_base_framebuffer(int p_fb) { - RasterizerStorageGLES3::system_fbo = gl_view_base_fb; + // gl_view_base_fb has not been updated yet + RasterizerStorageGLES3::system_fbo = p_fb; return 0; }; @@ -517,7 +520,7 @@ Error OSIPhone::native_video_play(String p_path, float p_volume, String p_audio_ print("Unable to play %S using the native player as it resides in a .pck file\n", p_path.c_str()); return ERR_INVALID_PARAMETER; } else { - p_path = p_path.replace("res:/", GlobalConfig::get_singleton()->get_resource_path()); + p_path = p_path.replace("res:/", ProjectSettings::get_singleton()->get_resource_path()); } } else if (p_path.begins_with("user://")) p_path = p_path.replace("user:/", get_data_dir()); @@ -552,6 +555,11 @@ void OSIPhone::native_video_stop() { _stop_video(); } +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) { main_loop = NULL; diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h index 4031b7524..b15e9fdff 100644 --- a/platform/iphone/os_iphone.h +++ b/platform/iphone/os_iphone.h @@ -200,6 +200,7 @@ public: virtual void native_video_focus_out(); virtual void native_video_stop(); + virtual bool _check_internal_feature_support(const String &p_feature); OSIPhone(int width, int height); ~OSIPhone(); }; diff --git a/platform/javascript/SCsub b/platform/javascript/SCsub index 02ff2090f..b804863ee 100644 --- a/platform/javascript/SCsub +++ b/platform/javascript/SCsub @@ -19,29 +19,34 @@ javascript_objects = [] 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']\""]) +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"']) -html_file = env.Program('#bin/godot', javascript_objects, PROGSUFFIX=env["PROGSUFFIX"] + ".html")[0] +# 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)) +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)) + implicit_targets.extend(asmjs_files) + +# 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") -basename = "godot" + env["PROGSUFFIX"] # output file name without file extension # 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') -zip_dir = env.Dir('#bin/.javascript_zip') -zip_files = [] -js_file = env.SideEffect(html_file.File(basename+'.js'), html_file) -zip_files.append(env.InstallAs( - [zip_dir.File('godot.html'), zip_dir.File('godot.js'), zip_dir.File('godotfs.js')], - [fixup_html, js_file, '#misc/dist/html_fs/godotfs.js'])) - -if env['wasm'] == 'yes': - wasm_file = env.SideEffect(html_file.File(basename+'.wasm'), html_file) - zip_files.append(env.InstallAs(zip_dir.File('godot.wasm'), wasm_file)) -else: - asmjs_files = env.SideEffect([html_file.File(basename+'.asm.js'), html_file.File(basename+'.html.mem')], html_file) - zip_files.append(env.InstallAs([zip_dir.File('godot.asm.js'), zip_dir.File('godot.mem')], asmjs_files)) - -Zip('#bin/godot', zip_files, ZIPSUFFIX=env['PROGSUFFIX']+env['ZIPSUFFIX'], ZIPROOT=zip_dir) +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") diff --git a/platform/javascript/detect.py b/platform/javascript/detect.py index 41fe3fb02..68c8d1eea 100644 --- a/platform/javascript/detect.py +++ b/platform/javascript/detect.py @@ -1,6 +1,6 @@ import os -import sys import string +import sys def is_active(): @@ -12,7 +12,8 @@ def get_name(): def can_build(): - return os.environ.has_key("EMSCRIPTEN_ROOT") + + return (os.environ.has_key("EMSCRIPTEN_ROOT")) def get_opts(): @@ -27,12 +28,12 @@ def get_flags(): return [ ('tools', 'no'), - ('module_etc1_enabled', 'no'), ('module_theora_enabled', 'no'), ] def create(env): + # remove Windows' .exe suffix return env.Clone(tools=['textfile', 'zip'], PROGSUFFIX='') @@ -45,10 +46,26 @@ def escape_target_backslashes(target, source, env, for_signature): def configure(env): - env['ENV'] = os.environ - env.Append(CPPPATH=['#platform/javascript']) + ## Build type + + if (env["target"] == "release"): + env.Append(CCFLAGS=['-O3']) + env.Append(LINKFLAGS=['-O3']) + elif (env["target"] == "release_debug"): + env.Append(CCFLAGS=['-O2', '-DDEBUG_ENABLED']) + env.Append(LINKFLAGS=['-O2', '-s', 'ASSERTIONS=1']) + # retain function names at the cost of file size, for backtraces and profiling + env.Append(LINKFLAGS=['--profiling-funcs']) + + elif (env["target"] == "debug"): + env.Append(CCFLAGS=['-O1', '-D_DEBUG', '-g', '-DDEBUG_ENABLED']) + env.Append(LINKFLAGS=['-O1', '-g']) + + ## Compiler configuration + + env['ENV'] = os.environ env.PrependENVPath('PATH', os.environ['EMSCRIPTEN_ROOT']) env['CC'] = 'emcc' env['CXX'] = 'em++' @@ -57,6 +74,7 @@ def configure(env): # Emscripten's ar has issues with duplicate file names, so use cc env['AR'] = 'emcc' env['ARFLAGS'] = '-o' + if (os.name == 'nt'): # use TempFileMunge on Windows since some commands get too long for # cmd.exe even with spawn_fix @@ -68,26 +86,20 @@ def configure(env): env['OBJSUFFIX'] = '.bc' env['LIBSUFFIX'] = '.bc' - if (env["target"] == "release"): - env.Append(CCFLAGS=['-O3']) - env.Append(LINKFLAGS=['-O3']) - elif (env["target"] == "release_debug"): - env.Append(CCFLAGS=['-O2', '-DDEBUG_ENABLED']) - env.Append(LINKFLAGS=['-O2', '-s', 'ASSERTIONS=1']) - # retain function names at the cost of file size, for backtraces and profiling - env.Append(LINKFLAGS=['--profiling-funcs']) - elif (env["target"] == "debug"): - env.Append(CCFLAGS=['-O1', '-D_DEBUG', '-g', '-DDEBUG_ENABLED']) - env.Append(LINKFLAGS=['-O1', '-g']) + ## Compile flags - # TODO: Move that to opus module's config - if("module_opus_enabled" in env and env["module_opus_enabled"] != "no"): - env.opus_fixed_point = "yes" + env.Append(CPPPATH=['#platform/javascript']) + env.Append(CPPFLAGS=['-DJAVASCRIPT_ENABLED', '-DUNIX_ENABLED', '-DPTHREAD_NO_RENAME', '-DTYPED_METHOD_BIND', '-DNO_THREADS']) + env.Append(CPPFLAGS=['-DGLES3_ENABLED']) # These flags help keep the file size down env.Append(CPPFLAGS=["-fno-exceptions", '-DNO_SAFE_CAST', '-fno-rtti']) - env.Append(CPPFLAGS=['-DJAVASCRIPT_ENABLED', '-DUNIX_ENABLED', '-DPTHREAD_NO_RENAME', '-DTYPED_METHOD_BIND', '-DNO_THREADS']) - env.Append(CPPFLAGS=['-DGLES3_ENABLED']) + + if env['javascript_eval'] == 'yes': + env.Append(CPPFLAGS=['-DJAVASCRIPT_EVAL_ENABLED']) + + ## Link flags + env.Append(LINKFLAGS=['-s', 'USE_WEBGL2=1']) if (env['wasm'] == 'yes'): @@ -101,8 +113,6 @@ def configure(env): env.Append(LINKFLAGS=['-s', 'ASM_JS=1']) env.Append(LINKFLAGS=['--separate-asm']) - if env['javascript_eval'] == 'yes': - env.Append(CPPFLAGS=['-DJAVASCRIPT_EVAL_ENABLED']) - - - import methods + # TODO: Move that to opus module's config + if("module_opus_enabled" in env and env["module_opus_enabled"] != "no"): + env.opus_fixed_point = "yes" diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp index 4bdfdae39..b436d5236 100644 --- a/platform/javascript/export/export.cpp +++ b/platform/javascript/export/export.cpp @@ -61,6 +61,7 @@ public: virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const; virtual String get_name() const; + virtual String get_os_name() const; virtual Ref<Texture> get_logo() const; virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const; @@ -74,6 +75,12 @@ public: virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags); virtual Ref<Texture> get_run_icon() const; + virtual void get_platform_features(List<String> *r_features) { + + r_features->push_back("web"); + r_features->push_back("JavaScript"); + } + EditorExportPlatformJavaScript(); }; @@ -167,6 +174,11 @@ String EditorExportPlatformJavaScript::get_name() const { return "HTML5"; } +String EditorExportPlatformJavaScript::get_os_name() const { + + return "JavaScript"; +} + Ref<Texture> EditorExportPlatformJavaScript::get_logo() const { return logo; diff --git a/platform/javascript/godot_shell.html b/platform/javascript/godot_shell.html index 6c7069a8f..ee7399a12 100644 --- a/platform/javascript/godot_shell.html +++ b/platform/javascript/godot_shell.html @@ -83,6 +83,10 @@ color: white; } + #canvas:focus { + outline: none; + } + /* Status display * ============== */ @@ -147,7 +151,7 @@ $GODOT_HEAD_INCLUDE </head> <body> <div id="container"> - <canvas id="canvas" width="640" height="480" onclick="canvas.ownerDocument.defaultView.focus();" oncontextmenu="event.preventDefault();"> + <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> diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp index 9df26f147..0708d4619 100644 --- a/platform/javascript/os_javascript.cpp +++ b/platform/javascript/os_javascript.cpp @@ -29,8 +29,8 @@ /*************************************************************************/ #include "os_javascript.h" -#include "core/global_config.h" #include "core/io/file_access_buffered_fa.h" +#include "core/project_settings.h" #include "dom_keys.h" #include "drivers/gles3/rasterizer_gles3.h" #include "drivers/unix/dir_access_unix.h" @@ -145,6 +145,31 @@ static EM_BOOL _fullscreen_change_callback(int event_type, const EmscriptenFulls static InputDefault *_input; +static bool is_canvas_focused() { + + /* clang-format off */ + return EM_ASM_INT_V( + return document.activeElement == Module.canvas; + ); + /* clang-format on */ +} + +static void focus_canvas() { + + /* clang-format off */ + EM_ASM( + Module.canvas.focus(); + ); + /* clang-format on */ +} + +static bool _cursor_inside_canvas = true; + +static bool is_cursor_inside_canvas() { + + return _cursor_inside_canvas; +} + static EM_BOOL _mousebutton_callback(int event_type, const EmscriptenMouseEvent *mouse_event, void *user_data) { ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_MOUSEDOWN && event_type != EMSCRIPTEN_EVENT_MOUSEUP, false); @@ -164,26 +189,42 @@ static EM_BOOL _mousebutton_callback(int event_type, const EmscriptenMouseEvent } int mask = _input->get_mouse_button_mask(); - if (ev->is_pressed()) + if (ev->is_pressed()) { + // since the event is consumed, focus manually + if (!is_canvas_focused()) { + focus_canvas(); + } mask |= 1 << ev->get_button_index(); - else + } else if (mask & (1 << ev->get_button_index())) { mask &= ~(1 << ev->get_button_index()); + } else { + // release event, but press was outside the canvas, so ignore + return false; + } ev->set_button_mask(mask >> 1); _input->parse_input_event(ev); + // prevent selection dragging return true; } static EM_BOOL _mousemove_callback(int event_type, const EmscriptenMouseEvent *mouse_event, void *user_data) { ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_MOUSEMOVE, false); + OS_JavaScript *os = static_cast<OS_JavaScript *>(user_data); + int input_mask = _input->get_mouse_button_mask(); + Point2 pos = Point2(mouse_event->canvasX, mouse_event->canvasY); + // outside the canvas, only read mouse movement if dragging started inside + // the canvas; imitating desktop app behaviour + if (!is_cursor_inside_canvas() && !input_mask) + return false; Ref<InputEventMouseMotion> ev; ev.instance(); dom2godot_mod(mouse_event, ev); - ev->set_button_mask(_input->get_mouse_button_mask() >> 1); + ev->set_button_mask(input_mask >> 1); - ev->set_position(Point2(mouse_event->canvasX, mouse_event->canvasY)); + ev->set_position(pos); ev->set_global_position(ev->get_position()); ev->set_relative(_input->get_mouse_position() - ev->get_position()); @@ -191,12 +232,20 @@ static EM_BOOL _mousemove_callback(int event_type, const EmscriptenMouseEvent *m ev->set_speed(_input->get_last_mouse_speed()); _input->parse_input_event(ev); - return true; + // don't suppress mouseover/leave events + return false; } static EM_BOOL _wheel_callback(int event_type, const EmscriptenWheelEvent *wheel_event, void *user_data) { ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_WHEEL, false); + if (!is_canvas_focused()) { + if (is_cursor_inside_canvas()) { + focus_canvas(); + } else { + return false; + } + } Ref<InputEventMouseButton> ev; ev.instance(); @@ -387,6 +436,15 @@ static EM_BOOL joy_callback_func(int p_type, const EmscriptenGamepadEvent *p_eve return false; } +extern "C" { +void send_notification(int notif) { + if (notif == MainLoop::NOTIFICATION_WM_MOUSE_ENTER || notif == MainLoop::NOTIFICATION_WM_MOUSE_EXIT) { + _cursor_inside_canvas = notif == MainLoop::NOTIFICATION_WM_MOUSE_ENTER; + } + OS_JavaScript::get_singleton()->get_main_loop()->notification(notif); +} +} + void OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { print_line("Init OS"); @@ -465,17 +523,17 @@ void OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, i EM_CHECK(ev) EMSCRIPTEN_RESULT result; - SET_EM_CALLBACK("#canvas", mousemove, _mousemove_callback) + SET_EM_CALLBACK("#window", mousemove, _mousemove_callback) SET_EM_CALLBACK("#canvas", mousedown, _mousebutton_callback) - SET_EM_CALLBACK("#canvas", mouseup, _mousebutton_callback) - SET_EM_CALLBACK("#canvas", wheel, _wheel_callback) - SET_EM_CALLBACK("#canvas", touchstart, _touchpress_callback) - SET_EM_CALLBACK("#canvas", touchmove, _touchmove_callback) - SET_EM_CALLBACK("#canvas", touchend, _touchpress_callback) - SET_EM_CALLBACK("#canvas", touchcancel, _touchpress_callback) - SET_EM_CALLBACK(NULL, keydown, _keydown_callback) - SET_EM_CALLBACK(NULL, keypress, _keypress_callback) - SET_EM_CALLBACK(NULL, keyup, _keyup_callback) + SET_EM_CALLBACK("#window", mouseup, _mousebutton_callback) + SET_EM_CALLBACK("#window", wheel, _wheel_callback) + SET_EM_CALLBACK("#window", touchstart, _touchpress_callback) + SET_EM_CALLBACK("#window", touchmove, _touchmove_callback) + SET_EM_CALLBACK("#window", touchend, _touchpress_callback) + SET_EM_CALLBACK("#window", touchcancel, _touchpress_callback) + SET_EM_CALLBACK("#canvas", keydown, _keydown_callback) + SET_EM_CALLBACK("#canvas", keypress, _keypress_callback) + SET_EM_CALLBACK("#canvas", keyup, _keyup_callback) SET_EM_CALLBACK(NULL, resize, _browser_resize_callback) SET_EM_CALLBACK(NULL, fullscreenchange, _fullscreen_change_callback) SET_EM_CALLBACK_NODATA(gamepadconnected, joy_callback_func) @@ -485,9 +543,24 @@ 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); - GlobalConfig::get_singleton()->add_singleton(GlobalConfig::Singleton("JavaScript", javascript_eval)); + ProjectSettings::get_singleton()->add_singleton(ProjectSettings::Singleton("JavaScript", javascript_eval)); #endif visual_server->init(); @@ -777,20 +850,6 @@ void OS_JavaScript::main_loop_end() { main_loop->finish(); } -void OS_JavaScript::main_loop_focusout() { - - if (main_loop) - main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT); - //audio_driver_javascript.set_pause(true); -} - -void OS_JavaScript::main_loop_focusin() { - - if (main_loop) - main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN); - //audio_driver_javascript.set_pause(false); -} - void OS_JavaScript::process_accelerometer(const Vector3 &p_accelerometer) { input->set_accelerometer(p_accelerometer); @@ -828,7 +887,7 @@ String OS_JavaScript::get_data_dir() const { return get_data_dir_func(); */ return "/userfs"; - //return GlobalConfig::get_singleton()->get_singleton_object("GodotOS")->call("get_data_dir"); + //return ProjectSettings::get_singleton()->get_singleton_object("GodotOS")->call("get_data_dir"); }; String OS_JavaScript::get_executable_path() const { @@ -911,6 +970,11 @@ int OS_JavaScript::get_power_percent_left() { return power_manager->get_power_percent_left(); } +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 +} + OS_JavaScript::OS_JavaScript(const char *p_execpath, GetDataDirFunc p_get_data_dir_func) { set_cmdline(p_execpath, get_cmdline_args()); main_loop = NULL; diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h index 65269148e..24e96e20d 100644 --- a/platform/javascript/os_javascript.h +++ b/platform/javascript/os_javascript.h @@ -164,6 +164,8 @@ public: virtual int get_power_seconds_left(); virtual int get_power_percent_left(); + virtual bool _check_internal_feature_support(const String &p_feature); + OS_JavaScript(const char *p_execpath, GetDataDirFunc p_get_data_dir_func); ~OS_JavaScript(); }; diff --git a/platform/osx/audio_driver_osx.cpp b/platform/osx/audio_driver_osx.cpp index 7469d5297..d7a91b165 100644 --- a/platform/osx/audio_driver_osx.cpp +++ b/platform/osx/audio_driver_osx.cpp @@ -31,11 +31,15 @@ #include "audio_driver_osx.h" -Error AudioDriverOSX::init() { +static OSStatus outputDeviceAddressCB(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *__nullable inClientData) { + AudioDriverOSX *driver = (AudioDriverOSX *)inClientData; - active = false; - channels = 2; + driver->reopen(); + return noErr; +} + +Error AudioDriverOSX::initDevice() { AudioStreamBasicDescription strdesc; strdesc.mFormatID = kAudioFormatLinearPCM; strdesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; @@ -43,12 +47,10 @@ Error AudioDriverOSX::init() { strdesc.mSampleRate = 44100; strdesc.mFramesPerPacket = 1; strdesc.mBitsPerChannel = 16; - strdesc.mBytesPerFrame = - strdesc.mBitsPerChannel * strdesc.mChannelsPerFrame / 8; - strdesc.mBytesPerPacket = - strdesc.mBytesPerFrame * strdesc.mFramesPerPacket; + strdesc.mBytesPerFrame = strdesc.mBitsPerChannel * strdesc.mChannelsPerFrame / 8; + strdesc.mBytesPerPacket = strdesc.mBytesPerFrame * strdesc.mFramesPerPacket; - OSStatus result = noErr; + OSStatus result; AURenderCallbackStruct callback; AudioComponentDescription desc; AudioComponent comp = NULL; @@ -58,83 +60,130 @@ Error AudioDriverOSX::init() { zeromem(&desc, sizeof(desc)); desc.componentType = kAudioUnitType_Output; - desc.componentSubType = 0; /* !!! FIXME: ? */ - comp = AudioComponentFindNext(NULL, &desc); + desc.componentSubType = kAudioUnitSubType_HALOutput; desc.componentManufacturer = kAudioUnitManufacturer_Apple; + comp = AudioComponentFindNext(NULL, &desc); + ERR_FAIL_COND_V(comp == NULL, FAILED); + result = AudioComponentInstanceNew(comp, &audio_unit); ERR_FAIL_COND_V(result != noErr, FAILED); - ERR_FAIL_COND_V(comp == NULL, FAILED); - result = AudioUnitSetProperty(audio_unit, - kAudioUnitProperty_StreamFormat, - scope, bus, &strdesc, sizeof(strdesc)); + result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_StreamFormat, scope, bus, &strdesc, sizeof(strdesc)); ERR_FAIL_COND_V(result != noErr, FAILED); zeromem(&callback, sizeof(AURenderCallbackStruct)); callback.inputProc = &AudioDriverOSX::output_callback; callback.inputProcRefCon = this; - result = AudioUnitSetProperty(audio_unit, - kAudioUnitProperty_SetRenderCallback, - scope, bus, &callback, sizeof(callback)); + result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_SetRenderCallback, scope, bus, &callback, sizeof(callback)); ERR_FAIL_COND_V(result != noErr, FAILED); result = AudioUnitInitialize(audio_unit); ERR_FAIL_COND_V(result != noErr, FAILED); - result = AudioOutputUnitStart(audio_unit); + return OK; +} + +Error AudioDriverOSX::finishDevice() { + OSStatus result; + + if (active) { + result = AudioOutputUnitStop(audio_unit); + ERR_FAIL_COND_V(result != noErr, FAILED); + + active = false; + } + + result = AudioUnitUninitialize(audio_unit); + ERR_FAIL_COND_V(result != noErr, FAILED); + + return OK; +} + +Error AudioDriverOSX::init() { + OSStatus result; + + mutex = Mutex::create(); + active = false; + channels = 2; + + outputDeviceAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; + outputDeviceAddress.mScope = kAudioObjectPropertyScopeGlobal; + outputDeviceAddress.mElement = kAudioObjectPropertyElementMaster; + + result = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &outputDeviceAddress, &outputDeviceAddressCB, this); ERR_FAIL_COND_V(result != noErr, FAILED); const int samples = 1024; samples_in = memnew_arr(int32_t, samples); // whatever buffer_frames = samples / channels; - return OK; + return initDevice(); }; +Error AudioDriverOSX::reopen() { + Error err; + bool restart = false; + + lock(); + + if (active) { + restart = true; + } + + err = finishDevice(); + if (err != OK) { + ERR_PRINT("finishDevice failed"); + unlock(); + return err; + } + + err = initDevice(); + if (err != OK) { + ERR_PRINT("initDevice failed"); + unlock(); + return err; + } + + if (restart) { + start(); + } + + unlock(); + + return OK; +} + OSStatus AudioDriverOSX::output_callback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) { - AudioBuffer *abuf; AudioDriverOSX *ad = (AudioDriverOSX *)inRefCon; - bool mix = true; - - if (!ad->active) - mix = false; - else if (ad->mutex) { - mix = ad->mutex->try_lock() == OK; - }; - - if (!mix) { + if (!ad->active || !ad->try_lock()) { for (unsigned int i = 0; i < ioData->mNumberBuffers; i++) { - abuf = &ioData->mBuffers[i]; + AudioBuffer *abuf = &ioData->mBuffers[i]; zeromem(abuf->mData, abuf->mDataByteSize); }; return 0; }; - int frames_left; - for (unsigned int i = 0; i < ioData->mNumberBuffers; i++) { - abuf = &ioData->mBuffers[i]; - frames_left = inNumberFrames; + AudioBuffer *abuf = &ioData->mBuffers[i]; + int frames_left = inNumberFrames; int16_t *out = (int16_t *)abuf->mData; while (frames_left) { int frames = MIN(frames_left, ad->buffer_frames); - //ad->lock(); ad->audio_server_process(frames, ad->samples_in); - //ad->unlock(); - for (int i = 0; i < frames * ad->channels; i++) { + for (int j = 0; j < frames * ad->channels; j++) { - out[i] = ad->samples_in[i] >> 16; + out[j] = ad->samples_in[j] >> 16; } frames_left -= frames; @@ -142,14 +191,20 @@ OSStatus AudioDriverOSX::output_callback(void *inRefCon, }; }; - if (ad->mutex) - ad->mutex->unlock(); + ad->unlock(); return 0; }; void AudioDriverOSX::start() { - active = true; + if (!active) { + OSStatus result = AudioOutputUnitStart(audio_unit); + if (result != noErr) { + ERR_PRINT("AudioOutputUnitStart failed"); + } else { + active = true; + } + } }; int AudioDriverOSX::get_mix_rate() const { @@ -161,29 +216,47 @@ AudioDriver::SpeakerMode AudioDriverOSX::get_speaker_mode() const { }; void AudioDriverOSX::lock() { - if (active && mutex) + if (mutex) mutex->lock(); }; + void AudioDriverOSX::unlock() { - if (active && mutex) + if (mutex) mutex->unlock(); }; +bool AudioDriverOSX::try_lock() { + if (mutex) + return mutex->try_lock() == OK; + return true; +} + void AudioDriverOSX::finish() { + OSStatus result; - if (active) - AudioOutputUnitStop(audio_unit); + finishDevice(); - memdelete_arr(samples_in); -}; + result = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &outputDeviceAddress, &outputDeviceAddressCB, this); + if (result != noErr) { + ERR_PRINT("AudioObjectRemovePropertyListener failed"); + } -AudioDriverOSX::AudioDriverOSX() { + if (mutex) { + memdelete(mutex); + mutex = NULL; + } - mutex = Mutex::create(); //NULL; + if (samples_in) { + memdelete_arr(samples_in); + samples_in = NULL; + } }; -AudioDriverOSX::~AudioDriverOSX(){ - +AudioDriverOSX::AudioDriverOSX() { + mutex = NULL; + samples_in = NULL; }; +AudioDriverOSX::~AudioDriverOSX(){}; + #endif diff --git a/platform/osx/audio_driver_osx.h b/platform/osx/audio_driver_osx.h index 9b48dab40..287c9d6cb 100644 --- a/platform/osx/audio_driver_osx.h +++ b/platform/osx/audio_driver_osx.h @@ -35,10 +35,12 @@ #include "servers/audio_server.h" #include <AudioUnit/AudioUnit.h> +#include <CoreAudio/AudioHardware.h> class AudioDriverOSX : public AudioDriver { AudioComponentInstance audio_unit; + AudioObjectPropertyAddress outputDeviceAddress; bool active; Mutex *mutex; @@ -52,6 +54,9 @@ class AudioDriverOSX : public AudioDriver { UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData); + Error initDevice(); + Error finishDevice(); + public: const char *get_name() const { return "AudioUnit"; @@ -65,6 +70,9 @@ public: virtual void unlock(); virtual void finish(); + bool try_lock(); + Error reopen(); + AudioDriverOSX(); ~AudioDriverOSX(); }; diff --git a/platform/osx/detect.py b/platform/osx/detect.py index 39ee33ae8..d9891dda6 100644 --- a/platform/osx/detect.py +++ b/platform/osx/detect.py @@ -1,4 +1,3 @@ - import os import sys @@ -22,9 +21,7 @@ def can_build(): def get_opts(): return [ - ('force_64_bits', 'Force 64 bits binary', 'no'), ('osxcross_sdk', 'OSXCross SDK version', 'darwin14'), - ] @@ -36,36 +33,37 @@ def get_flags(): def configure(env): - env.Append(CPPPATH=['#platform/osx']) - - if (env["bits"] == "default"): - env["bits"] = "32" + ## Build type if (env["target"] == "release"): - - env.Append(CCFLAGS=['-O2', '-ffast-math', '-fomit-frame-pointer', '-ftree-vectorize', '-msse2']) + env.Prepend(CCFLAGS=['-O2', '-ffast-math', '-fomit-frame-pointer', '-ftree-vectorize', '-msse2']) elif (env["target"] == "release_debug"): - - env.Append(CCFLAGS=['-O2', '-DDEBUG_ENABLED']) + env.Prepend(CCFLAGS=['-O2', '-DDEBUG_ENABLED']) elif (env["target"] == "debug"): + env.Prepend(CCFLAGS=['-g3', '-DDEBUG_ENABLED', '-DDEBUG_MEMORY_ENABLED']) - env.Append(CCFLAGS=['-g3', '-DDEBUG_ENABLED', '-DDEBUG_MEMORY_ENABLED']) + ## Architecture - if (not os.environ.has_key("OSXCROSS_ROOT")): - # regular native build - if (env["bits"] == "64"): - env.Append(CCFLAGS=['-arch', 'x86_64']) - env.Append(LINKFLAGS=['-arch', 'x86_64']) + is64 = sys.maxsize > 2**32 + if (env["bits"] == "default"): + env["bits"] = "64" if is64 else "32" + + ## Compiler configuration + + if (not os.environ.has_key("OSXCROSS_ROOT")): # regular native build + if (env["bits"] == "fat"): + env.Append(CCFLAGS=['-arch', 'i386', '-arch', 'x86_64']) + env.Append(LINKFLAGS=['-arch', 'i386', '-arch', 'x86_64']) elif (env["bits"] == "32"): env.Append(CCFLAGS=['-arch', 'i386']) env.Append(LINKFLAGS=['-arch', 'i386']) - else: - env.Append(CCFLAGS=['-arch', 'i386', '-arch', 'x86_64']) - env.Append(LINKFLAGS=['-arch', 'i386', '-arch', 'x86_64']) - else: - # osxcross build + else: # 64-bit, default + env.Append(CCFLAGS=['-arch', 'x86_64']) + env.Append(LINKFLAGS=['-arch', 'x86_64']) + + else: # osxcross build root = os.environ.get("OSXCROSS_ROOT", 0) if env["bits"] == "64": basecmd = root + "/target/bin/x86_64-apple-" + env["osxcross_sdk"] + "-" @@ -78,26 +76,22 @@ def configure(env): env['RANLIB'] = basecmd + "ranlib" env['AS'] = basecmd + "as" - env.Append(CPPFLAGS=["-DAPPLE_STYLE_KEYS"]) - env.Append(CPPFLAGS=['-DUNIX_ENABLED', '-DGLES2_ENABLED', '-DOSX_ENABLED']) - env.Append(CPPFLAGS=["-mmacosx-version-min=10.9"]) - env.Append(LIBS=['pthread']) - #env.Append(CPPFLAGS=['-F/Developer/SDKs/MacOSX10.4u.sdk/System/Library/Frameworks', '-isysroot', '/Developer/SDKs/MacOSX10.4u.sdk', '-mmacosx-version-min=10.4']) - #env.Append(LINKFLAGS=['-mmacosx-version-min=10.4', '-isysroot', '/Developer/SDKs/MacOSX10.4u.sdk', '-Wl,-syslibroot,/Developer/SDKs/MacOSX10.4u.sdk']) - env.Append(LINKFLAGS=['-framework', 'Cocoa', '-framework', 'Carbon', '-framework', 'OpenGL', '-framework', 'AGL', '-framework', 'AudioUnit', '-lz', '-framework', 'IOKit', '-framework', 'ForceFeedback']) - env.Append(LINKFLAGS=["-mmacosx-version-min=10.9"]) - if (env["CXX"] == "clang++"): env.Append(CPPFLAGS=['-DTYPED_METHOD_BIND']) env["CC"] = "clang" env["LD"] = "clang++" - import methods + ## Dependencies + + if (env['builtin_libtheora'] != 'no'): + env["x86_libtheora_opt_gcc"] = True - env.Append(BUILDERS={'GLSL120': env.Builder(action=methods.build_legacygl_headers, suffix='glsl.h', src_suffix='.glsl')}) - env.Append(BUILDERS={'GLSL': env.Builder(action=methods.build_glsl_headers, suffix='glsl.h', src_suffix='.glsl')}) - env.Append(BUILDERS={'GLSL120GLES': env.Builder(action=methods.build_gles2_headers, suffix='glsl.h', src_suffix='.glsl')}) - #env.Append( BUILDERS = { 'HLSL9' : env.Builder(action = methods.build_hlsl_dx9_headers, suffix = 'hlsl.h',src_suffix = '.hlsl') } ) + ## Flags + + env.Append(CPPPATH=['#platform/osx']) + env.Append(CPPFLAGS=['-DOSX_ENABLED', '-DUNIX_ENABLED', '-DGLES2_ENABLED', '-DAPPLE_STYLE_KEYS']) + env.Append(LINKFLAGS=['-framework', 'Cocoa', '-framework', 'Carbon', '-framework', 'OpenGL', '-framework', 'AGL', '-framework', 'AudioUnit', '-framework', 'CoreAudio', '-lz', '-framework', 'IOKit', '-framework', 'ForceFeedback']) + env.Append(LIBS=['pthread']) - env["x86_libtheora_opt_gcc"] = True - + env.Append(CPPFLAGS=['-mmacosx-version-min=10.9']) + env.Append(LINKFLAGS=['-mmacosx-version-min=10.9']) diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp index 066adde78..03f424de8 100644 --- a/platform/osx/export/export.cpp +++ b/platform/osx/export/export.cpp @@ -32,15 +32,16 @@ #include "editor/editor_export.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" -#include "global_config.h" #include "io/marshalls.h" #include "io/resource_saver.h" #include "io/zip_io.h" #include "os/file_access.h" #include "os/os.h" #include "platform/osx/logo.gen.h" +#include "project_settings.h" #include "string.h" #include "version.h" +#include <sys/stat.h> class EditorExportPlatformOSX : public EditorExportPlatform { @@ -52,6 +53,10 @@ class EditorExportPlatformOSX : public EditorExportPlatform { void _fix_plist(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &plist, const String &p_binary); void _make_icon(const Ref<Image> &p_icon, Vector<uint8_t> &p_data); +#ifdef OSX_ENABLED + Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path); + Error _create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name); +#endif protected: virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features); @@ -59,13 +64,25 @@ protected: public: virtual String get_name() const { return "Mac OSX"; } + virtual String get_os_name() const { return "OSX"; } virtual Ref<Texture> get_logo() const { return logo; } +#ifdef OSX_ENABLED + virtual String get_binary_extension() const { return "dmg"; } +#else virtual String get_binary_extension() const { return "zip"; } +#endif virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0); virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const; + virtual void get_platform_features(List<String> *r_features) { + + r_features->push_back("pc"); + r_features->push_back("s3tc"); + r_features->push_back("OSX"); + } + EditorExportPlatformOSX(); ~EditorExportPlatformOSX(); }; @@ -90,6 +107,11 @@ void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options) r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/bits_mode", PROPERTY_HINT_ENUM, "Fat (32 & 64 bits),64 bits,32 bits"), 0)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "display/high_res"), false)); + +#ifdef OSX_ENABLED + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/entitlements"), "")); +#endif } void EditorExportPlatformOSX::_make_icon(const Ref<Image> &p_icon, Vector<uint8_t> &p_data) { @@ -171,27 +193,68 @@ void EditorExportPlatformOSX::_fix_plist(const Ref<EditorExportPreset> &p_preset } CharString cs = strnew.utf8(); - plist.resize(cs.size()); - for (int i = 9; i < cs.size(); i++) { + plist.resize(cs.size() - 1); + for (int i = 0; i < cs.size() - 1; i++) { plist[i] = cs[i]; } } +#ifdef OSX_ENABLED +/** + If we're running the OSX version of the Godot editor we'll: + - export our application bundle to a temporary folder + - attempt to code sign it + - and then wrap it up in a DMG +**/ + +Error EditorExportPlatformOSX::_code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path) { + List<String> args; + if (p_preset->get("codesign/entitlements") != "") { + /* this should point to our entitlements.plist file that sandboxes our application, I don't know if this should also be placed in our app bundle */ + args.push_back("-entitlements"); + args.push_back(p_preset->get("codesign/entitlements")); + } + args.push_back("-s"); + args.push_back(p_preset->get("codesign/identity")); + args.push_back("-v"); /* provide some more feedback */ + args.push_back(p_path); + Error err = OS::get_singleton()->execute("/usr/bin/codesign", args, true); + ERR_FAIL_COND_V(err, err); + + return OK; +} + +Error EditorExportPlatformOSX::_create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name) { + List<String> args; + args.push_back("create"); + args.push_back(p_dmg_path); + args.push_back("-volname"); + args.push_back(p_pkg_name); + args.push_back("-fs"); + args.push_back("HFS+"); + args.push_back("-srcfolder"); + args.push_back(p_app_path_name); + Error err = OS::get_singleton()->execute("/usr/bin/hdiutil", args, true); + ERR_FAIL_COND_V(err, err); + + return OK; +} + Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { - String src_pkg; + String src_pkg_name; - EditorProgress ep("export", "Exporting for OSX", 104); + EditorProgress ep("export", "Exporting for OSX", 3); if (p_debug) - src_pkg = p_preset->get("custom_package/debug"); + src_pkg_name = p_preset->get("custom_package/debug"); else - src_pkg = p_preset->get("custom_package/release"); + src_pkg_name = p_preset->get("custom_package/release"); - if (src_pkg == "") { + if (src_pkg_name == "") { String err; - src_pkg = find_export_template("osx.zip", &err); - if (src_pkg == "") { + src_pkg_name = find_export_template("osx.zip", &err); + if (src_pkg_name == "") { EditorNode::add_io_error(err); return ERR_FILE_NOT_FOUND; } @@ -202,20 +265,15 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p ep.step("Creating app", 0); - unzFile pkg = unzOpen2(src_pkg.utf8().get_data(), &io); - if (!pkg) { + unzFile src_pkg_zip = unzOpen2(src_pkg_name.utf8().get_data(), &io); + if (!src_pkg_zip) { - EditorNode::add_io_error("Could not find template app to export:\n" + src_pkg); + EditorNode::add_io_error("Could not find template app to export:\n" + src_pkg_name); return ERR_FILE_NOT_FOUND; } - ERR_FAIL_COND_V(!pkg, ERR_CANT_OPEN); - int ret = unzGoToFirstFile(pkg); - - zlib_filefunc_def io2 = io; - FileAccess *dst_f = NULL; - io2.opaque = &dst_f; - zipFile dpkg = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, NULL, &io2); + ERR_FAIL_COND_V(!src_pkg_zip, ERR_CANT_OPEN); + int ret = unzGoToFirstFile(src_pkg_zip); String binary_to_use = "godot_osx_" + String(p_debug ? "debug" : "release") + "."; int bits_mode = p_preset->get("application/bits_mode"); @@ -225,19 +283,39 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p String pkg_name; if (p_preset->get("application/name") != "") pkg_name = p_preset->get("application/name"); // app_name - else if (String(GlobalConfig::get_singleton()->get("application/name")) != "") - pkg_name = String(GlobalConfig::get_singleton()->get("application/name")); + else if (String(ProjectSettings::get_singleton()->get("application/config/name")) != "") + pkg_name = String(ProjectSettings::get_singleton()->get("application/config/name")); else pkg_name = "Unnamed"; + // We're on OSX so we can export to DMG, but first we create our application bundle + String tmp_app_path_name = p_path.get_base_dir() + "/" + pkg_name + ".app"; + print_line("Exporting to " + tmp_app_path_name); + DirAccess *tmp_app_path = DirAccess::create_for_path(tmp_app_path_name); + ERR_FAIL_COND_V(!tmp_app_path, ERR_CANT_CREATE) + + ///@TODO We should delete the existing application bundle especially if we attempt to code sign it, but what is a safe way to do this? Maybe call system function so it moves to trash? + // tmp_app_path->erase_contents_recursive(); + + // Create our folder structure or rely on unzip? + print_line("Creating " + tmp_app_path_name + "/Contents/MacOS"); + Error dir_err = tmp_app_path->make_dir_recursive(tmp_app_path_name + "/Contents/MacOS"); + ERR_FAIL_COND_V(dir_err, ERR_CANT_CREATE) + print_line("Creating " + tmp_app_path_name + "/Contents/Resources"); + dir_err = tmp_app_path->make_dir_recursive(tmp_app_path_name + "/Contents/Resources"); + ERR_FAIL_COND_V(dir_err, ERR_CANT_CREATE) + + /* Now process our template */ bool found_binary = false; + int total_size = 0; while (ret == UNZ_OK) { + bool is_execute = false; //get filename unz_file_info info; char fname[16384]; - ret = unzGetCurrentFileInfo(pkg, &info, fname, 16384, NULL, 0, NULL, 0); + ret = unzGetCurrentFileInfo(src_pkg_zip, &info, fname, 16384, NULL, 0, NULL, 0); String file = fname; @@ -246,9 +324,9 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p data.resize(info.uncompressed_size); //read - unzOpenCurrentFile(pkg); - unzReadCurrentFile(pkg, data.ptr(), data.size()); - unzCloseCurrentFile(pkg); + unzOpenCurrentFile(src_pkg_zip); + unzReadCurrentFile(src_pkg_zip, data.ptr(), data.size()); + unzCloseCurrentFile(src_pkg_zip); //write @@ -261,10 +339,11 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p if (file.begins_with("Contents/MacOS/godot_")) { if (file != "Contents/MacOS/" + binary_to_use) { - ret = unzGoToNextFile(pkg); + ret = unzGoToNextFile(src_pkg_zip); continue; //ignore! } found_binary = true; + is_execute = true; file = "Contents/MacOS/" + pkg_name; } @@ -274,7 +353,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p if (p_preset->get("application/icon") != "") iconpath = p_preset->get("application/icon"); else - iconpath = GlobalConfig::get_singleton()->get("application/icon"); + iconpath = ProjectSettings::get_singleton()->get("application/config/icon"); print_line("icon? " + iconpath); if (iconpath != "") { Ref<Image> icon; @@ -288,11 +367,206 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p //bleh? } - file = pkg_name + ".app/" + file; + if (data.size() > 0) { + print_line("ADDING: " + file + " size: " + itos(data.size())); + total_size += data.size(); + + /* write it into our application bundle */ + file = tmp_app_path_name + "/" + file; + + /* write the file, need to add chmod */ + FileAccess *f = FileAccess::open(file, FileAccess::WRITE); + ERR_FAIL_COND_V(!f, ERR_CANT_CREATE) + f->store_buffer(data.ptr(), data.size()); + f->close(); + memdelete(f); + + if (is_execute) { + // we need execute rights on this file + chmod(file.utf8().get_data(), 0755); + } else { + // seems to already be set correctly + // chmod(file.utf8().get_data(), 0644); + } + } + + ret = unzGoToNextFile(src_pkg_zip); + } + + /* we're done with our source zip */ + unzClose(src_pkg_zip); + + if (!found_binary) { + ERR_PRINTS("Requested template binary '" + binary_to_use + "' not found. It might be missing from your template archive."); + unzClose(src_pkg_zip); + return ERR_FILE_NOT_FOUND; + } + + ep.step("Making PKG", 1); + + String pack_path = tmp_app_path_name + "/Contents/Resources/" + pkg_name + ".pck"; + Error err = save_pack(p_preset, pack_path); + // chmod(pack_path.utf8().get_data(), 0644); + + if (err) { + return err; + } + + /* see if we can code sign our new package */ + if (p_preset->get("codesign/identity") != "") { + ep.step("Code signing bundle", 2); + + /* the order in which we code sign is important, this is a bit of a shame or we could do this in our loop that extracts the files from our ZIP */ + + // start with our application + err = _code_sign(p_preset, tmp_app_path_name + "/Contents/MacOS/" + pkg_name); + ERR_FAIL_COND_V(err, err); + + ///@TODO we should check the contents of /Contents/Frameworks for frameworks to sign + + // we should probably loop through all resources and sign them? + err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Resources/icon.icns"); + ERR_FAIL_COND_V(err, err); + err = _code_sign(p_preset, pack_path); + ERR_FAIL_COND_V(err, err); + err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Info.plist"); + ERR_FAIL_COND_V(err, err); + } + + /* and finally create a DMG */ + ep.step("Making DMG", 3); + err = _create_dmg(p_path, pkg_name, tmp_app_path_name); + ERR_FAIL_COND_V(err, err); + + return OK; +} + +#else + +/** + When exporting for OSX from any other platform we don't have access to code signing or creating DMGs so we'll wrap the bundle into a zip file. + + Should probably find a nicer way to have just one export method instead of duplicating the method like this but I would the code got very + messy with switches inside of it. +**/ +Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { + + String src_pkg_name; + + EditorProgress ep("export", "Exporting for OSX", 104); + + if (p_debug) + src_pkg_name = p_preset->get("custom_package/debug"); + else + src_pkg_name = p_preset->get("custom_package/release"); + + if (src_pkg_name == "") { + String err; + src_pkg_name = find_export_template("osx.zip", &err); + if (src_pkg_name == "") { + EditorNode::add_io_error(err); + return ERR_FILE_NOT_FOUND; + } + } + + FileAccess *src_f = NULL; + zlib_filefunc_def io = zipio_create_io_from_file(&src_f); + + ep.step("Creating app", 0); + + unzFile src_pkg_zip = unzOpen2(src_pkg_name.utf8().get_data(), &io); + if (!src_pkg_zip) { + + EditorNode::add_io_error("Could not find template app to export:\n" + src_pkg_name); + return ERR_FILE_NOT_FOUND; + } + + ERR_FAIL_COND_V(!src_pkg_zip, ERR_CANT_OPEN); + int ret = unzGoToFirstFile(src_pkg_zip); + + String binary_to_use = "godot_osx_" + String(p_debug ? "debug" : "release") + "."; + int bits_mode = p_preset->get("application/bits_mode"); + binary_to_use += String(bits_mode == 0 ? "fat" : bits_mode == 1 ? "64" : "32"); + + print_line("binary: " + binary_to_use); + String pkg_name; + if (p_preset->get("application/name") != "") + pkg_name = p_preset->get("application/name"); // app_name + else if (String(ProjectSettings::get_singleton()->get("application/config/name")) != "") + pkg_name = String(ProjectSettings::get_singleton()->get("application/config/name")); + else + pkg_name = "Unnamed"; + + /* Open our destination zip file */ + zlib_filefunc_def io2 = io; + FileAccess *dst_f = NULL; + io2.opaque = &dst_f; + zipFile dst_pkg_zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, NULL, &io2); + + bool found_binary = false; + + while (ret == UNZ_OK) { + + //get filename + unz_file_info info; + char fname[16384]; + ret = unzGetCurrentFileInfo(src_pkg_zip, &info, fname, 16384, NULL, 0, NULL, 0); + + String file = fname; + + print_line("READ: " + file); + Vector<uint8_t> data; + data.resize(info.uncompressed_size); + + //read + unzOpenCurrentFile(src_pkg_zip); + unzReadCurrentFile(src_pkg_zip, data.ptr(), data.size()); + unzCloseCurrentFile(src_pkg_zip); + + //write + + file = file.replace_first("osx_template.app/", ""); + + if (file == "Contents/Info.plist") { + print_line("parse plist"); + _fix_plist(p_preset, data, pkg_name); + } + + if (file.begins_with("Contents/MacOS/godot_")) { + if (file != "Contents/MacOS/" + binary_to_use) { + ret = unzGoToNextFile(src_pkg_zip); + continue; //ignore! + } + found_binary = true; + file = "Contents/MacOS/" + pkg_name; + } + + if (file == "Contents/Resources/icon.icns") { + //see if there is an icon + String iconpath; + if (p_preset->get("application/icon") != "") + iconpath = p_preset->get("application/icon"); + else + iconpath = ProjectSettings::get_singleton()->get("application/config/icon"); + print_line("icon? " + iconpath); + if (iconpath != "") { + Ref<Image> icon; + icon.instance(); + icon->load(iconpath); + if (!icon->empty()) { + print_line("loaded?"); + _make_icon(icon, data); + } + } + //bleh? + } if (data.size() > 0) { print_line("ADDING: " + file + " size: " + itos(data.size())); + /* add it to our zip file */ + file = pkg_name + ".app/" + file; + zip_fileinfo fi; fi.tmz_date.tm_hour = info.tmu_date.tm_hour; fi.tmz_date.tm_min = info.tmu_date.tm_min; @@ -304,7 +578,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p fi.internal_fa = info.internal_fa; fi.external_fa = info.external_fa; - int err = zipOpenNewFileInZip(dpkg, + int err = zipOpenNewFileInZip(dst_pkg_zip, file.utf8().get_data(), &fi, NULL, @@ -316,37 +590,37 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p Z_DEFAULT_COMPRESSION); print_line("OPEN ERR: " + itos(err)); - err = zipWriteInFileInZip(dpkg, data.ptr(), data.size()); + err = zipWriteInFileInZip(dst_pkg_zip, data.ptr(), data.size()); print_line("WRITE ERR: " + itos(err)); - zipCloseFileInZip(dpkg); + zipCloseFileInZip(dst_pkg_zip); } - ret = unzGoToNextFile(pkg); + ret = unzGoToNextFile(src_pkg_zip); } if (!found_binary) { ERR_PRINTS("Requested template binary '" + binary_to_use + "' not found. It might be missing from your template archive."); - zipClose(dpkg, NULL); - unzClose(pkg); + zipClose(dst_pkg_zip, NULL); + unzClose(src_pkg_zip); return ERR_FILE_NOT_FOUND; } ep.step("Making PKG", 1); - String pack_path = EditorSettings::get_singleton()->get_settings_path() + "/tmp/data.pck"; + String pack_path = EditorSettings::get_singleton()->get_settings_path() + "/tmp/" + pkg_name + ".pck"; Error err = save_pack(p_preset, pack_path); if (err) { - zipClose(dpkg, NULL); - unzClose(pkg); + zipClose(dst_pkg_zip, NULL); + unzClose(src_pkg_zip); return err; } { //write datapack - zipOpenNewFileInZip(dpkg, - (pkg_name + ".app/Contents/Resources/data.pck").utf8().get_data(), + zipOpenNewFileInZip(dst_pkg_zip, + (pkg_name + ".app/Contents/Resources/" + pkg_name + ".pck").utf8().get_data(), NULL, NULL, 0, @@ -366,17 +640,18 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p int r = pf->get_buffer(buf, BSIZE); if (r <= 0) break; - zipWriteInFileInZip(dpkg, buf, r); + zipWriteInFileInZip(dst_pkg_zip, buf, r); } - zipCloseFileInZip(dpkg); + zipCloseFileInZip(dst_pkg_zip); memdelete(pf); } - zipClose(dpkg, NULL); - unzClose(pkg); + zipClose(dst_pkg_zip, NULL); + unzClose(src_pkg_zip); return OK; } +#endif bool EditorExportPlatformOSX::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h index dc3e88df2..cb9dd1dd8 100644 --- a/platform/osx/os_osx.h +++ b/platform/osx/os_osx.h @@ -101,11 +101,7 @@ public: bool maximized; bool zoomed; - Vector<Rect2> screens; - Vector<int> screen_dpi; - Size2 window_size; - int current_screen; Rect2 restore_rect; power_osx *power_manager; @@ -117,6 +113,8 @@ public: return 1.0; } + void _update_window(); + float display_scale; protected: @@ -208,6 +206,8 @@ public: virtual int get_power_seconds_left(); virtual int get_power_percent_left(); + virtual bool _check_internal_feature_support(const String &p_feature); + void run(); void set_mouse_mode(MouseMode p_mode); diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index ad1e308ae..4a01532d8 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -80,6 +80,7 @@ static int mouse_y = 0; static int prev_mouse_x = 0; static int prev_mouse_y = 0; static int button_mask = 0; +static bool mouse_down_control = false; @interface GodotApplication : NSApplication @end @@ -151,6 +152,46 @@ static int button_mask = 0; return NO; } +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 +- (void)windowDidEnterFullScreen:(NSNotification *)notification { + OS_OSX::singleton->zoomed = true; +} + +- (void)windowDidExitFullScreen:(NSNotification *)notification { + OS_OSX::singleton->zoomed = false; +} +#endif // MAC_OS_X_VERSION_MAX_ALLOWED + +- (void)windowDidChangeBackingProperties:(NSNotification *)notification { + if (!OS_OSX::singleton) + return; + + NSWindow *window = (NSWindow *)[notification object]; + CGFloat newBackingScaleFactor = [window backingScaleFactor]; + CGFloat oldBackingScaleFactor = [[[notification userInfo] objectForKey:@"NSBackingPropertyOldScaleFactorKey"] doubleValue]; + + if (newBackingScaleFactor != oldBackingScaleFactor) { + //Set new display scale and window size + OS_OSX::singleton->display_scale = newBackingScaleFactor; + + const NSRect contentRect = [OS_OSX::singleton->window_view frame]; + const NSRect fbRect = contentRect; //convertRectToBacking(contentRect); + + OS_OSX::singleton->window_size.width = fbRect.size.width * OS_OSX::singleton->display_scale; + OS_OSX::singleton->window_size.height = fbRect.size.height * OS_OSX::singleton->display_scale; + + //Update context + if (OS_OSX::singleton->main_loop) { + [OS_OSX::singleton->context update]; + + //Force window resize ??? + NSRect frame = [OS_OSX::singleton->window_object frame]; + [OS_OSX::singleton->window_object setFrame:NSMakeRect(frame.origin.x, frame.origin.y, 1, 1) display:YES]; + [OS_OSX::singleton->window_object setFrame:frame display:YES]; + } + } +} + - (void)windowDidResize:(NSNotification *)notification { [OS_OSX::singleton->context update]; @@ -160,6 +201,12 @@ static int button_mask = 0; OS_OSX::singleton->window_size.width = fbRect.size.width * OS_OSX::singleton->display_scale; OS_OSX::singleton->window_size.height = fbRect.size.height * OS_OSX::singleton->display_scale; + if (OS_OSX::singleton->main_loop) { + Main::force_redraw(); + //Event retrieval blocks until resize is over. Call Main::iteration() directly. + Main::iteration(); + } + /* _GodotInputFramebufferSize(window, fbRect.size.width, fbRect.size.height); _GodotInputWindowSize(window, contentRect.size.width, contentRect.size.height); @@ -285,41 +332,48 @@ static int button_mask = 0; //setModeCursor(window, window->cursorMode); } -- (void)mouseDown:(NSEvent *)event { - - button_mask |= BUTTON_MASK_LEFT; +static void _mouseDownEvent(NSEvent *event, int index, int mask, bool pressed) { + if (pressed) { + button_mask |= mask; + } else { + button_mask &= ~mask; + } Ref<InputEventMouseButton> mb; mb.instance(); get_key_modifier_state([event modifierFlags], mb); - mb->set_button_index(BUTTON_LEFT); - mb->set_pressed(true); + mb->set_button_index(index); + mb->set_pressed(pressed); mb->set_position(Vector2(mouse_x, mouse_y)); mb->set_global_position(Vector2(mouse_x, mouse_y)); mb->set_button_mask(button_mask); - mb->set_doubleclick([event clickCount] == 2); + if (index == BUTTON_LEFT && pressed) { + mb->set_doubleclick([event clickCount] == 2); + } OS_OSX::singleton->push_input(mb); } +- (void)mouseDown:(NSEvent *)event { + if (([event modifierFlags] & NSControlKeyMask)) { + mouse_down_control = true; + _mouseDownEvent(event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, true); + } else { + mouse_down_control = false; + _mouseDownEvent(event, BUTTON_LEFT, BUTTON_MASK_LEFT, true); + } +} + - (void)mouseDragged:(NSEvent *)event { [self mouseMoved:event]; } - (void)mouseUp:(NSEvent *)event { - - button_mask &= ~BUTTON_MASK_LEFT; - Ref<InputEventMouseButton> mb; - mb.instance(); - - get_key_modifier_state([event modifierFlags], mb); - mb->set_button_index(BUTTON_LEFT); - mb->set_pressed(false); - mb->set_position(Vector2(mouse_x, mouse_y)); - mb->set_global_position(Vector2(mouse_x, mouse_y)); - mb->set_button_mask(button_mask); - mb->set_doubleclick([event clickCount] == 2); - OS_OSX::singleton->push_input(mb); + if (mouse_down_control) { + _mouseDownEvent(event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, false); + } else { + _mouseDownEvent(event, BUTTON_LEFT, BUTTON_MASK_LEFT, false); + } } - (void)mouseMoved:(NSEvent *)event { @@ -347,20 +401,7 @@ static int button_mask = 0; } - (void)rightMouseDown:(NSEvent *)event { - - button_mask |= BUTTON_MASK_RIGHT; - - Ref<InputEventMouseButton> mb; - mb.instance(); - - get_key_modifier_state([event modifierFlags], mb); - mb->set_button_index(BUTTON_RIGHT); - mb->set_pressed(true); - mb->set_position(Vector2(mouse_x, mouse_y)); - mb->set_global_position(Vector2(mouse_x, mouse_y)); - mb->set_button_mask(button_mask); - mb->set_doubleclick([event clickCount] == 2); - OS_OSX::singleton->push_input(mb); + _mouseDownEvent(event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, true); } - (void)rightMouseDragged:(NSEvent *)event { @@ -368,20 +409,7 @@ static int button_mask = 0; } - (void)rightMouseUp:(NSEvent *)event { - - button_mask &= ~BUTTON_MASK_RIGHT; - - Ref<InputEventMouseButton> mb; - mb.instance(); - - get_key_modifier_state([event modifierFlags], mb); - mb->set_button_index(BUTTON_RIGHT); - mb->set_pressed(false); - mb->set_position(Vector2(mouse_x, mouse_y)); - mb->set_global_position(Vector2(mouse_x, mouse_y)); - mb->set_button_mask(button_mask); - mb->set_doubleclick([event clickCount] == 2); - OS_OSX::singleton->push_input(mb); + _mouseDownEvent(event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, false); } - (void)otherMouseDown:(NSEvent *)event { @@ -389,19 +417,7 @@ static int button_mask = 0; if ((int)[event buttonNumber] != 2) return; - button_mask |= BUTTON_MASK_MIDDLE; - - Ref<InputEventMouseButton> mb; - mb.instance(); - - get_key_modifier_state([event modifierFlags], mb); - mb->set_button_index(BUTTON_MIDDLE); - mb->set_pressed(true); - mb->set_position(Vector2(mouse_x, mouse_y)); - mb->set_global_position(Vector2(mouse_x, mouse_y)); - mb->set_button_mask(button_mask); - mb->set_doubleclick([event clickCount] == 2); - OS_OSX::singleton->push_input(mb); + _mouseDownEvent(event, BUTTON_MIDDLE, BUTTON_MASK_MIDDLE, true); } - (void)otherMouseDragged:(NSEvent *)event { @@ -413,19 +429,7 @@ static int button_mask = 0; if ((int)[event buttonNumber] != 2) return; - button_mask &= ~BUTTON_MASK_MIDDLE; - - Ref<InputEventMouseButton> mb; - mb.instance(); - - get_key_modifier_state([event modifierFlags], mb); - mb->set_button_index(BUTTON_MIDDLE); - mb->set_pressed(false); - mb->set_position(Vector2(mouse_x, mouse_y)); - mb->set_global_position(Vector2(mouse_x, mouse_y)); - mb->set_button_mask(button_mask); - mb->set_doubleclick([event clickCount] == 2); - OS_OSX::singleton->push_input(mb); + _mouseDownEvent(event, BUTTON_MIDDLE, BUTTON_MASK_MIDDLE, false); } - (void)mouseExited:(NSEvent *)event { @@ -554,7 +558,7 @@ static int translateKey(unsigned int key) { /* 49 */ KEY_UNKNOWN, /* VolumeDown */ /* 4a */ KEY_UNKNOWN, /* Mute */ /* 4b */ KEY_KP_DIVIDE, - /* 4c */ KEY_KP_ENTER, + /* 4c */ KEY_ENTER, /* 4d */ KEY_UNKNOWN, /* 4e */ KEY_KP_SUBTRACT, /* 4f */ KEY_UNKNOWN, @@ -921,6 +925,8 @@ void OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_au [NSApp activateIgnoringOtherApps:YES]; + _update_window(); + [window_object makeKeyAndOrderFront:nil]; if (p_desired.fullscreen) @@ -970,32 +976,6 @@ void OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_au _ensure_data_dir(); - NSArray *screenArray = [NSScreen screens]; - printf("nscreen count %i\n", (int)[screenArray count]); - for (int i = 0; i < [screenArray count]; i++) { - - float displayScale = 1.0; - - if (display_scale > 1.0 && [[screenArray objectAtIndex:i] respondsToSelector:@selector(backingScaleFactor)]) { - displayScale = [[screenArray objectAtIndex:i] backingScaleFactor]; - } - - NSRect nsrect = [[screenArray objectAtIndex:i] visibleFrame]; - Rect2 rect = Rect2(nsrect.origin.x, nsrect.origin.y, nsrect.size.width, nsrect.size.height); - rect.position *= displayScale; - rect.size *= displayScale; - screens.push_back(rect); - - NSDictionary *description = [[screenArray objectAtIndex:i] deviceDescription]; - NSSize displayPixelSize = [[description objectForKey:NSDeviceSize] sizeValue]; - CGSize displayPhysicalSize = CGDisplayScreenSize( - [[description objectForKey:@"NSScreenNumber"] unsignedIntValue]); - - //printf("width: %i pwidth %i rect width %i\n",int(displayPixelSize.width*displayScale),int(displayPhysicalSize.width*displayScale),int(nsrect.size.width)); - int dpi = (displayPixelSize.width * 25.4f / displayPhysicalSize.width) * displayScale; - - screen_dpi.push_back(dpi); - }; restore_rect = Rect2(get_window_position(), get_window_size()); } @@ -1016,8 +996,6 @@ void OS_OSX::finalize() { physics_2d_server->finish(); memdelete(physics_2d_server); - - screens.clear(); } void OS_OSX::set_main_loop(MainLoop *p_main_loop) { @@ -1267,37 +1245,105 @@ void OS_OSX::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) con } int OS_OSX::get_screen_count() const { - - return screens.size(); + NSArray *screenArray = [NSScreen screens]; + return [screenArray count]; }; int OS_OSX::get_current_screen() const { + Vector2 wpos = get_window_position(); - return current_screen; + int count = get_screen_count(); + for (int i = 0; i < count; i++) { + Point2 pos = get_screen_position(i); + Size2 size = get_screen_size(i); + if ((wpos.x >= pos.x && wpos.x < pos.x + size.width) && (wpos.y >= pos.y && wpos.y < pos.y + size.height)) + return i; + } + return 0; }; void OS_OSX::set_current_screen(int p_screen) { - - current_screen = p_screen; + Vector2 wpos = get_window_position() - get_screen_position(get_current_screen()); + set_window_position(wpos + get_screen_position(p_screen)); }; Point2 OS_OSX::get_screen_position(int p_screen) const { + NSArray *screenArray = [NSScreen screens]; + if (p_screen < [screenArray count]) { + float displayScale = 1.0; - ERR_FAIL_INDEX_V(p_screen, screens.size(), Point2()); - return screens[p_screen].position; -}; + if (display_scale > 1.0 && [[screenArray objectAtIndex:p_screen] respondsToSelector:@selector(backingScaleFactor)]) { + displayScale = [[screenArray objectAtIndex:p_screen] backingScaleFactor]; + } + + NSRect nsrect = [[screenArray objectAtIndex:p_screen] frame]; + return Point2(nsrect.origin.x, nsrect.origin.y) * displayScale; + } + + return Point2(); +} int OS_OSX::get_screen_dpi(int p_screen) const { + NSArray *screenArray = [NSScreen screens]; + if (p_screen < [screenArray count]) { + float displayScale = 1.0; + + if (display_scale > 1.0 && [[screenArray objectAtIndex:p_screen] respondsToSelector:@selector(backingScaleFactor)]) { + displayScale = [[screenArray objectAtIndex:p_screen] backingScaleFactor]; + } + + NSDictionary *description = [[screenArray objectAtIndex:p_screen] deviceDescription]; + NSSize displayPixelSize = [[description objectForKey:NSDeviceSize] sizeValue]; + CGSize displayPhysicalSize = CGDisplayScreenSize( + [[description objectForKey:@"NSScreenNumber"] unsignedIntValue]); + + return (displayPixelSize.width * 25.4f / displayPhysicalSize.width) * displayScale; + } - ERR_FAIL_INDEX_V(p_screen, screens.size(), 72); - return screen_dpi[p_screen]; + return 72; } Size2 OS_OSX::get_screen_size(int p_screen) const { + NSArray *screenArray = [NSScreen screens]; + if (p_screen < [screenArray count]) { + float displayScale = 1.0; - ERR_FAIL_INDEX_V(p_screen, screens.size(), Point2()); - return screens[p_screen].size; -}; + if (display_scale > 1.0 && [[screenArray objectAtIndex:p_screen] respondsToSelector:@selector(backingScaleFactor)]) { + displayScale = [[screenArray objectAtIndex:p_screen] backingScaleFactor]; + } + + // Note: Use frame to get the whole screen size + NSRect nsrect = [[screenArray objectAtIndex:p_screen] frame]; + return Size2(nsrect.size.width, nsrect.size.height) * displayScale; + } + + return Size2(); +} + +void OS_OSX::_update_window() { + bool borderless_full = false; + + if (get_borderless_window()) { + NSRect frameRect = [window_object frame]; + NSRect screenRect = [[window_object screen] frame]; + + // Check if our window covers up the screen + if (frameRect.origin.x <= screenRect.origin.x && frameRect.origin.y <= frameRect.origin.y && + frameRect.size.width >= screenRect.size.width && frameRect.size.height >= screenRect.size.height) { + borderless_full = true; + } + } + + if (borderless_full) { + // If the window covers up the screen set the level to above the main menu and hide on deactivate + [window_object setLevel:NSMainMenuWindowLevel + 1]; + [window_object setHidesOnDeactivate:YES]; + } else { + // Reset these when our window is not a borderless window that covers up the screen + [window_object setLevel:NSNormalWindowLevel]; + [window_object setHidesOnDeactivate:NO]; + } +} Point2 OS_OSX::get_window_position() const { @@ -1311,6 +1357,8 @@ void OS_OSX::set_window_position(const Point2 &p_position) { Point2 size = p_position; size /= display_scale; [window_object setFrame:NSMakeRect(size.x, size.y, [window_object frame].size.width, [window_object frame].size.height) display:YES]; + + _update_window(); }; Size2 OS_OSX::get_window_size() const { @@ -1322,17 +1370,22 @@ void OS_OSX::set_window_size(const Size2 p_size) { Size2 size = p_size; - // NSRect used by setFrame includes the title bar, so add it to our size.y - CGFloat menuBarHeight = [[[NSApplication sharedApplication] mainMenu] menuBarHeight]; - if (menuBarHeight != 0.f) { - size.y += menuBarHeight; + if (get_borderless_window() == false) { + // NSRect used by setFrame includes the title bar, so add it to our size.y + CGFloat menuBarHeight = [[[NSApplication sharedApplication] mainMenu] menuBarHeight]; + if (menuBarHeight != 0.f) { + size.y += menuBarHeight; #if MAC_OS_X_VERSION_MAX_ALLOWED <= 101104 - } else { - size.y += [[NSStatusBar systemStatusBar] thickness]; + } else { + size.y += [[NSStatusBar systemStatusBar] thickness]; #endif + } } + NSRect frame = [window_object frame]; [window_object setFrame:NSMakeRect(frame.origin.x, frame.origin.y, size.x, size.y) display:YES]; + + _update_window(); }; void OS_OSX::set_window_fullscreen(bool p_enabled) { @@ -1390,7 +1443,7 @@ void OS_OSX::set_window_maximized(bool p_enabled) { if (p_enabled) { restore_rect = Rect2(get_window_position(), get_window_size()); - [window_object setFrame:[[[NSScreen screens] objectAtIndex:current_screen] visibleFrame] display:YES]; + [window_object setFrame:[[[NSScreen screens] objectAtIndex:get_current_screen()] visibleFrame] display:YES]; } else { set_window_size(restore_rect.size); set_window_position(restore_rect.position); @@ -1416,9 +1469,12 @@ void OS_OSX::request_attention() { void OS_OSX::set_borderless_window(int p_borderless) { - if (p_borderless) + // OrderOut prevents a lose focus bug with the window + [window_object orderOut:nil]; + + if (p_borderless) { [window_object setStyleMask:NSWindowStyleMaskBorderless]; - else { + } else { [window_object setStyleMask:NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask]; // Force update of the window styles @@ -1429,6 +1485,10 @@ void OS_OSX::set_borderless_window(int p_borderless) { // Restore the window title [window_object setTitle:[NSString stringWithUTF8String:title.utf8().get_data()]]; } + + _update_window(); + + [window_object makeKeyAndOrderFront:nil]; } bool OS_OSX::get_borderless_window() { @@ -1661,12 +1721,52 @@ OS_OSX::OS_OSX() { // In case we are unbundled, make us a proper UI application [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; -#if 0 // Menu bar setup must go between sharedApplication above and // finishLaunching below, in order to properly emulate the behavior // of NSApplicationMain - createMenuBar(); -#endif + NSMenuItem *menu_item; + NSString *title; + + NSString *nsappname = [[[NSBundle mainBundle] performSelector:@selector(localizedInfoDictionary)] objectForKey:@"CFBundleName"]; + if (nsappname == nil) + nsappname = [[NSProcessInfo processInfo] processName]; + + // Setup Apple menu + NSMenu *apple_menu = [[NSMenu alloc] initWithTitle:@""]; + title = [NSString stringWithFormat:NSLocalizedString(@"About %@", nil), nsappname]; + [apple_menu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; + + [apple_menu addItem:[NSMenuItem separatorItem]]; + + NSMenu *services = [[NSMenu alloc] initWithTitle:@""]; + menu_item = [apple_menu addItemWithTitle:NSLocalizedString(@"Services", nil) action:nil keyEquivalent:@""]; + [apple_menu setSubmenu:services forItem:menu_item]; + [NSApp setServicesMenu:services]; + [services release]; + + [apple_menu addItem:[NSMenuItem separatorItem]]; + + title = [NSString stringWithFormat:NSLocalizedString(@"Hide %@", nil), nsappname]; + [apple_menu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"]; + + menu_item = [apple_menu addItemWithTitle:NSLocalizedString(@"Hide Others", nil) action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; + [menu_item setKeyEquivalentModifierMask:(NSAlternateKeyMask | NSCommandKeyMask)]; + + [apple_menu addItemWithTitle:NSLocalizedString(@"Show all", nil) action:@selector(unhideAllApplications:) keyEquivalent:@""]; + + [apple_menu addItem:[NSMenuItem separatorItem]]; + + title = [NSString stringWithFormat:NSLocalizedString(@"Quit %@", nil), nsappname]; + [apple_menu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"]; + + // Setup menu bar + NSMenu *main_menu = [[NSMenu alloc] initWithTitle:@""]; + menu_item = [main_menu addItemWithTitle:@"" action:nil keyEquivalent:@""]; + [main_menu setSubmenu:apple_menu forItem:menu_item]; + [NSApp setMainMenu:main_menu]; + + [main_menu release]; + [apple_menu release]; [NSApp finishLaunching]; @@ -1676,11 +1776,13 @@ OS_OSX::OS_OSX() { cursor_shape = CURSOR_ARROW; - current_screen = 0; - maximized = false; minimized = false; window_size = Vector2(1024, 600); zoomed = false; display_scale = 1.0; } + +bool OS_OSX::_check_internal_feature_support(const String &p_feature) { + return p_feature == "pc" || p_feature == "s3tc"; +} diff --git a/platform/server/detect.py b/platform/server/detect.py index 32f3c5513..2bb4b59e9 100644 --- a/platform/server/detect.py +++ b/platform/server/detect.py @@ -1,4 +1,3 @@ - import os import sys @@ -13,17 +12,16 @@ def get_name(): def can_build(): - if (os.name != "posix"): + if (os.name != "posix" or sys.platform == "darwin"): return False - return True # enabled + return True def get_opts(): return [ - ('use_llvm', 'Use llvm compiler', 'no'), - ('force_32_bits', 'Force 32 bits binary', 'no') + ('use_llvm', 'Use the LLVM compiler', 'no'), ] @@ -35,46 +33,59 @@ def get_flags(): def configure(env): - env.Append(CPPPATH=['#platform/server']) - if (env["use_llvm"] == "yes"): - env["CC"] = "clang" - env["CXX"] = "clang++" - env["LD"] = "clang++" - - is64 = sys.maxsize > 2**32 - - if (env["bits"] == "default"): - if (is64): - env["bits"] = "64" - else: - env["bits"] = "32" - - # if (env["tools"]=="no"): - # #no tools suffix - # env['OBJSUFFIX'] = ".nt"+env['OBJSUFFIX'] - # env['LIBSUFFIX'] = ".nt"+env['LIBSUFFIX'] + ## Build type if (env["target"] == "release"): - env.Append(CCFLAGS=['-O2', '-ffast-math', '-fomit-frame-pointer']) elif (env["target"] == "release_debug"): - env.Append(CCFLAGS=['-O2', '-ffast-math', '-DDEBUG_ENABLED']) elif (env["target"] == "debug"): - env.Append(CCFLAGS=['-g2', '-DDEBUG_ENABLED', '-DDEBUG_MEMORY_ENABLED']) + ## Architecture + + is64 = sys.maxsize > 2**32 + if (env["bits"] == "default"): + env["bits"] = "64" if is64 else "32" + + ## Compiler configuration + + if (env["use_llvm"] == "yes"): + if ('clang++' not in env['CXX']): + env["CC"] = "clang" + env["CXX"] = "clang++" + env["LD"] = "clang++" + env.Append(CPPFLAGS=['-DTYPED_METHOD_BIND']) + env.extra_suffix = ".llvm" + env.extra_suffix + + ## Dependencies - # Shared libraries, when requested + # 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) + env.ParseConfig('pkg-config openssl --cflags --libs') if (env['builtin_libwebp'] == 'no'): 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'] == 'no'): env.ParseConfig('pkg-config freetype2 --cflags --libs') @@ -109,11 +120,12 @@ def configure(env): if (env['builtin_libogg'] == 'no'): env.ParseConfig('pkg-config ogg --cflags --libs') + ## Flags - env.Append(CPPFLAGS=['-DSERVER_ENABLED', '-DUNIX_ENABLED']) - env.Append(LIBS=['pthread', 'z']) # TODO detect linux/BSD! + # Linkflags below this line should typically stay the last ones + if (env['builtin_zlib'] == 'no'): + env.ParseConfig('pkg-config zlib --cflags --libs') - if (env["CXX"] == "clang++"): - env.Append(CPPFLAGS=['-DTYPED_METHOD_BIND']) - env["CC"] = "clang" - env["LD"] = "clang++" + env.Append(CPPPATH=['#platform/server']) + env.Append(CPPFLAGS=['-DSERVER_ENABLED', '-DUNIX_ENABLED']) + env.Append(LIBS=['pthread']) diff --git a/platform/uwp/detect.py b/platform/uwp/detect.py index baff7f978..64dac93f1 100644 --- a/platform/uwp/detect.py +++ b/platform/uwp/detect.py @@ -1,8 +1,7 @@ +import methods import os - -import sys import string -import methods +import sys def is_active(): @@ -26,7 +25,9 @@ def can_build(): def get_opts(): - return [] + + return [ + ] def get_flags(): @@ -39,16 +40,36 @@ def get_flags(): def configure(env): - if(env["bits"] != "default"): - print "Error: bits argument is disabled for MSVC" - print ("Bits argument is not supported for MSVC compilation. Architecture depends on the Native/Cross Compile Tools Prompt/Developer Console (or Visual Studio settings)" - + " that is being used to run SCons. As a consequence, bits argument is disabled. Run scons again without bits argument (example: scons p=uwp) and SCons will attempt to detect what MSVC compiler" - + " will be executed and inform you.") + if (env["bits"] != "default"): + print("Error: bits argument is disabled for MSVC") + print(""" + Bits argument is not supported for MSVC compilation. Architecture depends on the Native/Cross Compile Tools Prompt/Developer Console + (or Visual Studio settings) that is being used to run SCons. As a consequence, bits argument is disabled. Run scons again without bits + argument (example: scons p=uwp) and SCons will attempt to detect what MSVC compiler will be executed and inform you. + """) sys.exit() - arch = "" - env['ENV'] = os.environ + ## Build type + if (env["target"] == "release"): + env.Append(CPPFLAGS=['/O2', '/GL']) + env.Append(CPPFLAGS=['/MD']) + env.Append(LINKFLAGS=['/SUBSYSTEM:WINDOWS', '/LTCG']) + + elif (env["target"] == "release_debug"): + env.Append(CCFLAGS=['/O2', '/Zi', '/DDEBUG_ENABLED']) + env.Append(CPPFLAGS=['/MD']) + env.Append(LINKFLAGS=['/SUBSYSTEM:CONSOLE']) + + elif (env["target"] == "debug"): + env.Append(CCFLAGS=['/Zi', '/DDEBUG_ENABLED', '/DDEBUG_MEMORY_ENABLED']) + env.Append(CPPFLAGS=['/MDd']) + env.Append(LINKFLAGS=['/SUBSYSTEM:CONSOLE']) + env.Append(LINKFLAGS=['/DEBUG']) + + ## Compiler configuration + + env['ENV'] = os.environ vc_base_path = os.environ['VCTOOLSINSTALLDIR'] if "VCTOOLSINSTALLDIR" in os.environ else os.environ['VCINSTALLDIR'] # ANGLE @@ -60,9 +81,12 @@ def configure(env): if os.path.isfile(str(os.getenv("ANGLE_SRC_PATH")) + "/winrt/10/src/angle.sln"): env["build_angle"] = True + ## Architecture + + arch = "" if os.getenv('Platform') == "ARM": - print "Compiled program architecture will be an ARM executable. (forcing bits=32)." + print("Compiled program architecture will be an ARM executable. (forcing bits=32).") arch = "arm" env["bits"] = "32" @@ -74,17 +98,16 @@ def configure(env): env.Append(LIBPATH=[angle_root + '/winrt/10/src/Release_ARM/lib']) else: - compiler_version_str = methods.detect_visual_c_compiler_version(env['ENV']) if(compiler_version_str == "amd64" or compiler_version_str == "x86_amd64"): env["bits"] = "64" - print "Compiled program architecture will be a x64 executable (forcing bits=64)." + print("Compiled program architecture will be a x64 executable (forcing bits=64).") elif (compiler_version_str == "x86" or compiler_version_str == "amd64_x86"): env["bits"] = "32" - print "Compiled program architecture will be a x86 executable. (forcing bits=32)." + print("Compiled program architecture will be a x86 executable. (forcing bits=32).") else: - print "Failed to detect MSVC compiler architecture version... Defaulting to 32bit executable settings (forcing bits=32). Compilation attempt will continue, but SCons can not detect for what architecture this build is compiled for. You should check your settings/compilation setup." + print("Failed to detect MSVC compiler architecture version... Defaulting to 32bit executable settings (forcing bits=32). Compilation attempt will continue, but SCons can not detect for what architecture this build is compiled for. You should check your settings/compilation setup.") env["bits"] = "32" if (env["bits"] == "32"): @@ -92,7 +115,6 @@ def configure(env): angle_build_cmd += "Win32" - env.Append(CPPFLAGS=['/DPNG_ABORT=abort']) env.Append(LINKFLAGS=['/MACHINE:X86']) env.Append(LIBPATH=[vc_base_path + 'lib/store']) env.Append(LIBPATH=[angle_root + '/winrt/10/src/Release_Win32/lib']) @@ -106,48 +128,30 @@ def configure(env): env.Append(LIBPATH=[os.environ['VCINSTALLDIR'] + 'lib/store/amd64']) env.Append(LIBPATH=[angle_root + '/winrt/10/src/Release_x64/lib']) - env.Append(CPPPATH=['#platform/uwp', '#drivers/windows']) - env.Append(LINKFLAGS=['/MANIFEST:NO', '/NXCOMPAT', '/DYNAMICBASE', '/WINMD', '/APPCONTAINER', '/ERRORREPORT:PROMPT', '/NOLOGO', '/TLBID:1', '/NODEFAULTLIB:"kernel32.lib"', '/NODEFAULTLIB:"ole32.lib"']) - env.Append(CPPFLAGS=['/D', '__WRL_NO_DEFAULT_LIB__', '/D', 'WIN32']) - - env.Append(CPPFLAGS=['/AI', vc_base_path + 'lib/store/references']) - env.Append(CPPFLAGS=['/AI', vc_base_path + 'lib/x86/store/references']) + env["PROGSUFFIX"] = "." + arch + env["PROGSUFFIX"] + env["OBJSUFFIX"] = "." + arch + env["OBJSUFFIX"] + env["LIBSUFFIX"] = "." + arch + env["LIBSUFFIX"] - if (env["target"] == "release"): + ## Compile flags - env.Append(CPPFLAGS=['/O2', '/GL']) - env.Append(CPPFLAGS=['/MD']) - env.Append(LINKFLAGS=['/SUBSYSTEM:WINDOWS', '/LTCG']) - - elif (env["target"] == "release_debug"): - - env.Append(CCFLAGS=['/O2', '/Zi', '/DDEBUG_ENABLED']) - env.Append(CPPFLAGS=['/MD']) - env.Append(LINKFLAGS=['/SUBSYSTEM:CONSOLE']) + env.Append(CPPPATH=['#platform/uwp', '#drivers/windows']) + env.Append(CCFLAGS=['/DUWP_ENABLED', '/DWINDOWS_ENABLED', '/DTYPED_METHOD_BIND']) + env.Append(CCFLAGS=['/DGLES2_ENABLED', '/DGL_GLEXT_PROTOTYPES', '/DEGL_EGLEXT_PROTOTYPES', '/DANGLE_ENABLED']) + winver = "0x0602" # Windows 8 is the minimum target for UWP build + env.Append(CCFLAGS=['/DWINVER=%s' % winver, '/D_WIN32_WINNT=%s' % winver]) - elif (env["target"] == "debug"): + env.Append(CPPFLAGS=['/D', '__WRL_NO_DEFAULT_LIB__', '/D', 'WIN32', '/DPNG_ABORT=abort']) - env.Append(CCFLAGS=['/Zi', '/DDEBUG_ENABLED', '/DDEBUG_MEMORY_ENABLED']) - env.Append(CPPFLAGS=['/MDd']) - env.Append(LINKFLAGS=['/SUBSYSTEM:CONSOLE']) - env.Append(LINKFLAGS=['/DEBUG']) + env.Append(CPPFLAGS=['/AI', vc_base_path + 'lib/store/references']) + env.Append(CPPFLAGS=['/AI', vc_base_path + 'lib/x86/store/references']) env.Append(CCFLAGS=string.split('/FS /MP /GS /wd"4453" /wd"28204" /wd"4291" /Zc:wchar_t /Gm- /fp:precise /D "_UNICODE" /D "UNICODE" /D "WINAPI_FAMILY=WINAPI_FAMILY_APP" /errorReport:prompt /WX- /Zc:forScope /Gd /EHsc /nologo')) env.Append(CXXFLAGS=string.split('/ZW /FS')) env.Append(CCFLAGS=['/AI', vc_base_path + '\\vcpackages', '/AI', os.environ['WINDOWSSDKDIR'] + '\\References\\CommonConfiguration\\Neutral']) - env["PROGSUFFIX"] = "." + arch + env["PROGSUFFIX"] - env["OBJSUFFIX"] = "." + arch + env["OBJSUFFIX"] - env["LIBSUFFIX"] = "." + arch + env["LIBSUFFIX"] - - env.Append(CCFLAGS=['/DUWP_ENABLED']) - env.Append(CCFLAGS=['/DWINDOWS_ENABLED']) - env.Append(CCFLAGS=['/DTYPED_METHOD_BIND']) - - env.Append(CCFLAGS=['/DGLES2_ENABLED', '/DGL_GLEXT_PROTOTYPES', '/DEGL_EGLEXT_PROTOTYPES', '/DANGLE_ENABLED']) + ## Link flags - winver = "0x0602" # Windows 8 is the minimum target for UWP build - env.Append(CCFLAGS=['/DWINVER=%s' % winver, '/D_WIN32_WINNT=%s' % winver]) + env.Append(LINKFLAGS=['/MANIFEST:NO', '/NXCOMPAT', '/DYNAMICBASE', '/WINMD', '/APPCONTAINER', '/ERRORREPORT:PROMPT', '/NOLOGO', '/TLBID:1', '/NODEFAULTLIB:"kernel32.lib"', '/NODEFAULTLIB:"ole32.lib"']) LIBS = [ 'WindowsApp', @@ -164,8 +168,3 @@ def configure(env): env['BUILDERS']['Program'] = methods.precious_program env.Append(BUILDERS={'ANGLE': env.Builder(action=angle_build_cmd)}) - - env.Append(BUILDERS={'GLSL120': env.Builder(action=methods.build_legacygl_headers, suffix='glsl.h', src_suffix='.glsl')}) - env.Append(BUILDERS={'GLSL': env.Builder(action=methods.build_glsl_headers, suffix='glsl.h', src_suffix='.glsl')}) - env.Append(BUILDERS={'HLSL9': env.Builder(action=methods.build_hlsl_dx9_headers, suffix='hlsl.h', src_suffix='.hlsl')}) - env.Append(BUILDERS={'GLSL120GLES': env.Builder(action=methods.build_gles2_headers, suffix='glsl.h', src_suffix='.glsl')}) diff --git a/platform/uwp/export/export.cpp b/platform/uwp/export/export.cpp index 976e6208e..a1aa58a5e 100644 --- a/platform/uwp/export/export.cpp +++ b/platform/uwp/export/export.cpp @@ -28,55 +28,16 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -/************************************************************************* - * The code for signing the package was ported from fb-util-for-appx - * available at https://github.com/facebook/fb-util-for-appx - * and distributed also under the following license: - -BSD License - -For fb-util-for-appx software - -Copyright (c) 2016, Facebook, Inc. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - * Neither the name Facebook nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*************************************************************************/ - -#if 0 #include "export.h" #include "bind/core_bind.h" -#include "editor/editor_import_export.h" +#include "editor/editor_export.h" #include "editor/editor_node.h" -#include "global_config.h" #include "io/marshalls.h" #include "io/zip_io.h" #include "object.h" #include "os/file_access.h" -#include "platform/uwp/logo.h" +#include "platform/uwp/logo.gen.h" +#include "project_settings.h" #include "version.h" #include "thirdparty/minizip/unzip.h" @@ -87,7 +48,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <zlib.h> // Capabilities -static const char* uwp_capabilities[] = { +static const char *uwp_capabilities[] = { "allJoyn", "codeGeneration", "internetClient", @@ -95,7 +56,7 @@ static const char* uwp_capabilities[] = { "privateNetworkClientServer", NULL }; -static const char* uwp_uap_capabilities[] = { +static const char *uwp_uap_capabilities[] = { "appointments", "blockedChatMessages", "chat", @@ -112,7 +73,7 @@ static const char* uwp_uap_capabilities[] = { "voipCall", NULL }; -static const char* uwp_device_capabilites[] = { +static const char *uwp_device_capabilites[] = { "bluetooth", "location", "microphone", @@ -121,166 +82,6 @@ static const char* uwp_device_capabilites[] = { NULL }; -#ifdef OPENSSL_ENABLED -#include <openssl/asn1.h> -#include <openssl/asn1t.h> -#include <openssl/bio.h> -#include <openssl/err.h> -#include <openssl/ossl_typ.h> -#include <openssl/pkcs12.h> -#include <openssl/pkcs7.h> -#include <openssl/x509.h> - -namespace asn1 { - // https://msdn.microsoft.com/en-us/gg463180.aspx - - struct SPCStatementType { - ASN1_OBJECT *type; - }; - DECLARE_ASN1_FUNCTIONS(SPCStatementType) - - struct SPCSpOpusInfo { - ASN1_TYPE *programName; - ASN1_TYPE *moreInfo; - }; - DECLARE_ASN1_FUNCTIONS(SPCSpOpusInfo) - - struct DigestInfo { - X509_ALGOR *digestAlgorithm; - ASN1_OCTET_STRING *digest; - }; - DECLARE_ASN1_FUNCTIONS(DigestInfo) - - struct SPCAttributeTypeAndOptionalValue { - ASN1_OBJECT *type; - ASN1_TYPE *value; // SPCInfoValue - }; - DECLARE_ASN1_FUNCTIONS(SPCAttributeTypeAndOptionalValue) - - // Undocumented. - struct SPCInfoValue { - ASN1_INTEGER *i1; - ASN1_OCTET_STRING *s1; - ASN1_INTEGER *i2; - ASN1_INTEGER *i3; - ASN1_INTEGER *i4; - ASN1_INTEGER *i5; - ASN1_INTEGER *i6; - }; - DECLARE_ASN1_FUNCTIONS(SPCInfoValue) - - struct SPCIndirectDataContent { - SPCAttributeTypeAndOptionalValue *data; - DigestInfo *messageDigest; - }; - DECLARE_ASN1_FUNCTIONS(SPCIndirectDataContent) - - IMPLEMENT_ASN1_FUNCTIONS(SPCIndirectDataContent) - ASN1_SEQUENCE(SPCIndirectDataContent) = { - ASN1_SIMPLE(SPCIndirectDataContent, data, - SPCAttributeTypeAndOptionalValue), - ASN1_SIMPLE(SPCIndirectDataContent, messageDigest, DigestInfo), - } ASN1_SEQUENCE_END(SPCIndirectDataContent) - - IMPLEMENT_ASN1_FUNCTIONS(SPCAttributeTypeAndOptionalValue) - ASN1_SEQUENCE(SPCAttributeTypeAndOptionalValue) = { - ASN1_SIMPLE(SPCAttributeTypeAndOptionalValue, type, - ASN1_OBJECT), - ASN1_OPT(SPCAttributeTypeAndOptionalValue, value, ASN1_ANY), - } ASN1_SEQUENCE_END(SPCAttributeTypeAndOptionalValue) - - IMPLEMENT_ASN1_FUNCTIONS(SPCInfoValue) - ASN1_SEQUENCE(SPCInfoValue) = { - ASN1_SIMPLE(SPCInfoValue, i1, ASN1_INTEGER), - ASN1_SIMPLE(SPCInfoValue, s1, ASN1_OCTET_STRING), - ASN1_SIMPLE(SPCInfoValue, i2, ASN1_INTEGER), - ASN1_SIMPLE(SPCInfoValue, i3, ASN1_INTEGER), - ASN1_SIMPLE(SPCInfoValue, i4, ASN1_INTEGER), - ASN1_SIMPLE(SPCInfoValue, i5, ASN1_INTEGER), - ASN1_SIMPLE(SPCInfoValue, i6, ASN1_INTEGER), - } ASN1_SEQUENCE_END(SPCInfoValue) - - IMPLEMENT_ASN1_FUNCTIONS(DigestInfo) - ASN1_SEQUENCE(DigestInfo) = { - ASN1_SIMPLE(DigestInfo, digestAlgorithm, X509_ALGOR), - ASN1_SIMPLE(DigestInfo, digest, ASN1_OCTET_STRING), - } ASN1_SEQUENCE_END(DigestInfo) - - ASN1_SEQUENCE(SPCSpOpusInfo) = { - ASN1_OPT(SPCSpOpusInfo, programName, ASN1_ANY), - ASN1_OPT(SPCSpOpusInfo, moreInfo, ASN1_ANY), - } ASN1_SEQUENCE_END(SPCSpOpusInfo) - IMPLEMENT_ASN1_FUNCTIONS(SPCSpOpusInfo) - - ASN1_SEQUENCE(SPCStatementType) = { - ASN1_SIMPLE(SPCStatementType, type, ASN1_OBJECT), - } ASN1_SEQUENCE_END(SPCStatementType) - IMPLEMENT_ASN1_FUNCTIONS(SPCStatementType) -} - -class EncodedASN1 { - - uint8_t* i_data; - size_t i_size; - - EncodedASN1(uint8_t** p_data, size_t p_size) { - - i_data = *p_data; - i_size = p_size; - } - -public: - - template <typename T, int(*TEncode)(T *, uint8_t **)> - static EncodedASN1 FromItem(T *item) { - uint8_t *dataRaw = NULL; - int size = TEncode(item, &dataRaw); - - return EncodedASN1(&dataRaw, size); - } - - const uint8_t *data() const { - return i_data; - } - - size_t size() const { - return i_size; - } - - // Assumes the encoded ASN.1 represents a SEQUENCE and puts it into - // an ASN1_STRING. - // - // The returned object holds a copy of this object's data. - ASN1_STRING* ToSequenceString() { - ASN1_STRING* string = ASN1_STRING_new(); - if (!string) { - return NULL; - } - if (!ASN1_STRING_set(string, i_data, i_size)) { - return NULL; - } - return string; - } - - // Assumes the encoded ASN.1 represents a SEQUENCE and puts it into - // an ASN1_TYPE. - // - // The returned object holds a copy of this object's data. - ASN1_TYPE* ToSequenceType() { - ASN1_STRING* string = ToSequenceString(); - ASN1_TYPE* type = ASN1_TYPE_new(); - if (!type) { - return NULL; - } - type->type = V_ASN1_SEQUENCE; - type->value.sequence = string; - return type; - } - -}; - -#endif // OPENSSL_ENABLED - class AppxPackager { enum { @@ -336,34 +137,33 @@ class AppxPackager { ZPOS64_T end_of_central_dir_offset; Vector<uint8_t> central_dir_data; - String hash_block(uint8_t* p_block_data, size_t p_block_len); + String hash_block(uint8_t *p_block_data, size_t p_block_len); void make_block_map(); void make_content_types(); - - _FORCE_INLINE_ unsigned int buf_put_int16(uint16_t p_val, uint8_t * p_buf) { + _FORCE_INLINE_ unsigned int buf_put_int16(uint16_t p_val, uint8_t *p_buf) { for (int i = 0; i < 2; i++) { *p_buf++ = (p_val >> (i * 8)) & 0xFF; } return 2; } - _FORCE_INLINE_ unsigned int buf_put_int32(uint32_t p_val, uint8_t * p_buf) { + _FORCE_INLINE_ unsigned int buf_put_int32(uint32_t p_val, uint8_t *p_buf) { for (int i = 0; i < 4; i++) { *p_buf++ = (p_val >> (i * 8)) & 0xFF; } return 4; } - _FORCE_INLINE_ unsigned int buf_put_int64(uint64_t p_val, uint8_t * p_buf) { + _FORCE_INLINE_ unsigned int buf_put_int64(uint64_t p_val, uint8_t *p_buf) { for (int i = 0; i < 8; i++) { *p_buf++ = (p_val >> (i * 8)) & 0xFF; } return 8; } - _FORCE_INLINE_ unsigned int buf_put_string(String p_val, uint8_t * p_buf) { + _FORCE_INLINE_ unsigned int buf_put_string(String p_val, uint8_t *p_buf) { for (int i = 0; i < p_val.length(); i++) { *p_buf++ = p_val.utf8().get(i); } @@ -376,168 +176,19 @@ class AppxPackager { String content_type(String p_extension); -#ifdef OPENSSL_ENABLED - - // Signing methods and structs: - - String certificate_path; - String certificate_pass; - bool sign_package; - - struct CertFile { - - EVP_PKEY* private_key; - X509* certificate; - }; - - SHA256_CTX axpc_context; // SHA256 context for ZIP file entries - SHA256_CTX axcd_context; // SHA256 context for ZIP directory entries - - struct AppxDigests { - - uint8_t axpc[SHA256_DIGEST_LENGTH]; // ZIP file entries - uint8_t axcd[SHA256_DIGEST_LENGTH]; // ZIP directory entry - uint8_t axct[SHA256_DIGEST_LENGTH]; // Content types XML - uint8_t axbm[SHA256_DIGEST_LENGTH]; // Block map XML - uint8_t axci[SHA256_DIGEST_LENGTH]; // Code Integrity file (optional) - }; - - CertFile cert_file; - AppxDigests digests; - - void MakeSPCInfoValue(asn1::SPCInfoValue &info); - Error MakeIndirectDataContent(asn1::SPCIndirectDataContent &idc); - Error add_attributes(PKCS7_SIGNER_INFO *signerInfo); - void make_digests(); - void write_digest(Vector<uint8_t> &p_out_buffer); - - Error openssl_error(unsigned long p_err); - Error read_cert_file(const String &p_path, const String &p_password, CertFile* p_out_cf); - Error sign(const CertFile &p_cert, const AppxDigests &digests, PKCS7* p_out_signature); - -#endif // OPENSSL_ENABLED - public: - - enum SignOption { - - SIGN, - DONT_SIGN, - }; - void set_progress_task(String p_task) { progress_task = p_task; } - void init(FileAccess* p_fa, SignOption p_sign, String &p_certificate_path, String &p_certificate_password); - void add_file(String p_file_name, const uint8_t* p_buffer, size_t p_len, int p_file_no, int p_total_files, bool p_compress = false); + void init(FileAccess *p_fa); + void add_file(String p_file_name, const uint8_t *p_buffer, size_t p_len, int p_file_no, int p_total_files, bool p_compress = false); void finish(); AppxPackager(); ~AppxPackager(); }; -class EditorExportPlatformUWP : public EditorExportPlatform { - - GDCLASS(EditorExportPlatformUWP, EditorExportPlatform); - - Ref<ImageTexture> logo; - - enum Platform { - ARM, - X86, - X64 - } arch; - - bool is_debug; - - String custom_release_package; - String custom_debug_package; - - String cmdline; - - String display_name; - String short_name; - String unique_name; - String description; - String publisher; - String publisher_display_name; - - String product_guid; - String publisher_guid; - - int version_major; - int version_minor; - int version_build; - int version_revision; - - bool orientation_landscape; - bool orientation_portrait; - bool orientation_landscape_flipped; - bool orientation_portrait_flipped; - - String background_color; - Ref<ImageTexture> store_logo; - Ref<ImageTexture> square44; - Ref<ImageTexture> square71; - Ref<ImageTexture> square150; - Ref<ImageTexture> square310; - Ref<ImageTexture> wide310; - Ref<ImageTexture> splash; - - bool name_on_square150; - bool name_on_square310; - bool name_on_wide; - - Set<String> capabilities; - Set<String> uap_capabilities; - Set<String> device_capabilities; - - bool sign_package; - String certificate_path; - String certificate_pass; - - _FORCE_INLINE_ bool array_has(const char** p_array, const char* p_value) const { - while (*p_array) { - if (String(*p_array) == String(p_value)) return true; - p_array++; - } - return false; - } - - bool _valid_resource_name(const String &p_name) const; - bool _valid_guid(const String &p_guid) const; - bool _valid_bgcolor(const String &p_color) const; - bool _valid_image(const Ref<ImageTexture> p_image, int p_width, int p_height) const; - - Vector<uint8_t> _fix_manifest(const Vector<uint8_t> &p_template, bool p_give_internet) const; - Vector<uint8_t> _get_image_data(const String &p_path); - - static Error save_appx_file(void *p_userdata, const String& p_path, const Vector<uint8_t>& p_data, int p_file, int p_total); - static bool _should_compress_asset(const String& p_path, const Vector<uint8_t>& p_data); - -protected: - - bool _set(const StringName& p_name, const Variant& p_value); - bool _get(const StringName& p_name, Variant &r_ret) const; - void _get_property_list(List<PropertyInfo> *p_list) const; - -public: - - virtual String get_name() const { return "Windows Universal"; } - virtual ImageCompression get_image_compression() const { return IMAGE_COMPRESSION_ETC1; } - virtual Ref<Texture> get_logo() const { return logo; } - - virtual bool can_export(String *r_error = NULL) const; - virtual String get_binary_extension() const { return "appx"; } - - virtual Error export_project(const String& p_path, bool p_debug, int p_flags = 0); - - EditorExportPlatformUWP(); - ~EditorExportPlatformUWP(); -}; - - /////////////////////////////////////////////////////////////////////////// -String AppxPackager::hash_block(uint8_t * p_block_data, size_t p_block_len) { +String AppxPackager::hash_block(uint8_t *p_block_data, size_t p_block_len) { char hash[32]; char base64[45]; @@ -545,7 +196,7 @@ String AppxPackager::hash_block(uint8_t * p_block_data, size_t p_block_len) { sha256_context ctx; sha256_init(&ctx); sha256_hash(&ctx, p_block_data, p_block_len); - sha256_done(&ctx, (uint8_t*)hash); + sha256_done(&ctx, (uint8_t *)hash); base64_encode(base64, hash, 32); base64[44] = '\0'; @@ -555,7 +206,7 @@ String AppxPackager::hash_block(uint8_t * p_block_data, size_t p_block_len) { void AppxPackager::make_block_map() { - FileAccess* tmp_file = FileAccess::open(tmp_blockmap_file_path, FileAccess::WRITE); + FileAccess *tmp_file = FileAccess::open(tmp_blockmap_file_path, FileAccess::WRITE); tmp_file->store_string("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"); tmp_file->store_string("<BlockMap xmlns=\"http://schemas.microsoft.com/appx/2010/blockmap\" HashMethod=\"http://www.w3.org/2001/04/xmlenc#sha256\">"); @@ -565,15 +216,11 @@ void AppxPackager::make_block_map() { FileMeta file = file_metadata[i]; tmp_file->store_string( - "<File Name=\"" + file.name.replace("/", "\\") - + "\" Size=\"" + itos(file.uncompressed_size) - + "\" LfhSize=\"" + itos(file.lfh_size) + "\">"); - + "<File Name=\"" + file.name.replace("/", "\\") + "\" Size=\"" + itos(file.uncompressed_size) + "\" LfhSize=\"" + itos(file.lfh_size) + "\">"); for (int j = 0; j < file.hashes.size(); j++) { - tmp_file->store_string("<Block Hash=\"" - + file.hashes[j].base64_hash + "\" "); + tmp_file->store_string("<Block Hash=\"" + file.hashes[j].base64_hash + "\" "); if (file.compressed) tmp_file->store_string("Size=\"" + itos(file.hashes[j].compressed_size) + "\" "); tmp_file->store_string("/>"); @@ -605,12 +252,12 @@ String AppxPackager::content_type(String p_extension) { void AppxPackager::make_content_types() { - FileAccess* tmp_file = FileAccess::open(tmp_content_types_file_path, FileAccess::WRITE); + FileAccess *tmp_file = FileAccess::open(tmp_content_types_file_path, FileAccess::WRITE); tmp_file->store_string("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); tmp_file->store_string("<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">"); - Map<String, String> types; + Map<String, String> types; for (int i = 0; i < file_metadata.size(); i++) { @@ -688,7 +335,6 @@ void AppxPackager::store_central_dir_header(const FileMeta p_file, bool p_do_has int offs = buf.size(); buf.resize(buf.size() + BASE_CENTRAL_DIR_SIZE + p_file.name.length()); - // Write magic offs += buf_put_int32(CENTRAL_DIR_MAGIC, &buf[offs]); @@ -732,12 +378,6 @@ void AppxPackager::store_central_dir_header(const FileMeta p_file, bool p_do_has // File name offs += buf_put_string(p_file.name, &buf[offs]); -#ifdef OPENSSL_ENABLED - // Calculate the hash for signing - if (p_do_hash) - SHA256_Update(&axcd_context, buf.ptr(), buf.size()); -#endif // OPENSSL_ENABLED - // Done! } @@ -811,23 +451,16 @@ Vector<uint8_t> AppxPackager::make_end_of_central_record() { return buf; } -void AppxPackager::init(FileAccess * p_fa, SignOption p_sign, String &p_certificate_path, String &p_certificate_password) { +void AppxPackager::init(FileAccess *p_fa) { package = p_fa; central_dir_offset = 0; end_of_central_dir_offset = 0; tmp_blockmap_file_path = EditorSettings::get_singleton()->get_settings_path() + "/tmp/tmpblockmap.xml"; tmp_content_types_file_path = EditorSettings::get_singleton()->get_settings_path() + "/tmp/tmpcontenttypes.xml"; -#ifdef OPENSSL_ENABLED - certificate_path = p_certificate_path; - certificate_pass = p_certificate_password; - sign_package = p_sign == SIGN; - SHA256_Init(&axpc_context); - SHA256_Init(&axcd_context); -#endif // OPENSSL_ENABLED } -void AppxPackager::add_file(String p_file_name, const uint8_t * p_buffer, size_t p_len, int p_file_no, int p_total_files, bool p_compress) { +void AppxPackager::add_file(String p_file_name, const uint8_t *p_buffer, size_t p_len, int p_file_no, int p_total_files, bool p_compress) { if (p_file_no >= 1 && p_total_files >= 1) { EditorNode::progress_task_step(progress_task, "File: " + p_file_name, (p_file_no * 100) / p_total_files); @@ -846,7 +479,7 @@ void AppxPackager::add_file(String p_file_name, const uint8_t * p_buffer, size_t // Data for compression z_stream strm; - FileAccess* strm_f = NULL; + FileAccess *strm_f = NULL; Vector<uint8_t> strm_in; strm_in.resize(BLOCK_SIZE); Vector<uint8_t> strm_out; @@ -892,11 +525,6 @@ void AppxPackager::add_file(String p_file_name, const uint8_t * p_buffer, size_t file_buffer.resize(file_buffer.size() + bh.compressed_size); for (int i = 0; i < bh.compressed_size; i++) file_buffer[start + i] = strm_out[i]; -#ifdef OPENSSL_ENABLED - if (do_hash) - SHA256_Update(&axpc_context, strm_out.ptr(), strm.total_out - total_out_before); -#endif // OPENSSL_ENABLED - } else { bh.compressed_size = block_size; //package->store_buffer(strm_in.ptr(), block_size); @@ -904,10 +532,6 @@ void AppxPackager::add_file(String p_file_name, const uint8_t * p_buffer, size_t file_buffer.resize(file_buffer.size() + block_size); for (int i = 0; i < bh.compressed_size; i++) file_buffer[start + i] = strm_in[i]; -#ifdef OPENSSL_ENABLED - if (do_hash) - SHA256_Update(&axpc_context, strm_in.ptr(), block_size); -#endif // OPENSSL_ENABLED } meta.hashes.push_back(bh); @@ -931,10 +555,6 @@ void AppxPackager::add_file(String p_file_name, const uint8_t * p_buffer, size_t file_buffer.resize(file_buffer.size() + (strm.total_out - total_out_before)); for (int i = 0; i < (strm.total_out - total_out_before); i++) file_buffer[start + i] = strm_out[i]; -#ifdef OPENSSL_ENABLED - if (do_hash) - SHA256_Update(&axpc_context, strm_out.ptr(), strm.total_out - total_out_before); -#endif // OPENSSL_ENABLED deflateEnd(&strm); meta.compressed_size = strm.total_out; @@ -953,14 +573,6 @@ void AppxPackager::add_file(String p_file_name, const uint8_t * p_buffer, size_t Vector<uint8_t> file_header = make_file_header(meta); meta.lfh_size = file_header.size(); -#ifdef OPENSSL_ENABLED - // Hash the data for signing - if (do_hash) { - SHA256_Update(&axpc_context, file_header.ptr(), file_header.size()); - SHA256_Update(&axpc_context, file_buffer.ptr(), file_buffer.size()); - } -#endif // OPENSSL_ENABLED - // Store the header and file; package->store_buffer(file_header.ptr(), file_header.size()); package->store_buffer(file_buffer.ptr(), file_buffer.size()); @@ -974,22 +586,12 @@ void AppxPackager::finish() { EditorNode::progress_task_step("export", "Creating block map...", 4); make_block_map(); - FileAccess* blockmap_file = FileAccess::open(tmp_blockmap_file_path, FileAccess::READ); + FileAccess *blockmap_file = FileAccess::open(tmp_blockmap_file_path, FileAccess::READ); Vector<uint8_t> blockmap_buffer; blockmap_buffer.resize(blockmap_file->get_len()); blockmap_file->get_buffer(blockmap_buffer.ptr(), blockmap_buffer.size()); -#ifdef OPENSSL_ENABLED - // Hash the file for signing - if (sign_package) { - SHA256_CTX axbm_context; - SHA256_Init(&axbm_context); - SHA256_Update(&axbm_context, blockmap_buffer.ptr(), blockmap_buffer.size()); - SHA256_Final(digests.axbm, &axbm_context); - } -#endif // OPENSSL_ENABLED - add_file("AppxBlockMap.xml", blockmap_buffer.ptr(), blockmap_buffer.size(), -1, -1, true); blockmap_file->close(); @@ -1000,22 +602,12 @@ void AppxPackager::finish() { EditorNode::progress_task_step("export", "Setting content types...", 5); make_content_types(); - FileAccess* types_file = FileAccess::open(tmp_content_types_file_path, FileAccess::READ); + FileAccess *types_file = FileAccess::open(tmp_content_types_file_path, FileAccess::READ); Vector<uint8_t> types_buffer; types_buffer.resize(types_file->get_len()); types_file->get_buffer(types_buffer.ptr(), types_buffer.size()); -#ifdef OPENSSL_ENABLED - if (sign_package) { - // Hash the file for signing - SHA256_CTX axct_context; - SHA256_Init(&axct_context); - SHA256_Update(&axct_context, types_buffer.ptr(), types_buffer.size()); - SHA256_Final(digests.axct, &axct_context); - } -#endif // OPENSSL_ENABLED - add_file("[Content_Types].xml", types_buffer.ptr(), types_buffer.size(), -1, -1, true); types_file->close(); @@ -1027,76 +619,6 @@ void AppxPackager::finish() { store_central_dir_header(file_metadata[i]); } -#ifdef OPENSSL_ENABLED - // Create the signature file - if (sign_package) { - - Error err = read_cert_file(certificate_path, certificate_pass, &cert_file); - - if (err != OK) { - EditorNode::add_io_error(TTR("Couldn't read the certificate file. Are the path and password both correct?")); - package->close(); - memdelete(package); - package = NULL; - return; - } - - - // Make a temp end of the zip for hashing - central_dir_offset = package->get_pos(); - end_of_central_dir_offset = central_dir_offset + central_dir_data.size(); - Vector<uint8_t> zip_end_dir = make_end_of_central_record(); - - // Hash the end directory - SHA256_Update(&axcd_context, zip_end_dir.ptr(), zip_end_dir.size()); - - // Finish the hashes - make_digests(); - - PKCS7* signature = PKCS7_new(); - if (!signature) { - EditorNode::add_io_error(TTR("Error creating the signature object.")); - package->close(); - memdelete(package); - package = NULL; - return; - } - - err = sign(cert_file, digests, signature); - - if (err != OK) { - EditorNode::add_io_error(TTR("Error creating the package signature.")); - package->close(); - memdelete(package); - package = NULL; - return; - } - - // Read the signature as bytes - BIO* bio_out = BIO_new(BIO_s_mem()); - i2d_PKCS7_bio(bio_out, signature); - - BIO_flush(bio_out); - - uint8_t* bio_ptr; - size_t bio_size = BIO_get_mem_data(bio_out, &bio_ptr); - - // Create the signature buffer with magic number - Vector<uint8_t> signature_file; - signature_file.resize(4 + bio_size); - buf_put_int32(P7X_SIGNATURE, signature_file.ptr()); - for (int i = 0; i < bio_size; i++) - signature_file[i + 4] = bio_ptr[i]; - - // Add the signature to the package - add_file("AppxSignature.p7x", signature_file.ptr(), signature_file.size(), -1, -1, true); - - // Add central directory entry - store_central_dir_header(file_metadata[file_metadata.size() - 1], false); - } -#endif // OPENSSL_ENABLED - - // Write central directory EditorNode::progress_task_step("export", "Finishing package...", 6); central_dir_offset = package->get_pos(); @@ -1112,1284 +634,759 @@ void AppxPackager::finish() { package = NULL; } -#ifdef OPENSSL_ENABLED -// https://support.microsoft.com/en-us/kb/287547 -const char SPC_INDIRECT_DATA_OBJID[] = "1.3.6.1.4.1.311.2.1.4"; -const char SPC_STATEMENT_TYPE_OBJID[] = "1.3.6.1.4.1.311.2.1.11"; -const char SPC_SP_OPUS_INFO_OBJID[] = "1.3.6.1.4.1.311.2.1.12"; -const char SPC_SIPINFO_OBJID[] = "1.3.6.1.4.1.311.2.1.30"; -#endif // OPENSSL_ENABLED - AppxPackager::AppxPackager() {} AppxPackager::~AppxPackager() {} - //////////////////////////////////////////////////////////////////// -#ifdef OPENSSL_ENABLED -Error AppxPackager::openssl_error(unsigned long p_err) { +class EditorExportUWP : public EditorExportPlatform { - ERR_load_crypto_strings(); + GDCLASS(EditorExportUWP, EditorExportPlatform); - char buffer[256]; - ERR_error_string_n(p_err, buffer, sizeof(buffer)); - - String err(buffer); - - ERR_EXPLAIN(err); - ERR_FAIL_V(FAILED); -} - -void AppxPackager::MakeSPCInfoValue(asn1::SPCInfoValue &info) { + Ref<ImageTexture> logo; - // I have no idea what these numbers mean. - static uint8_t s1Magic[] = { - 0x4B, 0xDF, 0xC5, 0x0A, 0x07, 0xCE, 0xE2, 0x4D, - 0xB7, 0x6E, 0x23, 0xC8, 0x39, 0xA0, 0x9F, 0xD1, + enum Platform { + ARM, + X86, + X64 }; - ASN1_INTEGER_set(info.i1, 0x01010000); - ASN1_OCTET_STRING_set(info.s1, s1Magic, sizeof(s1Magic)); - ASN1_INTEGER_set(info.i2, 0x00000000); - ASN1_INTEGER_set(info.i3, 0x00000000); - ASN1_INTEGER_set(info.i4, 0x00000000); - ASN1_INTEGER_set(info.i5, 0x00000000); - ASN1_INTEGER_set(info.i6, 0x00000000); -} - -Error AppxPackager::MakeIndirectDataContent(asn1::SPCIndirectDataContent &idc) { - - using namespace asn1; - - ASN1_TYPE* algorithmParameter = ASN1_TYPE_new(); - if (!algorithmParameter) { - return openssl_error(ERR_peek_last_error()); - } - algorithmParameter->type = V_ASN1_NULL; - SPCInfoValue* infoValue = SPCInfoValue_new(); - if (!infoValue) { - return openssl_error(ERR_peek_last_error()); - } - MakeSPCInfoValue(*infoValue); + bool _valid_resource_name(const String &p_name) const { - ASN1_TYPE* value = - EncodedASN1::FromItem<asn1::SPCInfoValue, - asn1::i2d_SPCInfoValue>(infoValue) - .ToSequenceType(); + if (p_name.empty()) return false; + if (p_name.ends_with(".")) return false; - { - Vector<uint8_t> digest; - write_digest(digest); - if (!ASN1_OCTET_STRING_set(idc.messageDigest->digest, - digest.ptr(), digest.size())) { + static const char *invalid_names[] = { + "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", + "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", + NULL + }; - return openssl_error(ERR_peek_last_error()); + const char **t = invalid_names; + while (*t) { + if (p_name == *t) return false; + t++; } - } - - idc.data->type = OBJ_txt2obj(SPC_SIPINFO_OBJID, 1); - idc.data->value = value; - idc.messageDigest->digestAlgorithm->algorithm = OBJ_nid2obj(NID_sha256); - idc.messageDigest->digestAlgorithm->parameter = algorithmParameter; - - return OK; -} - -Error AppxPackager::add_attributes(PKCS7_SIGNER_INFO * p_signer_info) { - - // Add opus attribute - asn1::SPCSpOpusInfo* opus = asn1::SPCSpOpusInfo_new(); - if (!opus) return openssl_error(ERR_peek_last_error()); - ASN1_STRING* opus_value = - EncodedASN1::FromItem<asn1::SPCSpOpusInfo, - asn1::i2d_SPCSpOpusInfo>(opus) - .ToSequenceString(); - - if (!PKCS7_add_signed_attribute( - p_signer_info, - OBJ_txt2nid(SPC_SP_OPUS_INFO_OBJID), - V_ASN1_SEQUENCE, - opus_value - )) { - - asn1::SPCSpOpusInfo_free(opus); - - ASN1_STRING_free(opus_value); - return openssl_error(ERR_peek_last_error()); - } - - // Add content type attribute - if (!PKCS7_add_signed_attribute( - p_signer_info, - NID_pkcs9_contentType, - V_ASN1_OBJECT, - OBJ_txt2obj(SPC_INDIRECT_DATA_OBJID, 1) - )) { - - asn1::SPCSpOpusInfo_free(opus); - ASN1_STRING_free(opus_value); - return openssl_error(ERR_peek_last_error()); - } - - // Add statement type attribute - asn1::SPCStatementType* statement_type = asn1::SPCStatementType_new(); - if (!statement_type) return openssl_error(ERR_peek_last_error()); - - statement_type->type = OBJ_nid2obj(NID_ms_code_ind); - ASN1_STRING* statement_type_value = - EncodedASN1::FromItem<asn1::SPCStatementType, - asn1::i2d_SPCStatementType>(statement_type) - .ToSequenceString(); - - if (!PKCS7_add_signed_attribute( - p_signer_info, - OBJ_txt2nid(SPC_STATEMENT_TYPE_OBJID), - V_ASN1_SEQUENCE, - statement_type_value - )) { - - ASN1_STRING_free(opus_value); - asn1::SPCStatementType_free(statement_type); - ASN1_STRING_free(statement_type_value); - - return openssl_error(ERR_peek_last_error()); + return true; } - return OK; - -} - -void AppxPackager::make_digests() { - - // AXPC - SHA256_Final(digests.axpc, &axpc_context); - - // AXCD - SHA256_Final(digests.axcd, &axcd_context); - - // AXCI - for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) - digests.axci[i] = 0; - -} - -void AppxPackager::write_digest(Vector<uint8_t>& p_out_buffer) { + bool _valid_guid(const String &p_guid) const { - // Size of digests plus 6 32-bit magic numbers - p_out_buffer.resize((SHA256_DIGEST_LENGTH * 5) + (6 * 4)); + Vector<String> parts = p_guid.split("-"); - int offs = 0; - - // APPX - uint32_t sig = 0x58505041; - offs += buf_put_int32(sig, &p_out_buffer[offs]); + if (parts.size() != 5) return false; + if (parts[0].length() != 8) return false; + for (int i = 1; i < 4; i++) + if (parts[i].length() != 4) return false; + if (parts[4].length() != 12) return false; - // AXPC - uint32_t axpc_sig = 0x43505841; - offs += buf_put_int32(axpc_sig, &p_out_buffer[offs]); - for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) { - p_out_buffer[offs++] = digests.axpc[i]; + return true; } - // AXCD - uint32_t axcd_sig = 0x44435841; - offs += buf_put_int32(axcd_sig, &p_out_buffer[offs]); - for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) { - p_out_buffer[offs++] = digests.axcd[i]; - } + bool _valid_bgcolor(const String &p_color) const { - // AXCT - uint32_t axct_sig = 0x54435841; - offs += buf_put_int32(axct_sig, &p_out_buffer[offs]); - for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) { - p_out_buffer[offs++] = digests.axct[i]; - } + if (p_color.empty()) return true; + if (p_color.begins_with("#") && p_color.is_valid_html_color()) return true; - // AXBM - uint32_t axbm_sig = 0x4D425841; - offs += buf_put_int32(axbm_sig, &p_out_buffer[offs]); - for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) { - p_out_buffer[offs++] = digests.axbm[i]; - } + // Colors from https://msdn.microsoft.com/en-us/library/windows/apps/dn934817.aspx + static const char *valid_colors[] = { + "aliceBlue", "antiqueWhite", "aqua", "aquamarine", "azure", "beige", + "bisque", "black", "blanchedAlmond", "blue", "blueViolet", "brown", + "burlyWood", "cadetBlue", "chartreuse", "chocolate", "coral", "cornflowerBlue", + "cornsilk", "crimson", "cyan", "darkBlue", "darkCyan", "darkGoldenrod", + "darkGray", "darkGreen", "darkKhaki", "darkMagenta", "darkOliveGreen", "darkOrange", + "darkOrchid", "darkRed", "darkSalmon", "darkSeaGreen", "darkSlateBlue", "darkSlateGray", + "darkTurquoise", "darkViolet", "deepPink", "deepSkyBlue", "dimGray", "dodgerBlue", + "firebrick", "floralWhite", "forestGreen", "fuchsia", "gainsboro", "ghostWhite", + "gold", "goldenrod", "gray", "green", "greenYellow", "honeydew", + "hotPink", "indianRed", "indigo", "ivory", "khaki", "lavender", + "lavenderBlush", "lawnGreen", "lemonChiffon", "lightBlue", "lightCoral", "lightCyan", + "lightGoldenrodYellow", "lightGreen", "lightGray", "lightPink", "lightSalmon", "lightSeaGreen", + "lightSkyBlue", "lightSlateGray", "lightSteelBlue", "lightYellow", "lime", "limeGreen", + "linen", "magenta", "maroon", "mediumAquamarine", "mediumBlue", "mediumOrchid", + "mediumPurple", "mediumSeaGreen", "mediumSlateBlue", "mediumSpringGreen", "mediumTurquoise", "mediumVioletRed", + "midnightBlue", "mintCream", "mistyRose", "moccasin", "navajoWhite", "navy", + "oldLace", "olive", "oliveDrab", "orange", "orangeRed", "orchid", + "paleGoldenrod", "paleGreen", "paleTurquoise", "paleVioletRed", "papayaWhip", "peachPuff", + "peru", "pink", "plum", "powderBlue", "purple", "red", + "rosyBrown", "royalBlue", "saddleBrown", "salmon", "sandyBrown", "seaGreen", + "seaShell", "sienna", "silver", "skyBlue", "slateBlue", "slateGray", + "snow", "springGreen", "steelBlue", "tan", "teal", "thistle", + "tomato", "transparent", "turquoise", "violet", "wheat", "white", + "whiteSmoke", "yellow", "yellowGreen", + NULL + }; - // AXCI - uint32_t axci_sig = 0x49435841; - offs += buf_put_int32(axci_sig, &p_out_buffer[offs]); - for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) { - p_out_buffer[offs++] = digests.axci[i]; - } + const char **color = valid_colors; - // Done! -} - -Error AppxPackager::read_cert_file(const String & p_path, const String &p_password, CertFile* p_out_cf) { - - ERR_FAIL_COND_V(!p_out_cf, ERR_INVALID_PARAMETER); - - BIO* bio = BIO_new_file(p_path.utf8().get_data(), "rb"); - if (!bio) { - return openssl_error(ERR_peek_last_error()); - } - - PKCS12* data = d2i_PKCS12_bio(bio, NULL); - if (!data) { - BIO_free(bio); - return openssl_error(ERR_peek_last_error()); - } - - /* Fails to link with GCC, need to solve when implement signing - if (!PKCS12_parse(data, p_password.utf8().get_data(), &p_out_cf->private_key, &p_out_cf->certificate, NULL)) { - PKCS12_free(data); - BIO_free(bio); - return openssl_error(ERR_peek_last_error()); - }*/ - - if (!p_out_cf->private_key) { - PKCS12_free(data); - BIO_free(bio); - return openssl_error(ERR_peek_last_error()); - } + while (*color) { + if (p_color == *color) return true; + color++; + } - if (!p_out_cf->certificate) { - PKCS12_free(data); - BIO_free(bio); - return openssl_error(ERR_peek_last_error()); + return false; } - PKCS12_free(data); - BIO_free(bio); + bool _valid_image(const StreamTexture *p_image, int p_width, int p_height) const { - return OK; -} + if (!p_image) { + return false; + } -Error AppxPackager::sign(const CertFile & p_cert, const AppxDigests & digests, PKCS7 * p_out_signature) { + // TODO: Add resource creation or image rescaling to enable other scales: + // 1.25, 1.5, 2.0 + real_t scales[] = { 1.0 }; + bool valid_w = false; + bool valid_h = false; - OpenSSL_add_all_algorithms(); + for (int i = 0; i < 1; i++) { - // Register object IDs - OBJ_create_and_add_object(SPC_INDIRECT_DATA_OBJID, NULL, NULL); - OBJ_create_and_add_object(SPC_SIPINFO_OBJID, NULL, NULL); - OBJ_create_and_add_object(SPC_SP_OPUS_INFO_OBJID, NULL, NULL); - OBJ_create_and_add_object(SPC_STATEMENT_TYPE_OBJID, NULL, NULL); + int w = ceil(p_width * scales[i]); + int h = ceil(p_height * scales[i]); - if (!PKCS7_set_type(p_out_signature, NID_pkcs7_signed)) { + if (w == p_image->get_width()) + valid_w = true; + if (h == p_image->get_height()) + valid_h = true; + } - return openssl_error(ERR_peek_last_error()); + return valid_w && valid_h; } - PKCS7_SIGNER_INFO *signer_info = PKCS7_add_signature(p_out_signature, p_cert.certificate, p_cert.private_key, EVP_sha256()); - if (!signer_info) return openssl_error(ERR_peek_last_error()); + Vector<uint8_t> _fix_manifest(const Ref<EditorExportPreset> &p_preset, const Vector<uint8_t> &p_template, bool p_give_internet) const { - add_attributes(signer_info); + String result = String::utf8((const char *)p_template.ptr(), p_template.size()); - if (!PKCS7_content_new(p_out_signature, NID_pkcs7_data)) { + result = result.replace("$godot_version$", VERSION_FULL_NAME); - return openssl_error(ERR_peek_last_error()); - } + result = result.replace("$identity_name$", p_preset->get("package/unique_name")); + result = result.replace("$publisher$", p_preset->get("package/publisher")); - if (!PKCS7_add_certificate(p_out_signature, p_cert.certificate)) { + result = result.replace("$product_guid$", p_preset->get("identity/product_guid")); + result = result.replace("$publisher_guid$", p_preset->get("identity/publisher_guid")); - return openssl_error(ERR_peek_last_error()); - } + String version = itos(p_preset->get("version/major")) + "." + itos(p_preset->get("version/minor")) + "." + itos(p_preset->get("version/build")) + "." + itos(p_preset->get("version/revision")); + result = result.replace("$version_string$", version); - asn1::SPCIndirectDataContent* idc = asn1::SPCIndirectDataContent_new(); + Platform arch = (Platform)(int)p_preset->get("architecture/target"); + String architecture = arch == ARM ? "ARM" : arch == X86 ? "x86" : "x64"; + result = result.replace("$architecture$", architecture); - MakeIndirectDataContent(*idc); - EncodedASN1 idc_encoded = - EncodedASN1::FromItem<asn1::SPCIndirectDataContent, asn1::i2d_SPCIndirectDataContent>(idc); + result = result.replace("$display_name$", String(p_preset->get("package/display_name")).empty() ? (String)ProjectSettings::get_singleton()->get("application/config/name") : String(p_preset->get("package/display_name"))); - BIO* signed_data = PKCS7_dataInit(p_out_signature, NULL); - - if (idc_encoded.size() < 2) { - - ERR_EXPLAIN("Invalid encoded size"); - ERR_FAIL_V(FAILED); - } + result = result.replace("$publisher_display_name$", p_preset->get("package/publisher_display_name")); + result = result.replace("$app_description$", p_preset->get("package/description")); + result = result.replace("$bg_color$", p_preset->get("images/background_color")); + result = result.replace("$short_name$", p_preset->get("package/short_name")); - if ((idc_encoded.data()[1] & 0x80) == 0x00) { - - ERR_EXPLAIN("Invalid encoded data"); - ERR_FAIL_V(FAILED); - } + String name_on_tiles = ""; + if ((bool)p_preset->get("tiles/show_name_on_square150x150")) { + name_on_tiles += " <uap:ShowOn Tile=\"square150x150Logo\" />\n"; + } + if ((bool)p_preset->get("tiles/show_name_on_wide310x150")) { + name_on_tiles += " <uap:ShowOn Tile=\"wide310x150Logo\" />\n"; + } + if ((bool)p_preset->get("tiles/show_name_on_square310x310")) { + name_on_tiles += " <uap:ShowOn Tile=\"square310x310Logo\" />\n"; + } - size_t skip = 4; + String show_name_on_tiles = ""; + if (!name_on_tiles.empty()) { + show_name_on_tiles = "<uap:ShowNameOnTiles>\n" + name_on_tiles + " </uap:ShowNameOnTiles>"; + } - if (BIO_write(signed_data, idc_encoded.data() + skip, idc_encoded.size() - skip) - != idc_encoded.size() - skip) { + result = result.replace("$name_on_tiles$", name_on_tiles); - return openssl_error(ERR_peek_last_error()); - } - if (BIO_flush(signed_data) != 1) { + String rotations = ""; + if ((bool)p_preset->get("orientation/landscape")) { + rotations += " <uap:Rotation Preference=\"landscape\" />\n"; + } + if ((bool)p_preset->get("orientation/portrait")) { + rotations += " <uap:Rotation Preference=\"portrait\" />\n"; + } + if ((bool)p_preset->get("orientation/landscape_flipped")) { + rotations += " <uap:Rotation Preference=\"landscapeFlipped\" />\n"; + } + if ((bool)p_preset->get("orientation/portrait_flipped")) { + rotations += " <uap:Rotation Preference=\"portraitFlipped\" />\n"; + } - return openssl_error(ERR_peek_last_error()); - } + String rotation_preference = ""; + if (!rotations.empty()) { + rotation_preference = "<uap:InitialRotationPreference>\n" + rotations + " </uap:InitialRotationPreference>"; + } - if (!PKCS7_dataFinal(p_out_signature, signed_data)) { + result = result.replace("$rotation_preference$", rotation_preference); - return openssl_error(ERR_peek_last_error()); - } + String capabilities_elements = ""; + const char **basic = uwp_capabilities; + while (*basic) { + if ((bool)p_preset->get("capabilities/" + String(*basic))) { + capabilities_elements += " <Capability Name=\"" + String(*basic) + "\" />\n"; + } + basic++; + } + const char **uap = uwp_uap_capabilities; + while (*uap) { + if ((bool)p_preset->get("capabilities/" + String(*uap))) { + capabilities_elements += " <uap:Capability Name=\"" + String(*uap) + "\" />\n"; + } + uap++; + } + const char **device = uwp_device_capabilites; + while (*device) { + if ((bool)p_preset->get("capabilities/" + String(*device))) { + capabilities_elements += " <DeviceCapability Name=\"" + String(*device) + "\" />\n"; + } + device++; + } - PKCS7* content = PKCS7_new(); - if (!content) { + if (!((bool)p_preset->get("capabilities/internetClient")) && p_give_internet) { + capabilities_elements += " <Capability Name=\"internetClient\" />\n"; + } - return openssl_error(ERR_peek_last_error()); - } + String capabilities_string = "<Capabilities />"; + if (!capabilities_elements.empty()) { + capabilities_string = "<Capabilities>\n" + capabilities_elements + " </Capabilities>"; + } - content->type = OBJ_txt2obj(SPC_INDIRECT_DATA_OBJID, 1); + result = result.replace("$capabilities_place$", capabilities_string); - ASN1_TYPE* idc_sequence = idc_encoded.ToSequenceType(); - content->d.other = idc_sequence; + Vector<uint8_t> r_ret; + r_ret.resize(result.length()); - if (!PKCS7_set_content(p_out_signature, content)) { + for (int i = 0; i < result.length(); i++) + r_ret[i] = result.utf8().get(i); - return openssl_error(ERR_peek_last_error()); + return r_ret; } - return OK; -} + Vector<uint8_t> _get_image_data(const Ref<EditorExportPreset> &p_preset, const String &p_path) { -#endif // OPENSSL_ENABLED + Vector<uint8_t> data; + StreamTexture *image; -//////////////////////////////////////////////////////////////////// + if (p_path.find("StoreLogo") != -1) { + image = p_preset->get("images/store_logo").is_zero() ? NULL : ((Object *)p_preset->get("images/store_logo"))->cast_to<StreamTexture>(); + } else if (p_path.find("Square44x44Logo") != -1) { + image = p_preset->get("images/square44x44_logo").is_zero() ? NULL : ((Object *)p_preset->get("images/square44x44_logo"))->cast_to<StreamTexture>(); + } else if (p_path.find("Square71x71Logo") != -1) { + image = p_preset->get("images/square71x71_logo").is_zero() ? NULL : ((Object *)p_preset->get("images/square71x71_logo"))->cast_to<StreamTexture>(); + } else if (p_path.find("Square150x150Logo") != -1) { + image = p_preset->get("images/square150x150_logo").is_zero() ? NULL : ((Object *)p_preset->get("images/square150x150_logo"))->cast_to<StreamTexture>(); + } else if (p_path.find("Square310x310Logo") != -1) { + image = p_preset->get("images/square310x310_logo").is_zero() ? NULL : ((Object *)p_preset->get("images/square310x310_logo"))->cast_to<StreamTexture>(); + } else if (p_path.find("Wide310x150Logo") != -1) { + image = p_preset->get("images/wide310x150_logo").is_zero() ? NULL : ((Object *)p_preset->get("images/wide310x150_logo"))->cast_to<StreamTexture>(); + } else if (p_path.find("SplashScreen") != -1) { + image = p_preset->get("images/splash_screen").is_zero() ? NULL : ((Object *)p_preset->get("images/splash_screen"))->cast_to<StreamTexture>(); + } + if (!image) return data; -bool EditorExportPlatformUWP::_valid_resource_name(const String &p_name) const { + String tmp_path = EditorSettings::get_singleton()->get_settings_path().plus_file("tmp/uwp_tmp_logo.png"); - if (p_name.empty()) return false; - if (p_name.ends_with(".")) return false; + Error err = image->get_data()->save_png(tmp_path); - static const char* invalid_names[] = { - "CON","PRN","AUX","NUL","COM1","COM2","COM3","COM4","COM5","COM6","COM7", - "COM8","COM9","LPT1","LPT2","LPT3","LPT4","LPT5","LPT6","LPT7","LPT8","LPT9", - NULL - }; + if (err != OK) { - const char** t = invalid_names; - while (*t) { - if (p_name == *t) return false; - t++; - } + String err_string = "Couldn't save temp logo file."; - return true; -} + EditorNode::add_io_error(err_string); + ERR_EXPLAIN(err_string); + ERR_FAIL_V(data); + } -bool EditorExportPlatformUWP::_valid_guid(const String & p_guid) const { + FileAccess *f = FileAccess::open(tmp_path, FileAccess::READ, &err); - Vector<String> parts = p_guid.split("-"); + if (err != OK) { - if (parts.size() != 5) return false; - if (parts[0].length() != 8) return false; - for (int i = 1; i < 4; i++) - if (parts[i].length() != 4) return false; - if (parts[4].length() != 12) return false; + String err_string = "Couldn't open temp logo file."; - return true; -} + EditorNode::add_io_error(err_string); + ERR_EXPLAIN(err_string); + ERR_FAIL_V(data); + } -bool EditorExportPlatformUWP::_valid_bgcolor(const String & p_color) const { + data.resize(f->get_len()); + f->get_buffer(data.ptr(), data.size()); - if (p_color.empty()) return true; - if (p_color.begins_with("#") && p_color.is_valid_html_color()) return true; + f->close(); + memdelete(f); - // Colors from https://msdn.microsoft.com/en-us/library/windows/apps/dn934817.aspx - static const char* valid_colors[] = { - "aliceBlue","antiqueWhite","aqua","aquamarine","azure","beige", - "bisque","black","blanchedAlmond","blue","blueViolet","brown", - "burlyWood","cadetBlue","chartreuse","chocolate","coral","cornflowerBlue", - "cornsilk","crimson","cyan","darkBlue","darkCyan","darkGoldenrod", - "darkGray","darkGreen","darkKhaki","darkMagenta","darkOliveGreen","darkOrange", - "darkOrchid","darkRed","darkSalmon","darkSeaGreen","darkSlateBlue","darkSlateGray", - "darkTurquoise","darkViolet","deepPink","deepSkyBlue","dimGray","dodgerBlue", - "firebrick","floralWhite","forestGreen","fuchsia","gainsboro","ghostWhite", - "gold","goldenrod","gray","green","greenYellow","honeydew", - "hotPink","indianRed","indigo","ivory","khaki","lavender", - "lavenderBlush","lawnGreen","lemonChiffon","lightBlue","lightCoral","lightCyan", - "lightGoldenrodYellow","lightGreen","lightGray","lightPink","lightSalmon","lightSeaGreen", - "lightSkyBlue","lightSlateGray","lightSteelBlue","lightYellow","lime","limeGreen", - "linen","magenta","maroon","mediumAquamarine","mediumBlue","mediumOrchid", - "mediumPurple","mediumSeaGreen","mediumSlateBlue","mediumSpringGreen","mediumTurquoise","mediumVioletRed", - "midnightBlue","mintCream","mistyRose","moccasin","navajoWhite","navy", - "oldLace","olive","oliveDrab","orange","orangeRed","orchid", - "paleGoldenrod","paleGreen","paleTurquoise","paleVioletRed","papayaWhip","peachPuff", - "peru","pink","plum","powderBlue","purple","red", - "rosyBrown","royalBlue","saddleBrown","salmon","sandyBrown","seaGreen", - "seaShell","sienna","silver","skyBlue","slateBlue","slateGray", - "snow","springGreen","steelBlue","tan","teal","thistle", - "tomato","transparent","turquoise","violet","wheat","white", - "whiteSmoke","yellow","yellowGreen", - NULL - }; + // Delete temp file + DirAccess *dir = DirAccess::open(tmp_path.get_base_dir(), &err); - const char** color = valid_colors; + if (err != OK) { - while(*color) { - if (p_color == *color) return true; - color++; - } + String err_string = "Couldn't open temp path to remove temp logo file."; - return false; -} + EditorNode::add_io_error(err_string); + ERR_EXPLAIN(err_string); + ERR_FAIL_V(data); + } -bool EditorExportPlatformUWP::_valid_image(const Ref<ImageTexture> p_image, int p_width, int p_height) const { + err = dir->remove(tmp_path); - if (!p_image.is_valid()) return false; + memdelete(dir); - // TODO: Add resource creation or image rescaling to enable other scales: - // 1.25, 1.5, 2.0 - real_t scales[] = { 1.0 }; - bool valid_w = false; - bool valid_h = false; + if (err != OK) { - for (int i = 0; i < 1; i++) { + String err_string = "Couldn't remove temp logo file."; - int w = ceil(p_width * scales[i]); - int h = ceil(p_height * scales[i]); + EditorNode::add_io_error(err_string); + ERR_EXPLAIN(err_string); + ERR_FAIL_V(data); + } - if (w == p_image->get_width()) - valid_w = true; - if (h == p_image->get_height()) - valid_h = true; + return data; } - return valid_w && valid_h; -} - -Vector<uint8_t> EditorExportPlatformUWP::_fix_manifest(const Vector<uint8_t> &p_template, bool p_give_internet) const { - - String result = String::utf8((const char*)p_template.ptr(), p_template.size()); - - result = result.replace("$godot_version$", VERSION_FULL_NAME); - - result = result.replace("$identity_name$", unique_name); - result = result.replace("$publisher$", publisher); - - result = result.replace("$product_guid$", product_guid); - result = result.replace("$publisher_guid$", publisher_guid); + static bool _should_compress_asset(const String &p_path, const Vector<uint8_t> &p_data) { - String version = itos(version_major) + "." + itos(version_minor) + "." + itos(version_build) + "." + itos(version_revision); - result = result.replace("$version_string$", version); + /* TODO: This was copied verbatim from Android export. It should be + * refactored to the parent class and also be used for .zip export. + */ - String architecture = arch == ARM ? "ARM" : arch == X86 ? "x86" : "x64"; - result = result.replace("$architecture$", architecture); + /* + * By not compressing files with little or not benefit in doing so, + * a performance gain is expected at runtime. Moreover, if the APK is + * zip-aligned, assets stored as they are can be efficiently read by + * Android by memory-mapping them. + */ - result = result.replace("$display_name$", display_name.empty() ? (String)GlobalConfig::get_singleton()->get("application/name") : display_name); - result = result.replace("$publisher_display_name$", publisher_display_name); - result = result.replace("$app_description$", description); - result = result.replace("$bg_color$", background_color); - result = result.replace("$short_name$", short_name); + // -- Unconditional uncompress to mimic AAPT plus some other - String name_on_tiles = ""; - if (name_on_square150) { - name_on_tiles += " <uap:ShowOn Tile=\"square150x150Logo\" />\n"; - } - if (name_on_wide) { - name_on_tiles += " <uap:ShowOn Tile=\"wide310x150Logo\" />\n"; - } - if (name_on_square310) { - name_on_tiles += " <uap:ShowOn Tile=\"square310x310Logo\" />\n"; - } - - String show_name_on_tiles = ""; - if (!name_on_tiles.empty()) { - show_name_on_tiles = "<uap:ShowNameOnTiles>\n" + name_on_tiles + " </uap:ShowNameOnTiles>"; - } - - result = result.replace("$name_on_tiles$", name_on_tiles); - - String rotations = ""; - if (orientation_landscape) { - rotations += " <uap:Rotation Preference=\"landscape\" />\n"; - } - if (orientation_portrait) { - rotations += " <uap:Rotation Preference=\"portrait\" />\n"; - } - if (orientation_landscape_flipped) { - rotations += " <uap:Rotation Preference=\"landscapeFlipped\" />\n"; - } - if (orientation_portrait_flipped) { - rotations += " <uap:Rotation Preference=\"portraitFlipped\" />\n"; - } + static const char *unconditional_compress_ext[] = { + // From https://github.com/android/platform_frameworks_base/blob/master/tools/aapt/Package.cpp + // These formats are already compressed, or don't compress well: + ".jpg", ".jpeg", ".png", ".gif", + ".wav", ".mp2", ".mp3", ".ogg", ".aac", + ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet", + ".rtttl", ".imy", ".xmf", ".mp4", ".m4a", + ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2", + ".amr", ".awb", ".wma", ".wmv", + // Godot-specific: + ".webp", // Same reasoning as .png + ".cfb", // Don't let small config files slow-down startup + ".scn", // Binary scenes are usually already compressed + ".stex", // Streamable textures are usually already compressed + // Trailer for easier processing + NULL + }; - String rotation_preference = ""; - if (!rotations.empty()) { - rotation_preference = "<uap:InitialRotationPreference>\n" + rotations + " </uap:InitialRotationPreference>"; - } + for (const char **ext = unconditional_compress_ext; *ext; ++ext) { + if (p_path.to_lower().ends_with(String(*ext))) { + return false; + } + } - result = result.replace("$rotation_preference$", rotation_preference); + // -- Compressed resource? - String capabilities_elements = ""; - const char **basic = uwp_capabilities; - while (*basic) { - if (capabilities.has(*basic)) { - capabilities_elements += " <Capability Name=\"" + String(*basic) + "\" />\n"; - } - basic++; - } - const char **uap = uwp_uap_capabilities; - while (*uap) { - if (uap_capabilities.has(*uap)) { - capabilities_elements += " <uap:Capability Name=\"" + String(*uap) + "\" />\n"; - } - uap++; - } - const char **device = uwp_device_capabilites; - while (*device) { - if (uap_capabilities.has(*device)) { - capabilities_elements += " <DeviceCapability Name=\"" + String(*device) + "\" />\n"; + if (p_data.size() >= 4 && p_data[0] == 'R' && p_data[1] == 'S' && p_data[2] == 'C' && p_data[3] == 'C') { + // Already compressed + return false; } - device++; - } - if (!capabilities.has("internetClient") && p_give_internet) { - capabilities_elements += " <Capability Name=\"internetClient\" />\n"; - } + // --- TODO: Decide on texture resources according to their image compression setting - String capabilities_string = "<Capabilities />"; - if (!capabilities_elements.empty()) { - capabilities_string = "<Capabilities>\n" + capabilities_elements + " </Capabilities>"; + return true; } - result = result.replace("$capabilities_place$", capabilities_string); + static Error save_appx_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total) { - Vector<uint8_t> r_ret; - r_ret.resize(result.length()); + AppxPackager *packager = (AppxPackager *)p_userdata; + String dst_path = p_path.replace_first("res://", "game/"); - for (int i = 0; i < result.length(); i++) - r_ret[i] = result.utf8().get(i); + packager->add_file(dst_path, p_data.ptr(), p_data.size(), p_file, p_total, _should_compress_asset(p_path, p_data)); - return r_ret; -} - -Vector<uint8_t> EditorExportPlatformUWP::_get_image_data(const String & p_path) { - - Vector<uint8_t> data; - Ref<ImageTexture> ref; - - if (p_path.find("StoreLogo") != -1) { - ref = store_logo; - } else if (p_path.find("Square44x44Logo") != -1) { - ref = square44; - } else if (p_path.find("Square71x71Logo") != -1) { - ref = square71; - } else if (p_path.find("Square150x150Logo") != -1) { - ref = square150; - } else if (p_path.find("Square310x310Logo") != -1) { - ref = square310; - } else if (p_path.find("Wide310x150Logo") != -1) { - ref = wide310; - } else if (p_path.find("SplashScreen") != -1) { - ref = splash; + return OK; } - if (!ref.is_valid()) return data; - - - String tmp_path = EditorSettings::get_singleton()->get_settings_path().plus_file("tmp/uwp_tmp_logo.png"); - - Error err = ref->get_data().save_png(tmp_path); - - if (err != OK) { - - String err_string = "Couldn't save temp logo file."; - - EditorNode::add_io_error(err_string); - ERR_EXPLAIN(err_string); - ERR_FAIL_V(data); +public: + virtual String get_name() const { + return "Windows Universal"; } - - FileAccess* f = FileAccess::open(tmp_path, FileAccess::READ, &err); - - if (err != OK) { - - String err_string = "Couldn't open temp logo file."; - - EditorNode::add_io_error(err_string); - ERR_EXPLAIN(err_string); - ERR_FAIL_V(data); + virtual String get_os_name() const { + return "UWP"; } - data.resize(f->get_len()); - f->get_buffer(data.ptr(), data.size()); - - f->close(); - memdelete(f); - - // Delete temp file - DirAccess* dir = DirAccess::open(tmp_path.get_base_dir(), &err); - - if (err != OK) { - - String err_string = "Couldn't open temp path to remove temp logo file."; - - EditorNode::add_io_error(err_string); - ERR_EXPLAIN(err_string); - ERR_FAIL_V(data); + virtual String get_binary_extension() const { + return "appx"; } - err = dir->remove(tmp_path); - - memdelete(dir); - - if (err != OK) { - - String err_string = "Couldn't remove temp logo file."; - - EditorNode::add_io_error(err_string); - ERR_EXPLAIN(err_string); - ERR_FAIL_V(data); + virtual Ref<Texture> get_logo() const { + return logo; } - return data; -} + virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) { + r_features->push_back("s3tc"); + r_features->push_back("etc"); + } -Error EditorExportPlatformUWP::save_appx_file(void * p_userdata, const String & p_path, const Vector<uint8_t>& p_data, int p_file, int p_total) { + virtual void get_export_options(List<ExportOption> *r_options) { + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "architecture/target", PROPERTY_HINT_ENUM, "ARM,x86,x64"), 1)); - AppxPackager *packager = (AppxPackager*)p_userdata; - String dst_path = p_path.replace_first("res://", "game/"); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "command_line/extra_args"), "")); - packager->add_file(dst_path, p_data.ptr(), p_data.size(), p_file, p_total, _should_compress_asset(p_path, p_data)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/display_name"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/short_name"), "Godot")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/unique_name"), "Godot.Engine")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/description"), "Godot Engine")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/publisher"), "CN=GodotEngine")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/publisher_display_name"), "Godot Engine")); - return OK; -} + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "identity/product_guid"), "00000000-0000-0000-0000-000000000000")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "identity/publisher_guid"), "00000000-0000-0000-0000-000000000000")); -bool EditorExportPlatformUWP::_should_compress_asset(const String & p_path, const Vector<uint8_t>& p_data) { + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/major"), 1)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/minor"), 0)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/build"), 0)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/revision"), 0)); - /* TODO: This was copied verbatim from Android export. It should be - * refactored to the parent class and also be used for .zip export. - */ + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/landscape"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/portrait"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/landscape_flipped"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/portrait_flipped"), true)); - /* - * By not compressing files with little or not benefit in doing so, - * a performance gain is expected at runtime. Moreover, if the APK is - * zip-aligned, assets stored as they are can be efficiently read by - * Android by memory-mapping them. - */ + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "images/background_color"), "transparent")); + r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/store_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture"), Variant())); + r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square44x44_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture"), Variant())); + r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square71x71_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture"), Variant())); + r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square150x150_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture"), Variant())); + r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square310x310_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture"), Variant())); + r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/wide310x150_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture"), Variant())); + r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/splash_screen", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture"), Variant())); - // -- Unconditional uncompress to mimic AAPT plus some other + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "tiles/show_name_on_square150x150"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "tiles/show_name_on_wide310x150"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "tiles/show_name_on_square310x310"), false)); - static const char* unconditional_compress_ext[] = { - // From https://github.com/android/platform_frameworks_base/blob/master/tools/aapt/Package.cpp - // These formats are already compressed, or don't compress well: - ".jpg", ".jpeg", ".png", ".gif", - ".wav", ".mp2", ".mp3", ".ogg", ".aac", - ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet", - ".rtttl", ".imy", ".xmf", ".mp4", ".m4a", - ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2", - ".amr", ".awb", ".wma", ".wmv", - // Godot-specific: - ".webp", // Same reasoning as .png - ".cfb", // Don't let small config files slow-down startup - // Trailer for easier processing - NULL - }; + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "zip"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "zip"), "")); - for (const char** ext = unconditional_compress_ext; *ext; ++ext) { - if (p_path.to_lower().ends_with(String(*ext))) { - return false; + // Capabilites + const char **basic = uwp_capabilities; + while (*basic) { + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/" + String(*basic).camelcase_to_underscore(false)), false)); + basic++; } - } - // -- Compressed resource? + const char **uap = uwp_uap_capabilities; + while (*uap) { + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/" + String(*uap).camelcase_to_underscore(false)), false)); + uap++; + } - if (p_data.size() >= 4 && p_data[0] == 'R' && p_data[1] == 'S' && p_data[2] == 'C' && p_data[3] == 'C') { - // Already compressed - return false; + const char **device = uwp_device_capabilites; + while (*device) { + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/" + String(*device).camelcase_to_underscore(false)), false)); + device++; + } } - // --- TODO: Decide on texture resources according to their image compression setting - - return true; -} - -bool EditorExportPlatformUWP::_set(const StringName& p_name, const Variant& p_value) { - - String n = p_name; - - if (n == "architecture/target") - arch = (Platform)((int)p_value); - else if (n == "custom_package/debug") - custom_debug_package = p_value; - else if (n == "custom_package/release") - custom_release_package = p_value; - else if (n == "command_line/extra_args") - cmdline = p_value; - else if (n == "package/display_name") - display_name = p_value; - else if (n == "package/short_name") - short_name = p_value; - else if (n == "package/unique_name") - unique_name = p_value; - else if (n == "package/description") - description = p_value; - else if (n == "package/publisher") - publisher = p_value; - else if (n == "package/publisher_display_name") - publisher_display_name = p_value; - else if (n == "identity/product_guid") - product_guid = p_value; - else if (n == "identity/publisher_guid") - publisher_guid = p_value; - else if (n == "version/major") - version_major = p_value; - else if (n == "version/minor") - version_minor = p_value; - else if (n == "version/build") - version_build = p_value; - else if (n == "version/revision") - version_revision = p_value; - else if (n == "orientation/landscape") - orientation_landscape = p_value; - else if (n == "orientation/portrait") - orientation_portrait = p_value; - else if (n == "orientation/landscape_flipped") - orientation_landscape_flipped = p_value; - else if (n == "orientation/portrait_flipped") - orientation_portrait_flipped = p_value; - else if (n == "images/background_color") - background_color = p_value; - else if (n == "images/store_logo") - store_logo = p_value; - else if (n == "images/square44x44_logo") - square44 = p_value; - else if (n == "images/square71x71_logo") - square71 = p_value; - else if (n == "images/square150x150_logo") - square150 = p_value; - else if (n == "images/square310x310_logo") - square310 = p_value; - else if (n == "images/wide310x150_logo") - wide310 = p_value; - else if (n == "images/splash_screen") - splash = p_value; - else if (n == "tiles/show_name_on_square150x150") - name_on_square150 = p_value; - else if (n == "tiles/show_name_on_wide310x150") - name_on_wide = p_value; - else if (n == "tiles/show_name_on_square310x310") - name_on_square310 = p_value; - -#if 0 // Signing disabled - else if (n == "signing/sign") - sign_package = p_value; - else if (n == "signing/certificate_file") - certificate_path = p_value; - else if (n == "signing/certificate_password") - certificate_pass = p_value; -#endif - else if (n.begins_with("capabilities/")) { - - String what = n.get_slice("/", 1).replace("_", ""); - bool enable = p_value; - - if (array_has(uwp_capabilities, what.utf8().get_data())) { - - if (enable) - capabilities.insert(what); - else - capabilities.erase(what); - - } else if (array_has(uwp_uap_capabilities, what.utf8().get_data())) { + virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { + String err; + bool valid = true; + Platform arch = (Platform)(int)(p_preset->get("architecture/target")); - if (enable) - uap_capabilities.insert(what); - else - uap_capabilities.erase(what); + String custom_debug_binary = p_preset->get("custom_template/debug"); + String custom_release_binary = p_preset->get("custom_template/release"); - } else if (array_has(uwp_device_capabilites, what.utf8().get_data())) { + String platform_infix; - if (enable) - device_capabilities.insert(what); - else - device_capabilities.erase(what); + switch (arch) { + case EditorExportUWP::ARM: { + platform_infix = "arm"; + } break; + case EditorExportUWP::X86: { + platform_infix = "x86"; + } break; + case EditorExportUWP::X64: { + platform_infix = "x64"; + } break; } - } else return false; - - return true; -} - -bool EditorExportPlatformUWP::_get(const StringName& p_name, Variant &r_ret) const { - - String n = p_name; - if (n == "architecture/target") - r_ret = (int)arch; - else if (n == "custom_package/debug") - r_ret = custom_debug_package; - else if (n == "custom_package/release") - r_ret = custom_release_package; - else if (n == "command_line/extra_args") - r_ret = cmdline; - else if (n == "package/display_name") - r_ret = display_name; - else if (n == "package/short_name") - r_ret = short_name; - else if (n == "package/unique_name") - r_ret = unique_name; - else if (n == "package/description") - r_ret = description; - else if (n == "package/publisher") - r_ret = publisher; - else if (n == "package/publisher_display_name") - r_ret = publisher_display_name; - else if (n == "identity/product_guid") - r_ret = product_guid; - else if (n == "identity/publisher_guid") - r_ret = publisher_guid; - else if (n == "version/major") - r_ret = version_major; - else if (n == "version/minor") - r_ret = version_minor; - else if (n == "version/build") - r_ret = version_build; - else if (n == "version/revision") - r_ret = version_revision; - else if (n == "orientation/landscape") - r_ret = orientation_landscape; - else if (n == "orientation/portrait") - r_ret = orientation_portrait; - else if (n == "orientation/landscape_flipped") - r_ret = orientation_landscape_flipped; - else if (n == "orientation/portrait_flipped") - r_ret = orientation_portrait_flipped; - else if (n == "images/background_color") - r_ret = background_color; - else if (n == "images/store_logo") - r_ret = store_logo; - else if (n == "images/square44x44_logo") - r_ret = square44; - else if (n == "images/square71x71_logo") - r_ret = square71; - else if (n == "images/square150x150_logo") - r_ret = square150; - else if (n == "images/square310x310_logo") - r_ret = square310; - else if (n == "images/wide310x150_logo") - r_ret = wide310; - else if (n == "images/splash_screen") - r_ret = splash; - else if (n == "tiles/show_name_on_square150x150") - r_ret = name_on_square150; - else if (n == "tiles/show_name_on_wide310x150") - r_ret = name_on_wide; - else if (n == "tiles/show_name_on_square310x310") - r_ret = name_on_square310; - -#if 0 // Signing disabled - else if (n == "signing/sign") - r_ret = sign_package; - else if (n == "signing/certificate_file") - r_ret = certificate_path; - else if (n == "signing/certificate_password") - r_ret = certificate_pass; -#endif - else if (n.begins_with("capabilities/")) { - - String what = n.get_slice("/", 1).replace("_", ""); - - if (array_has(uwp_capabilities, what.utf8().get_data())) { - - r_ret = capabilities.has(what); - - } else if (array_has(uwp_uap_capabilities, what.utf8().get_data())) { - - r_ret = uap_capabilities.has(what); - - } else if (array_has(uwp_device_capabilites, what.utf8().get_data())) { - - r_ret = device_capabilities.has(what); + if (!exists_export_template("uwp_" + platform_infix + "_debug.zip", &err) || !exists_export_template("uwp_" + platform_infix + "_debug.zip", &err)) { + valid = false; + r_missing_templates = true; } - } else return false; - - return true; -} - -void EditorExportPlatformUWP::_get_property_list(List<PropertyInfo>* p_list) const { - - p_list->push_back(PropertyInfo(Variant::STRING, "custom_package/debug", PROPERTY_HINT_GLOBAL_FILE, "appx")); - p_list->push_back(PropertyInfo(Variant::STRING, "custom_package/release", PROPERTY_HINT_GLOBAL_FILE, "appx")); - - p_list->push_back(PropertyInfo(Variant::INT, "architecture/target", PROPERTY_HINT_ENUM, "ARM,x86,x64")); - - p_list->push_back(PropertyInfo(Variant::STRING, "command_line/extra_args")); - p_list->push_back(PropertyInfo(Variant::STRING, "package/display_name")); - p_list->push_back(PropertyInfo(Variant::STRING, "package/short_name")); - p_list->push_back(PropertyInfo(Variant::STRING, "package/unique_name")); - p_list->push_back(PropertyInfo(Variant::STRING, "package/description")); - p_list->push_back(PropertyInfo(Variant::STRING, "package/publisher")); - p_list->push_back(PropertyInfo(Variant::STRING, "package/publisher_display_name")); - - p_list->push_back(PropertyInfo(Variant::STRING, "identity/product_guid")); - p_list->push_back(PropertyInfo(Variant::STRING, "identity/publisher_guid")); - - p_list->push_back(PropertyInfo(Variant::INT, "version/major")); - p_list->push_back(PropertyInfo(Variant::INT, "version/minor")); - p_list->push_back(PropertyInfo(Variant::INT, "version/build")); - p_list->push_back(PropertyInfo(Variant::INT, "version/revision")); - - p_list->push_back(PropertyInfo(Variant::BOOL, "orientation/landscape")); - p_list->push_back(PropertyInfo(Variant::BOOL, "orientation/portrait")); - p_list->push_back(PropertyInfo(Variant::BOOL, "orientation/landscape_flipped")); - p_list->push_back(PropertyInfo(Variant::BOOL, "orientation/portrait_flipped")); - - p_list->push_back(PropertyInfo(Variant::STRING, "images/background_color")); - p_list->push_back(PropertyInfo(Variant::OBJECT, "images/store_logo", PROPERTY_HINT_RESOURCE_TYPE, "ImageTexture")); - p_list->push_back(PropertyInfo(Variant::OBJECT, "images/square44x44_logo", PROPERTY_HINT_RESOURCE_TYPE, "ImageTexture")); - p_list->push_back(PropertyInfo(Variant::OBJECT, "images/square71x71_logo", PROPERTY_HINT_RESOURCE_TYPE, "ImageTexture")); - p_list->push_back(PropertyInfo(Variant::OBJECT, "images/square150x150_logo", PROPERTY_HINT_RESOURCE_TYPE, "ImageTexture")); - p_list->push_back(PropertyInfo(Variant::OBJECT, "images/square310x310_logo", PROPERTY_HINT_RESOURCE_TYPE, "ImageTexture")); - p_list->push_back(PropertyInfo(Variant::OBJECT, "images/wide310x150_logo", PROPERTY_HINT_RESOURCE_TYPE, "ImageTexture")); - p_list->push_back(PropertyInfo(Variant::OBJECT, "images/splash_screen", PROPERTY_HINT_RESOURCE_TYPE, "ImageTexture")); - - p_list->push_back(PropertyInfo(Variant::BOOL, "tiles/show_name_on_square150x150")); - p_list->push_back(PropertyInfo(Variant::BOOL, "tiles/show_name_on_wide310x150")); - p_list->push_back(PropertyInfo(Variant::BOOL, "tiles/show_name_on_square310x310")); - -#if 0 // Signing does not work :( disabling for now - p_list->push_back(PropertyInfo(Variant::BOOL, "signing/sign")); - p_list->push_back(PropertyInfo(Variant::STRING, "signing/certificate_file", PROPERTY_HINT_GLOBAL_FILE, "pfx")); - p_list->push_back(PropertyInfo(Variant::STRING, "signing/certificate_password")); -#endif - - // Capabilites - const char **basic = uwp_capabilities; - while (*basic) { - p_list->push_back(PropertyInfo(Variant::BOOL, "capabilities/" + String(*basic).camelcase_to_underscore(false))); - basic++; - } - - const char **uap = uwp_uap_capabilities; - while (*uap) { - p_list->push_back(PropertyInfo(Variant::BOOL, "capabilities/" + String(*uap).camelcase_to_underscore(false))); - uap++; - } + if (!valid && custom_debug_binary == "" && custom_release_binary == "") { + if (!err.empty()) { + r_error = err; + } + return valid; + } - const char **device = uwp_device_capabilites; - while (*device) { - p_list->push_back(PropertyInfo(Variant::BOOL, "capabilities/" + String(*device).camelcase_to_underscore(false))); - device++; - } + bool dvalid = true; + bool rvalid = true; -} - -bool EditorExportPlatformUWP::can_export(String * r_error) const { + if (!FileAccess::exists(custom_debug_binary)) { + dvalid = false; + err = "\nCustom debug binary not found."; + } - String err; - bool valid = true; + if (!FileAccess::exists(custom_release_binary)) { + rvalid = false; + err += "\nCustom release binary not found."; + } - if (!exists_export_template("uwp_x86_debug.zip") || !exists_export_template("uwp_x86_release.zip") - || !exists_export_template("uwp_arm_debug.zip") || !exists_export_template("uwp_arm_release.zip") - || !exists_export_template("uwp_x64_debug.zip") || !exists_export_template("uwp_x64_release.zip")) { - valid = false; - err += TTR("No export templates found.\nDownload and install export templates.") + "\n"; - } + if (dvalid || rvalid) + valid = true; - if (custom_debug_package != "" && !FileAccess::exists(custom_debug_package)) { - valid = false; - err += TTR("Custom debug package not found.") + "\n"; - } + if (!valid) { + r_error = err; + return valid; + } - if (custom_release_package != "" && !FileAccess::exists(custom_release_package)) { - valid = false; - err += TTR("Custom release package not found.") + "\n"; - } + if (!_valid_resource_name(p_preset->get("package/unique_name"))) { + valid = false; + err += "\nInvalid unique name."; + } - if (!_valid_resource_name(unique_name)) { - valid = false; - err += TTR("Invalid unique name.") + "\n"; - } + if (!_valid_guid(p_preset->get("identity/product_guid"))) { + valid = false; + err += "\nInvalid product GUID."; + } - if (!_valid_guid(product_guid)) { - valid = false; - err += TTR("Invalid product GUID.") + "\n"; - } + if (!_valid_guid(p_preset->get("identity/publisher_guid"))) { + valid = false; + err += "\nInvalid publisher GUID."; + } - if (!_valid_guid(publisher_guid)) { - valid = false; - err += TTR("Invalid publisher GUID.") + "\n"; - } + if (!_valid_bgcolor(p_preset->get("images/background_color"))) { + valid = false; + err += "\nInvalid background color."; + } - if (!_valid_bgcolor(background_color)) { - valid = false; - err += TTR("Invalid background color.") + "\n"; - } + if (!p_preset->get("images/store_logo").is_zero() && !_valid_image(((Object *)p_preset->get("images/store_logo"))->cast_to<StreamTexture>(), 50, 50)) { + valid = false; + err += "\nInvalid Store Logo image dimensions (should be 50x50)."; + } - if (store_logo.is_valid() && !_valid_image(store_logo, 50, 50)) { - valid = false; - err += TTR("Invalid Store Logo image dimensions (should be 50x50).") + "\n"; - } + if (!p_preset->get("images/square44x44_logo").is_zero() && !_valid_image(((Object *)p_preset->get("images/square44x44_logo"))->cast_to<StreamTexture>(), 44, 44)) { + valid = false; + err += "\nInvalid square 44x44 logo image dimensions (should be 44x44)."; + } - if (square44.is_valid() && !_valid_image(square44, 44, 44)) { - valid = false; - err += TTR("Invalid square 44x44 logo image dimensions (should be 44x44).") + "\n"; - } + if (!p_preset->get("images/square71x71_logo").is_zero() && !_valid_image(((Object *)p_preset->get("images/square71x71_logo"))->cast_to<StreamTexture>(), 71, 71)) { + valid = false; + err += "\nInvalid square 71x71 logo image dimensions (should be 71x71)."; + } - if (square71.is_valid() && !_valid_image(square71, 71, 71)) { - valid = false; - err += TTR("Invalid square 71x71 logo image dimensions (should be 71x71).") + "\n"; - } + if (!p_preset->get("images/square150x150_logo").is_zero() && !_valid_image(((Object *)p_preset->get("images/square150x150_logo"))->cast_to<StreamTexture>(), 150, 0)) { + valid = false; + err += "\nInvalid square 150x150 logo image dimensions (should be 150x150)."; + } - if (square150.is_valid() && !_valid_image(square150, 150, 150)) { - valid = false; - err += TTR("Invalid square 150x150 logo image dimensions (should be 150x150).") + "\n"; - } + if (!p_preset->get("images/square310x310_logo").is_zero() && !_valid_image(((Object *)p_preset->get("images/square310x310_logo"))->cast_to<StreamTexture>(), 310, 310)) { + valid = false; + err += "\nInvalid square 310x310 logo image dimensions (should be 310x310)."; + } - if (square310.is_valid() && !_valid_image(square310, 310, 310)) { - valid = false; - err += TTR("Invalid square 310x310 logo image dimensions (should be 310x310).") + "\n"; - } + if (!p_preset->get("images/wide310x150_logo").is_zero() && !_valid_image(((Object *)p_preset->get("images/wide310x150_logo"))->cast_to<StreamTexture>(), 310, 150)) { + valid = false; + err += "\nInvalid wide 310x150 logo image dimensions (should be 310x150)."; + } - if (wide310.is_valid() && !_valid_image(wide310, 310, 150)) { - valid = false; - err += TTR("Invalid wide 310x150 logo image dimensions (should be 310x150).") + "\n"; - } + if (!p_preset->get("images/splash_screen").is_zero() && !_valid_image(((Object *)p_preset->get("images/splash_screen"))->cast_to<StreamTexture>(), 620, 300)) { + valid = false; + err += "\nInvalid splash screen image dimensions (should be 620x300)."; + } - if (splash.is_valid() && !_valid_image(splash, 620, 300)) { - valid = false; - err += TTR("Invalid splash screen image dimensions (should be 620x300).") + "\n"; + r_error = err; + return valid; } - if (r_error) - *r_error = err; + virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) { - return valid; -} + String src_appx; -Error EditorExportPlatformUWP::export_project(const String & p_path, bool p_debug, int p_flags) { + EditorProgress ep("export", "Exporting for Windows Universal", 7); - String src_appx; + if (p_debug) + src_appx = p_preset->get("custom_template/debug"); + else + src_appx = p_preset->get("custom_template/release"); - EditorProgress ep("export", "Exporting for Windows Universal", 7); + src_appx = src_appx.strip_edges(); - if (is_debug) - src_appx = custom_debug_package; - else - src_appx = custom_release_package; + Platform arch = (Platform)(int)p_preset->get("architecture/target"); - if (src_appx == "") { - String err; - if (p_debug) { + if (src_appx == "") { + String err, infix; switch (arch) { - case X86: { - src_appx = find_export_template("uwp_x86_debug.zip", &err); - break; - } - case X64: { - src_appx = find_export_template("uwp_x64_debug.zip", &err); - break; - } case ARM: { - src_appx = find_export_template("uwp_arm_debug.zip", &err); - break; - } - } - } else { - switch (arch) { + infix = "_arm_"; + } break; case X86: { - src_appx = find_export_template("uwp_x86_release.zip", &err); - break; - } + infix = "_x86_"; + } break; case X64: { - src_appx = find_export_template("uwp_x64_release.zip", &err); - break; - } - case ARM: { - src_appx = find_export_template("uwp_arm_release.zip", &err); - break; - } + infix = "_x64_"; + } break; + } + if (p_debug) { + src_appx = find_export_template("uwp" + infix + "debug.zip", &err); + } else { + src_appx = find_export_template("uwp" + infix + "release.zip", &err); + } + if (src_appx == "") { + EditorNode::add_io_error(err); + return ERR_FILE_NOT_FOUND; } } - if (src_appx == "") { - EditorNode::add_io_error(err); - return ERR_FILE_NOT_FOUND; - } - } - - Error err = OK; - - FileAccess *fa_pack = FileAccess::open(p_path, FileAccess::WRITE, &err); - ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); - - AppxPackager packager; - packager.init(fa_pack, sign_package ? AppxPackager::SIGN : AppxPackager::DONT_SIGN, certificate_path, certificate_pass); - - FileAccess *src_f = NULL; - zlib_filefunc_def io = zipio_create_io_from_file(&src_f); - ep.step("Creating package...", 0); + Error err = OK; - unzFile pkg = unzOpen2(src_appx.utf8().get_data(), &io); + FileAccess *fa_pack = FileAccess::open(p_path, FileAccess::WRITE, &err); + ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); - if (!pkg) { + AppxPackager packager; + packager.init(fa_pack); - EditorNode::add_io_error("Could not find template appx to export:\n" + src_appx); - return ERR_FILE_NOT_FOUND; - } - - int ret = unzGoToFirstFile(pkg); - - ep.step("Copying template files...", 1); - - EditorNode::progress_add_task("template_files", "Template files", 100); - packager.set_progress_task("template_files"); + FileAccess *src_f = NULL; + zlib_filefunc_def io = zipio_create_io_from_file(&src_f); - int template_files_amount = 9; - int template_file_no = 1; + ep.step("Creating package...", 0); - while (ret == UNZ_OK) { + unzFile pkg = unzOpen2(src_appx.utf8().get_data(), &io); - // get file name - unz_file_info info; - char fname[16834]; - ret = unzGetCurrentFileInfo(pkg, &info, fname, 16834, NULL, 0, NULL, 0); + if (!pkg) { - String path = fname; - - if (path.ends_with("/")) { - // Ignore directories - ret = unzGoToNextFile(pkg); - continue; + EditorNode::add_io_error("Could not find template appx to export:\n" + src_appx); + return ERR_FILE_NOT_FOUND; } - Vector<uint8_t> data; - bool do_read = true; + int ret = unzGoToFirstFile(pkg); - if (path.begins_with("Assets/")) { + ep.step("Copying template files...", 1); - path = path.replace(".scale-100", ""); + EditorNode::progress_add_task("template_files", "Template files", 100); + packager.set_progress_task("template_files"); - data = _get_image_data(path); - if (data.size() > 0) do_read = false; - } + int template_files_amount = 9; + int template_file_no = 1; - //read - if (do_read) { - data.resize(info.uncompressed_size); - unzOpenCurrentFile(pkg); - unzReadCurrentFile(pkg, data.ptr(), data.size()); - unzCloseCurrentFile(pkg); - } + while (ret == UNZ_OK) { - if (path == "AppxManifest.xml") { + // get file name + unz_file_info info; + char fname[16834]; + ret = unzGetCurrentFileInfo(pkg, &info, fname, 16834, NULL, 0, NULL, 0); - data = _fix_manifest(data, p_flags&(EXPORT_DUMB_CLIENT | EXPORT_REMOTE_DEBUG)); - } + String path = fname; - print_line("ADDING: " + path); - - packager.add_file(path, data.ptr(), data.size(), template_file_no++, template_files_amount, _should_compress_asset(path, data)); + if (path.ends_with("/")) { + // Ignore directories + ret = unzGoToNextFile(pkg); + continue; + } - ret = unzGoToNextFile(pkg); - } + Vector<uint8_t> data; + bool do_read = true; - EditorNode::progress_end_task("template_files"); + if (path.begins_with("Assets/")) { - ep.step("Creating command line...", 2); + path = path.replace(".scale-100", ""); - Vector<String> cl = cmdline.strip_edges().split(" "); - for (int i = 0;i<cl.size();i++) { - if (cl[i].strip_edges().length() == 0) { - cl.remove(i); - i--; - } - } + data = _get_image_data(p_preset, path); + if (data.size() > 0) do_read = false; + } - if (!(p_flags & EXPORT_DUMB_CLIENT)) { - cl.push_back("-path"); - cl.push_back("game"); - } + //read + if (do_read) { + data.resize(info.uncompressed_size); + unzOpenCurrentFile(pkg); + unzReadCurrentFile(pkg, data.ptr(), data.size()); + unzCloseCurrentFile(pkg); + } - gen_export_flags(cl, p_flags); + if (path == "AppxManifest.xml") { - // Command line file - Vector<uint8_t> clf; + data = _fix_manifest(p_preset, data, p_flags & (DEBUG_FLAG_DUMB_CLIENT | DEBUG_FLAG_REMOTE_DEBUG)); + } - // Argc - clf.resize(4); - encode_uint32(cl.size(), clf.ptr()); + print_line("ADDING: " + path); - for (int i = 0; i < cl.size(); i++) { + packager.add_file(path, data.ptr(), data.size(), template_file_no++, template_files_amount, _should_compress_asset(path, data)); - CharString txt = cl[i].utf8(); - int base = clf.size(); - clf.resize(base + 4 + txt.length()); - encode_uint32(txt.length(), &clf[base]); - copymem(&clf[base + 4], txt.ptr(), txt.length()); - print_line(itos(i) + " param: " + cl[i]); - } - - packager.add_file("__cl__.cl", clf.ptr(), clf.size(), -1, -1, false); + ret = unzGoToNextFile(pkg); + } - ep.step("Adding project files...", 3); + EditorNode::progress_end_task("template_files"); - EditorNode::progress_add_task("project_files", "Project Files", 100); - packager.set_progress_task("project_files"); + ep.step("Creating command line...", 2); - err = export_project_files(save_appx_file, &packager, false); + Vector<String> cl = ((String)p_preset->get("command_line/extra_args")).strip_edges().split(" "); + for (int i = 0; i < cl.size(); i++) { + if (cl[i].strip_edges().length() == 0) { + cl.remove(i); + i--; + } + } - EditorNode::progress_end_task("project_files"); + if (!(p_flags & DEBUG_FLAG_DUMB_CLIENT)) { + cl.push_back("-path"); + cl.push_back("game"); + } - ep.step("Closing package...", 7); + gen_export_flags(cl, p_flags); - unzClose(pkg); + // Command line file + Vector<uint8_t> clf; - packager.finish(); + // Argc + clf.resize(4); + encode_uint32(cl.size(), clf.ptr()); - return OK; -} + for (int i = 0; i < cl.size(); i++) { -EditorExportPlatformUWP::EditorExportPlatformUWP() { + CharString txt = cl[i].utf8(); + int base = clf.size(); + clf.resize(base + 4 + txt.length()); + encode_uint32(txt.length(), &clf[base]); + copymem(&clf[base + 4], txt.ptr(), txt.length()); + print_line(itos(i) + " param: " + cl[i]); + } - Image img(_uwp_logo); - logo = Ref<ImageTexture>(memnew(ImageTexture)); - logo->create_from_image(img); + packager.add_file("__cl__.cl", clf.ptr(), clf.size(), -1, -1, false); - is_debug = true; + ep.step("Adding project files...", 3); - custom_release_package = ""; - custom_debug_package = ""; + EditorNode::progress_add_task("project_files", "Project Files", 100); + packager.set_progress_task("project_files"); - arch = X86; + err = export_project_files(p_preset, save_appx_file, &packager); - display_name = ""; - short_name = "Godot"; - unique_name = "Godot.Engine"; - description = "Godot Engine"; - publisher = "CN=GodotEngine"; - publisher_display_name = "Godot Engine"; + EditorNode::progress_end_task("project_files"); - product_guid = "00000000-0000-0000-0000-000000000000"; - publisher_guid = "00000000-0000-0000-0000-000000000000"; + ep.step("Closing package...", 7); - version_major = 1; - version_minor = 0; - version_build = 0; - version_revision = 0; + unzClose(pkg); - orientation_landscape = true; - orientation_portrait = true; - orientation_landscape_flipped = true; - orientation_portrait_flipped = true; + packager.finish(); - background_color = "transparent"; + return OK; + } - name_on_square150 = false; - name_on_square310 = false; - name_on_wide = false; + virtual void get_platform_features(List<String> *r_features) { - sign_package = false; - certificate_path = ""; - certificate_pass = ""; -} + r_features->push_back("pc"); + r_features->push_back("UWP"); + } -EditorExportPlatformUWP::~EditorExportPlatformUWP() {} + EditorExportUWP() { + Ref<Image> img = memnew(Image(_uwp_logo)); + logo.instance(); + logo->create_from_image(img); + } +}; -#endif void register_uwp_exporter() { -#if 0 - Ref<EditorExportPlatformUWP> exporter = Ref<EditorExportPlatformUWP>(memnew(EditorExportPlatformUWP)); - EditorImportExport::get_singleton()->add_export_platform(exporter); -#endif + Ref<EditorExportUWP> exporter = Ref<EditorExportUWP>(memnew(EditorExportUWP)); + EditorExport::get_singleton()->add_export_platform(exporter); } diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp index 28aaf9161..fabb227bf 100644 --- a/platform/uwp/os_uwp.cpp +++ b/platform/uwp/os_uwp.cpp @@ -35,12 +35,12 @@ #include "drivers/windows/mutex_windows.h" #include "drivers/windows/rw_lock_windows.h" #include "drivers/windows/semaphore_windows.h" -#include "global_config.h" #include "io/marshalls.h" #include "main/main.h" #include "platform/windows/packet_peer_udp_winsock.h" #include "platform/windows/stream_peer_winsock.h" #include "platform/windows/tcp_server_winsock.h" +#include "project_settings.h" #include "servers/audio_server.h" #include "servers/visual/visual_server_raster.h" #include "thread_uwp.h" @@ -310,7 +310,7 @@ void OSUWP::initialize(const VideoMode &p_desired, int p_video_driver, int p_aud if (is_keep_screen_on()) display_request->RequestActive(); - set_keep_screen_on(GLOBAL_DEF("display/keep_screen_on", true)); + set_keep_screen_on(GLOBAL_DEF("display/window/keep_screen_on", true)); } void OSUWP::set_clipboard(const String &p_text) { @@ -851,9 +851,8 @@ String OSUWP::get_data_dir() const { return String(data_folder->Path->Data()).replace("\\", "/"); } -bool OSUWP::check_feature_support(const String &p_feature) { - - return VisualServer::get_singleton()->has_os_feature(p_feature); +bool OSUWP::_check_internal_feature_support(const String &p_feature) { + return p_feature == "pc" || p_feature == "s3tc"; } PowerState OSUWP::get_power_state() { diff --git a/platform/uwp/os_uwp.h b/platform/uwp/os_uwp.h index 45b8eefde..dfa21a093 100644 --- a/platform/uwp/os_uwp.h +++ b/platform/uwp/os_uwp.h @@ -236,7 +236,7 @@ public: virtual void move_window_to_foreground(); virtual String get_data_dir() const; - virtual bool check_feature_support(const String &p_feature); + virtual bool _check_internal_feature_support(const String &p_feature); void set_gl_context(ContextEGL *p_context); void screen_size_changed(); diff --git a/platform/windows/detect.py b/platform/windows/detect.py index 5e56c1db4..4d93b3f24 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -1,104 +1,7 @@ -# -# tested on | Windows native | Linux cross-compilation -# ----------------------------+-------------------+--------------------------- -# Visual C++ Build Tools 2015 | WORKS | n/a -# MSVS C++ 2010 Express | WORKS | n/a -# Mingw-w64 | WORKS | WORKS -# Mingw-w32 | WORKS | WORKS -# MinGW | WORKS | untested -# -##### -# Note about Visual C++ Build Tools : -# -# - Visual C++ Build Tools is the standalone MSVC compiler : -# http://landinghub.visualstudio.com/visual-cpp-build-tools -# -##### -# Notes about MSVS C++ : -# -# - MSVC2010-Express compiles to 32bits only. -# -##### -# Notes about Mingw-w64 and Mingw-w32 under Windows : -# -# - both can be installed using the official installer : -# http://mingw-w64.sourceforge.net/download.php#mingw-builds -# -# - if you want to compile both 32bits and 64bits, don't forget to -# run the installer twice to install them both. -# -# - install them into a path that does not contain spaces -# ( example : "C:/Mingw-w32", "C:/Mingw-w64" ) -# -# - if you want to compile faster using the "-j" option, don't forget -# to install the appropriate version of the Pywin32 python extension -# available from : http://sourceforge.net/projects/pywin32/files/ -# -# - before running scons, you must add into the environment path -# the path to the "/bin" directory of the Mingw version you want -# to use : -# -# set PATH=C:/Mingw-w32/bin;%PATH% -# -# - then, scons should be able to detect gcc. -# - Mingw-w32 only compiles 32bits. -# - Mingw-w64 only compiles 64bits. -# -# - it is possible to add them both at the same time into the PATH env, -# if you also define the MINGW32_PREFIX and MINGW64_PREFIX environment -# variables. -# For instance, you could store that set of commands into a .bat script -# that you would run just before scons : -# -# set PATH=C:\mingw-w32\bin;%PATH% -# set PATH=C:\mingw-w64\bin;%PATH% -# set MINGW32_PREFIX=C:\mingw-w32\bin\ -# set MINGW64_PREFIX=C:\mingw-w64\bin\ -# -##### -# Notes about Mingw, Mingw-w64 and Mingw-w32 under Linux : -# -# - default toolchain prefixes are : -# "i586-mingw32msvc-" for MinGW -# "i686-w64-mingw32-" for Mingw-w32 -# "x86_64-w64-mingw32-" for Mingw-w64 -# -# - if both MinGW and Mingw-w32 are installed on your system -# Mingw-w32 should take the priority over MinGW. -# -# - it is possible to manually override prefixes by defining -# the MINGW32_PREFIX and MINGW64_PREFIX environment variables. -# -##### -# Notes about Mingw under Windows : -# -# - this is the MinGW version from http://mingw.org/ -# - install it into a path that does not contain spaces -# ( example : "C:/MinGW" ) -# - several DirectX headers might be missing. You can copy them into -# the C:/MinGW/include" directory from this page : -# https://code.google.com/p/mingw-lib/source/browse/trunk/working/avcodec_to_widget_5/directx_include/ -# - before running scons, add the path to the "/bin" directory : -# set PATH=C:/MinGW/bin;%PATH% -# - scons should be able to detect gcc. -# - -##### -# TODO : -# -# - finish to cleanup this script to remove all the remains of previous hacks and workarounds -# - make it work with the Windows7 SDK that is supposed to enable 64bits compilation for MSVC2010-Express -# - confirm it works well with other Visual Studio versions. -# - update the wiki about the pywin32 extension required for the "-j" option under Windows. -# - update the wiki to document MINGW32_PREFIX and MINGW64_PREFIX -# - +import methods import os - import sys -import methods - def is_active(): return True @@ -115,7 +18,7 @@ def can_build(): if (os.getenv("VCINSTALLDIR")): return True else: - print("\nMSVC not detected, attempting Mingw.") + print("\nMSVC not detected, attempting MinGW.") mingw32 = "" mingw64 = "" if (os.getenv("MINGW32_PREFIX")): @@ -126,7 +29,7 @@ def can_build(): test = "gcc --version > NUL 2>&1" if os.system(test) != 0 and os.system(mingw32 + test) != 0 and os.system(mingw64 + test) != 0: print("- could not detect gcc.") - print("Please, make sure a path to a Mingw /bin directory is accessible into the environment PATH.\n") + print("Please, make sure a path to a MinGW /bin directory is accessible into the environment PATH.\n") return False else: print("- gcc detected.") @@ -145,7 +48,7 @@ def can_build(): if (os.getenv("MINGW64_PREFIX")): mingw64 = os.getenv("MINGW64_PREFIX") - test = "gcc --version &>/dev/null" + test = "gcc --version > /dev/null 2>&1" if (os.system(mingw + test) == 0 or os.system(mingw64 + test) == 0 or os.system(mingw32 + test) == 0): return True @@ -162,7 +65,7 @@ def get_opts(): mingw32 = "i686-w64-mingw32-" mingw64 = "x86_64-w64-mingw32-" - if os.system(mingw32 + "gcc --version &>/dev/null") != 0: + if os.system(mingw32 + "gcc --version > /dev/null 2>&1") != 0: mingw32 = mingw if (os.getenv("MINGW32_PREFIX")): @@ -172,8 +75,8 @@ def get_opts(): mingw64 = os.getenv("MINGW64_PREFIX") return [ - ('mingw_prefix', 'Mingw Prefix', mingw32), - ('mingw_prefix_64', 'Mingw Prefix 64 bits', mingw64), + ('mingw_prefix', 'MinGW Prefix', mingw32), + ('mingw_prefix_64', 'MinGW Prefix 64 bits', mingw64), ] @@ -211,55 +114,86 @@ def configure(env): # Targeted Windows version: Vista (and later) winver = "0x0600" # Windows Vista is the minimum target for windows builds - env['is_mingw'] = False - if (os.name == "nt" and os.getenv("VCINSTALLDIR")): - # build using visual studio + if (os.name == "nt" and os.getenv("VCINSTALLDIR")): # MSVC + env['ENV']['TMP'] = os.environ['TMP'] - env.Append(CPPPATH=['#platform/windows/include']) - env.Append(LIBPATH=['#platform/windows/lib']) - env.Append(CCFLAGS=['/DWINVER=%s' % winver, '/D_WIN32_WINNT=%s' % winver]) - if (env["target"] == "release"): + ## Build type + if (env["target"] == "release"): env.Append(CCFLAGS=['/O2']) env.Append(LINKFLAGS=['/SUBSYSTEM:WINDOWS']) env.Append(LINKFLAGS=['/ENTRY:mainCRTStartup']) elif (env["target"] == "release_debug"): - env.Append(CCFLAGS=['/O2', '/DDEBUG_ENABLED']) env.Append(LINKFLAGS=['/SUBSYSTEM:CONSOLE']) - elif (env["target"] == "debug_release"): + elif (env["target"] == "debug_release"): env.Append(CCFLAGS=['/Z7', '/Od']) env.Append(LINKFLAGS=['/DEBUG']) env.Append(LINKFLAGS=['/SUBSYSTEM:WINDOWS']) env.Append(LINKFLAGS=['/ENTRY:mainCRTStartup']) elif (env["target"] == "debug"): - env.Append(CCFLAGS=['/Z7', '/DDEBUG_ENABLED', '/DDEBUG_MEMORY_ENABLED', '/DD3D_DEBUG_INFO', '/Od']) env.Append(LINKFLAGS=['/SUBSYSTEM:CONSOLE']) env.Append(LINKFLAGS=['/DEBUG']) - env.Append(CCFLAGS=['/MT', '/Gd', '/GR', '/nologo']) + ## Architecture + + # Note: this detection/override code from here onward should be here instead of in SConstruct because it's platform and compiler specific (MSVC/Windows) + if (env["bits"] != "default"): + print("Error: bits argument is disabled for MSVC") + print(""" + Bits argument is not supported for MSVC compilation. Architecture depends on the Native/Cross Compile Tools Prompt/Developer Console + (or Visual Studio settings) that is being used to run SCons. As a consequence, bits argument is disabled. Run scons again without bits + argument (example: scons p=windows) and SCons will attempt to detect what MSVC compiler will be executed and inform you. + """) + sys.exit() + + # Forcing bits argument because MSVC does not have a flag to set this through SCons... it's different compilers (cl.exe's) called from the proper command prompt + # that decide the architecture that is build for. Scons can only detect the os.getenviron (because vsvarsall.bat sets a lot of stuff for cl.exe to work with) + env["bits"] = "32" + env["x86_libtheora_opt_vc"] = True + + ## Compiler configuration + + env['ENV'] = os.environ + # This detection function needs the tools env (that is env['ENV'], not SCons's env), and that is why it's this far bellow in the code + compiler_version_str = methods.detect_visual_c_compiler_version(env['ENV']) + + print("Detected MSVC compiler: " + compiler_version_str) + # If building for 64bit architecture, disable assembly optimisations for 32 bit builds (theora as of writting)... vc compiler for 64bit can not compile _asm + if(compiler_version_str == "amd64" or compiler_version_str == "x86_amd64"): + env["bits"] = "64" + env["x86_libtheora_opt_vc"] = False + print("Compiled program architecture will be a 64 bit executable (forcing bits=64).") + elif (compiler_version_str == "x86" or compiler_version_str == "amd64_x86"): + print("Compiled program architecture will be a 32 bit executable. (forcing bits=32).") + else: + print("Failed to detect MSVC compiler architecture version... Defaulting to 32bit executable settings (forcing bits=32). Compilation attempt will continue, but SCons can not detect for what architecture this build is compiled for. You should check your settings/compilation setup.") + + ## Compile flags + + env.Append(CCFLAGS=['/MT', '/Gd', '/GR', '/nologo']) env.Append(CXXFLAGS=['/TP']) env.Append(CPPFLAGS=['/DMSVC', '/GR', ]) env.Append(CCFLAGS=['/I' + os.getenv("WindowsSdkDir") + "/Include"]) + env.Append(CCFLAGS=['/DWINDOWS_ENABLED']) + env.Append(CCFLAGS=['/DOPENGL_ENABLED']) env.Append(CCFLAGS=['/DRTAUDIO_ENABLED']) - env.Append(CCFLAGS=['/DWIN32']) env.Append(CCFLAGS=['/DTYPED_METHOD_BIND']) + env.Append(CCFLAGS=['/DWIN32']) + env.Append(CCFLAGS=['/DWINVER=%s' % winver, '/D_WIN32_WINNT=%s' % winver]) + if env["bits"] == "64": + env.Append(CCFLAGS=['/D_WIN64']) - env.Append(CCFLAGS=['/DOPENGL_ENABLED']) LIBS = ['winmm', 'opengl32', 'dsound', 'kernel32', 'ole32', 'oleaut32', 'user32', 'gdi32', 'IPHLPAPI', 'Shlwapi', 'wsock32', 'Ws2_32', 'shell32', 'advapi32', 'dinput8', 'dxguid'] env.Append(LINKFLAGS=[p + env["LIBSUFFIX"] for p in LIBS]) env.Append(LIBPATH=[os.getenv("WindowsSdkDir") + "/Lib"]) - if (os.getenv("DXSDK_DIR")): - DIRECTX_PATH = os.getenv("DXSDK_DIR") - else: - DIRECTX_PATH = "C:/Program Files/Microsoft DirectX SDK (March 2009)" if (os.getenv("VCINSTALLDIR")): VC_PATH = os.getenv("VCINSTALLDIR") @@ -268,51 +202,37 @@ def configure(env): env.Append(CCFLAGS=["/I" + p for p in os.getenv("INCLUDE").split(";")]) env.Append(LIBPATH=[p for p in os.getenv("LIB").split(";")]) - env.Append(CCFLAGS=["/I" + DIRECTX_PATH + "/Include"]) - env.Append(LIBPATH=[DIRECTX_PATH + "/Lib/x86"]) - env['ENV'] = os.environ - - # This detection function needs the tools env (that is env['ENV'], not SCons's env), and that is why it's this far bellow in the code - compiler_version_str = methods.detect_visual_c_compiler_version(env['ENV']) - - # Note: this detection/override code from here onward should be here instead of in SConstruct because it's platform and compiler specific (MSVC/Windows) - if(env["bits"] != "default"): - print "Error: bits argument is disabled for MSVC" - print ("Bits argument is not supported for MSVC compilation. Architecture depends on the Native/Cross Compile Tools Prompt/Developer Console (or Visual Studio settings)" - + " that is being used to run SCons. As a consequence, bits argument is disabled. Run scons again without bits argument (example: scons p=windows) and SCons will attempt to detect what MSVC compiler" - + " will be executed and inform you.") - sys.exit() - - # Forcing bits argument because MSVC does not have a flag to set this through SCons... it's different compilers (cl.exe's) called from the proper command prompt - # that decide the architecture that is build for. Scons can only detect the os.getenviron (because vsvarsall.bat sets a lot of stuff for cl.exe to work with) - env["bits"] = "32" - env["x86_libtheora_opt_vc"] = True - - print "Detected MSVC compiler: " + compiler_version_str - # If building for 64bit architecture, disable assembly optimisations for 32 bit builds (theora as of writting)... vc compiler for 64bit can not compile _asm - if(compiler_version_str == "amd64" or compiler_version_str == "x86_amd64"): - env["bits"] = "64" - env["x86_libtheora_opt_vc"] = False - print "Compiled program architecture will be a 64 bit executable (forcing bits=64)." - elif (compiler_version_str == "x86" or compiler_version_str == "amd64_x86"): - print "Compiled program architecture will be a 32 bit executable. (forcing bits=32)." - else: - print "Failed to detect MSVC compiler architecture version... Defaulting to 32bit executable settings (forcing bits=32). Compilation attempt will continue, but SCons can not detect for what architecture this build is compiled for. You should check your settings/compilation setup." - if env["bits"] == "64": - env.Append(CCFLAGS=['/D_WIN64']) # Incremental linking fix env['BUILDERS']['ProgramOriginal'] = env['BUILDERS']['Program'] env['BUILDERS']['Program'] = methods.precious_program - else: + else: # MinGW # Workaround for MinGW. See: # http://www.scons.org/wiki/LongCmdLinesOnWin32 env.use_windows_spawn_fix() - # build using mingw - env.Append(CCFLAGS=['-DWINVER=%s' % winver, '-D_WIN32_WINNT=%s' % winver]) + ## Build type + + if (env["target"] == "release"): + env.Append(CCFLAGS=['-msse2']) + + if (env["bits"] == "64"): + env.Append(CCFLAGS=['-O3']) + else: + env.Append(CCFLAGS=['-O2']) + + env.Append(LINKFLAGS=['-Wl,--subsystem,windows']) + + elif (env["target"] == "release_debug"): + env.Append(CCFLAGS=['-O2', '-DDEBUG_ENABLED']) + + elif (env["target"] == "debug"): + env.Append(CCFLAGS=['-g', '-DDEBUG_ENABLED', '-DDEBUG_MEMORY_ENABLED']) + + ## Compiler configuration + if (os.name == "nt"): env['ENV']['TMP'] = os.environ['TMP'] # way to go scons, you can be so stupid sometimes else: @@ -332,37 +252,6 @@ def configure(env): env.Append(LINKFLAGS=['-static']) mingw_prefix = env["mingw_prefix_64"] - nulstr = "" - - if (os.name == "posix"): - nulstr = ">/dev/null" - else: - nulstr = ">nul" - - # if os.system(mingw_prefix+"gcc --version"+nulstr)!=0: - # #not really super consistent but.. - # print("Can't find Windows compiler: "+mingw_prefix) - # sys.exit(255) - - if (env["target"] == "release"): - - env.Append(CCFLAGS=['-msse2']) - - if (env["bits"] == "64"): - env.Append(CCFLAGS=['-O3']) - else: - env.Append(CCFLAGS=['-O2']) - - env.Append(LINKFLAGS=['-Wl,--subsystem,windows']) - - elif (env["target"] == "release_debug"): - - env.Append(CCFLAGS=['-O2', '-DDEBUG_ENABLED']) - - elif (env["target"] == "debug"): - - env.Append(CCFLAGS=['-g', '-DDEBUG_ENABLED', '-DDEBUG_MEMORY_ENABLED']) - env["CC"] = mingw_prefix + "gcc" env['AS'] = mingw_prefix + "as" env['CXX'] = mingw_prefix + "g++" @@ -371,29 +260,15 @@ def configure(env): env['LD'] = mingw_prefix + "g++" env["x86_libtheora_opt_gcc"] = True - #env['CC'] = "winegcc" - #env['CXX'] = "wineg++" + ## Compile flags env.Append(CCFLAGS=['-DWINDOWS_ENABLED', '-mwindows']) - env.Append(CPPFLAGS=['-DRTAUDIO_ENABLED']) env.Append(CCFLAGS=['-DOPENGL_ENABLED']) + env.Append(CCFLAGS=['-DRTAUDIO_ENABLED']) + env.Append(CCFLAGS=['-DWINVER=%s' % winver, '-D_WIN32_WINNT=%s' % winver]) env.Append(LIBS=['mingw32', 'opengl32', 'dsound', 'ole32', 'd3d9', 'winmm', 'gdi32', 'iphlpapi', 'shlwapi', 'wsock32', 'ws2_32', 'kernel32', 'oleaut32', 'dinput8', 'dxguid']) - # if (env["bits"]=="32"): - # env.Append(LIBS=['gcc_s']) - # #--with-arch=i686 - # env.Append(CPPFLAGS=['-march=i686']) - # env.Append(LINKFLAGS=['-march=i686']) - - #'d3dx9d' env.Append(CPPFLAGS=['-DMINGW_ENABLED']) - # env.Append(LINKFLAGS=['-g']) # resrc - env['is_mingw'] = True env.Append(BUILDERS={'RES': env.Builder(action=build_res_file, suffix='.o', src_suffix='.rc')}) - - env.Append(BUILDERS={'GLSL120': env.Builder(action=methods.build_legacygl_headers, suffix='glsl.h', src_suffix='.glsl')}) - env.Append(BUILDERS={'GLSL': env.Builder(action=methods.build_glsl_headers, suffix='glsl.h', src_suffix='.glsl')}) - env.Append(BUILDERS={'HLSL9': env.Builder(action=methods.build_hlsl_dx9_headers, suffix='hlsl.h', src_suffix='.hlsl')}) - env.Append(BUILDERS={'GLSL120GLES': env.Builder(action=methods.build_gles2_headers, suffix='glsl.h', src_suffix='.glsl')}) diff --git a/platform/windows/export/export.cpp b/platform/windows/export/export.cpp index c9271f626..6cb33e2ff 100644 --- a/platform/windows/export/export.cpp +++ b/platform/windows/export/export.cpp @@ -46,6 +46,7 @@ void register_windows_exporter() { platform->set_debug_32("windows_32_debug.exe"); platform->set_release_64("windows_64_release.exe"); platform->set_debug_64("windows_64_debug.exe"); + platform->set_os_name("Windows"); EditorExport::get_singleton()->add_export_platform(platform); } diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index e94529dd9..da14d5c28 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -37,12 +37,12 @@ #include "drivers/windows/rw_lock_windows.h" #include "drivers/windows/semaphore_windows.h" #include "drivers/windows/thread_windows.h" -#include "global_config.h" #include "io/marshalls.h" #include "joypad.h" #include "lang_table.h" #include "main/main.h" #include "packet_peer_udp_winsock.h" +#include "project_settings.h" #include "servers/audio_server.h" #include "servers/visual/visual_server_raster.h" #include "servers/visual/visual_server_wrap_mt.h" @@ -805,7 +805,12 @@ void OS_Windows::process_key_events() { k->set_pressed(ke.uMsg == WM_KEYDOWN); - k->set_scancode(KeyMappingWindows::get_keysym(ke.wParam)); + if ((ke.lParam & (1 << 24)) && (ke.wParam == VK_RETURN)) { + // Special case for Numpad Enter key + k->set_scancode(KEY_ENTER); + } else { + k->set_scancode(KeyMappingWindows::get_keysym(ke.wParam)); + } if (i + 1 < key_event_pos && key_event_buffer[i + 1].uMsg == WM_CHAR) { k->set_unicode(key_event_buffer[i + 1].wParam); @@ -1410,28 +1415,21 @@ void OS_Windows::set_window_size(const Size2 p_size) { return; } - RECT crect; - GetClientRect(hWnd, &crect); + int w = p_size.width; + int h = p_size.height; RECT rect; GetWindowRect(hWnd, &rect); - int dx = (rect.right - rect.left) - (crect.right - crect.left); - int dy = (rect.bottom - rect.top) - (crect.bottom - crect.top); - - rect.right = rect.left + p_size.width + dx; - rect.bottom = rect.top + p_size.height + dy; - //print_line("PRE: "+itos(rect.left)+","+itos(rect.top)+","+itos(rect.right-rect.left)+","+itos(rect.bottom-rect.top)); + if (video_mode.borderless_window == false) { + RECT crect; + GetClientRect(hWnd, &crect); - /*if (video_mode.resizable) { - AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE); - } else { - AdjustWindowRect(&rect, WS_CAPTION | WS_POPUPWINDOW, FALSE); - }*/ - - //print_line("POST: "+itos(rect.left)+","+itos(rect.top)+","+itos(rect.right-rect.left)+","+itos(rect.bottom-rect.top)); + w += (rect.right - rect.left) - (crect.right - crect.left); + h += (rect.bottom - rect.top) - (crect.bottom - crect.top); + } - MoveWindow(hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE); + MoveWindow(hWnd, rect.left, rect.top, w, h, TRUE); } void OS_Windows::set_window_fullscreen(bool p_enabled) { @@ -1451,21 +1449,18 @@ void OS_Windows::set_window_fullscreen(bool p_enabled) { Point2 pos = get_screen_position(cs); Size2 size = get_screen_size(cs); - /* r.left = pos.x; - r.top = pos.y; - r.bottom = pos.y+size.y; - r.right = pos.x+size.x; -*/ - SetWindowLongPtr(hWnd, GWL_STYLE, - WS_SYSMENU | WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE); - MoveWindow(hWnd, pos.x, pos.y, size.width, size.height, TRUE); - video_mode.fullscreen = true; + _update_window_style(false); + + MoveWindow(hWnd, pos.x, pos.y, size.width, size.height, TRUE); + } else { RECT rect; + video_mode.fullscreen = false; + if (pre_fs_valid) { rect = pre_fs_rect; } else { @@ -1475,35 +1470,12 @@ void OS_Windows::set_window_fullscreen(bool p_enabled) { rect.bottom = video_mode.height; } - if (video_mode.resizable) { - - SetWindowLongPtr(hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW | WS_VISIBLE); - //AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE); - MoveWindow(hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE); - } else { + _update_window_style(false); - SetWindowLongPtr(hWnd, GWL_STYLE, WS_CAPTION | WS_POPUPWINDOW | WS_VISIBLE); - //AdjustWindowRect(&rect, WS_CAPTION | WS_POPUPWINDOW, FALSE); - MoveWindow(hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE); - } + MoveWindow(hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE); - video_mode.fullscreen = false; pre_fs_valid = true; - /* - DWORD dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; - DWORD dwStyle=WS_OVERLAPPEDWINDOW; - if (!video_mode.resizable) { - dwStyle &= ~WS_THICKFRAME; - dwStyle &= ~WS_MAXIMIZEBOX; - } - AdjustWindowRectEx(&pre_fs_rect, dwStyle, FALSE, dwExStyle); - video_mode.fullscreen=false; - video_mode.width=pre_fs_rect.right-pre_fs_rect.left; - video_mode.height=pre_fs_rect.bottom-pre_fs_rect.top; -*/ } - - //MoveWindow(hWnd,r.left,r.top,p_size.x,p_size.y,TRUE); } bool OS_Windows::is_window_fullscreen() const { @@ -1513,30 +1485,10 @@ void OS_Windows::set_window_resizable(bool p_enabled) { if (video_mode.resizable == p_enabled) return; - /* - GetWindowRect(hWnd,&pre_fs_rect); - DWORD dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; - DWORD dwStyle=WS_OVERLAPPEDWINDOW; - if (!p_enabled) { - dwStyle &= ~WS_THICKFRAME; - dwStyle &= ~WS_MAXIMIZEBOX; - } - AdjustWindowRectEx(&pre_fs_rect, dwStyle, FALSE, dwExStyle); - */ - - if (!video_mode.fullscreen) { - if (p_enabled) { - SetWindowLongPtr(hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW | WS_VISIBLE); - } else { - SetWindowLongPtr(hWnd, GWL_STYLE, WS_CAPTION | WS_MINIMIZEBOX | WS_POPUPWINDOW | WS_VISIBLE); - } - - RECT rect; - GetWindowRect(hWnd, &rect); - MoveWindow(hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE); - } video_mode.resizable = p_enabled; + + _update_window_style(); } bool OS_Windows::is_window_resizable() const { @@ -1576,13 +1528,36 @@ bool OS_Windows::is_window_maximized() const { } void OS_Windows::set_borderless_window(int p_borderless) { + if (video_mode.borderless_window == p_borderless) + return; + video_mode.borderless_window = p_borderless; + + _update_window_style(); } bool OS_Windows::get_borderless_window() { return video_mode.borderless_window; } +void OS_Windows::_update_window_style(bool repaint) { + if (video_mode.fullscreen || video_mode.borderless_window) { + SetWindowLongPtr(hWnd, GWL_STYLE, WS_SYSMENU | WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE); + } else { + if (video_mode.resizable) { + SetWindowLongPtr(hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW | WS_VISIBLE); + } else { + SetWindowLongPtr(hWnd, GWL_STYLE, WS_CAPTION | WS_MINIMIZEBOX | WS_POPUPWINDOW | WS_VISIBLE); + } + } + + if (repaint) { + RECT rect; + GetWindowRect(hWnd, &rect); + MoveWindow(hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE); + } +} + Error OS_Windows::open_dynamic_library(const String p_path, void *&p_library_handle) { p_library_handle = (void *)LoadLibrary(p_path.utf8().get_data()); if (!p_library_handle) { @@ -1599,12 +1574,16 @@ Error OS_Windows::close_dynamic_library(void *p_library_handle) { return OK; } -Error OS_Windows::get_dynamic_library_symbol_handle(void *p_library_handle, const String p_name, void *&p_symbol_handle) { +Error OS_Windows::get_dynamic_library_symbol_handle(void *p_library_handle, const String p_name, void *&p_symbol_handle, bool p_optional) { char *error; p_symbol_handle = (void *)GetProcAddress((HMODULE)p_library_handle, p_name.utf8().get_data()); if (!p_symbol_handle) { - ERR_EXPLAIN("Can't resolve symbol " + p_name + ". Error: " + String::num(GetLastError())); - ERR_FAIL_V(ERR_CANT_RESOLVE); + if (!p_optional) { + ERR_EXPLAIN("Can't resolve symbol " + p_name + ". Error: " + String::num(GetLastError())); + ERR_FAIL_V(ERR_CANT_RESOLVE); + } else { + return ERR_CANT_RESOLVE; + } } return OK; } @@ -2259,7 +2238,7 @@ String OS_Windows::get_data_dir() const { if (has_environment("APPDATA")) { - bool use_godot = GlobalConfig::get_singleton()->get("application/use_shared_user_dir"); + bool use_godot = ProjectSettings::get_singleton()->get("application/config/use_shared_user_dir"); if (!use_godot) return (OS::get_singleton()->get_environment("APPDATA") + "/" + an).replace("\\", "/"); else @@ -2267,7 +2246,7 @@ String OS_Windows::get_data_dir() const { } } - return GlobalConfig::get_singleton()->get_resource_path(); + return ProjectSettings::get_singleton()->get_resource_path(); } bool OS_Windows::is_joy_known(int p_device) { @@ -2304,9 +2283,9 @@ int OS_Windows::get_power_percent_left() { return power_manager->get_power_percent_left(); } -bool OS_Windows::check_feature_support(const String &p_feature) { +bool OS_Windows::_check_internal_feature_support(const String &p_feature) { - return VisualServer::get_singleton()->has_os_feature(p_feature); + return p_feature == "pc" || p_feature == "s3tc"; } OS_Windows::OS_Windows(HINSTANCE _hInstance) { diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index 6cbdd5883..6856e7e9b 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -133,6 +133,8 @@ class OS_Windows : public OS { 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); + void _update_window_style(bool repaint = true); + // functions used by main to initialize/deintialize the OS protected: virtual int get_video_driver_count() const; @@ -225,7 +227,7 @@ public: virtual Error open_dynamic_library(const String p_path, void *&p_library_handle); virtual Error close_dynamic_library(void *p_library_handle); - virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String p_name, void *&p_symbol_handle); + virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String p_name, void *&p_symbol_handle, bool p_optional = false); virtual MainLoop *get_main_loop() const; @@ -286,7 +288,7 @@ public: virtual int get_power_seconds_left(); virtual int get_power_percent_left(); - virtual bool check_feature_support(const String &p_feature); + virtual bool _check_internal_feature_support(const String &p_feature); OS_Windows(HINSTANCE _hInstance); ~OS_Windows(); diff --git a/platform/x11/detect.py b/platform/x11/detect.py index 0ba0f6839..79778136a 100644 --- a/platform/x11/detect.py +++ b/platform/x11/detect.py @@ -1,7 +1,6 @@ - import os -import sys import platform +import sys def is_active(): @@ -14,15 +13,12 @@ def get_name(): def can_build(): - if (os.name != "posix"): + if (os.name != "posix" or sys.platform == "darwin"): return False - if sys.platform == "darwin": - return False # no x11 on mac for now - - errorval = os.system("pkg-config --version > /dev/null") - - if (errorval): + # Check the minimal dependencies + x11_error = os.system("pkg-config --version > /dev/null") + if (x11_error): print("pkg-config not found.. x11 disabled.") return False @@ -31,11 +27,6 @@ def can_build(): print("X11 not found.. x11 disabled.") return False - ssl_error = os.system("pkg-config openssl --modversion > /dev/null ") - if (ssl_error): - print("OpenSSL not found.. x11 disabled.") - return False - x11_error = os.system("pkg-config xcursor --modversion > /dev/null ") if (x11_error): print("xcursor not found.. x11 disabled.") @@ -51,18 +42,18 @@ def can_build(): print("xrandr not found.. x11 disabled.") return False - return True # X11 enabled + return True def get_opts(): return [ - ('use_llvm', 'Use llvm compiler', 'no'), - ('use_static_cpp', 'link stdc++ statically', 'no'), - ('use_sanitizer', 'Use llvm compiler sanitize address', 'no'), - ('use_leak_sanitizer', 'Use llvm compiler sanitize memory leaks', 'no'), + ('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'), + ('pulseaudio', 'Detect & use pulseaudio', 'yes'), ('udev', 'Use udev for gamepad connection callbacks', 'no'), ('debug_release', 'Add debug symbols to release version', 'no'), ] @@ -80,66 +71,62 @@ def get_flags(): def configure(env): - is64 = sys.maxsize > 2**32 + ## Build type + + if (env["target"] == "release"): + env.Prepend(CCFLAGS=['-Ofast']) + if (env["debug_release"] == "yes"): + env.Prepend(CCFLAGS=['-g2']) + elif (env["target"] == "release_debug"): + env.Prepend(CCFLAGS=['-O2', '-ffast-math', '-DDEBUG_ENABLED']) + if (env["debug_release"] == "yes"): + env.Prepend(CCFLAGS=['-g2']) + + elif (env["target"] == "debug"): + env.Prepend(CCFLAGS=['-g2', '-DDEBUG_ENABLED', '-DDEBUG_MEMORY_ENABLED']) + + ## Architecture + + is64 = sys.maxsize > 2**32 if (env["bits"] == "default"): - if (is64): - env["bits"] = "64" - else: - env["bits"] = "32" + env["bits"] = "64" if is64 else "32" + + ## Compiler configuration - env.Append(CPPPATH=['#platform/x11']) if (env["use_llvm"] == "yes"): - if 'clang++' not in env['CXX']: + if ('clang++' not in env['CXX']): env["CC"] = "clang" env["CXX"] = "clang++" env["LD"] = "clang++" env.Append(CPPFLAGS=['-DTYPED_METHOD_BIND']) - env.extra_suffix = ".llvm" - - if (env["use_sanitizer"] == "yes"): - env.Append(CCFLAGS=['-fsanitize=address', '-fno-omit-frame-pointer']) - env.Append(LINKFLAGS=['-fsanitize=address']) - env.extra_suffix += "s" + env.extra_suffix = ".llvm" + env.extra_suffix - if (env["use_leak_sanitizer"] == "yes"): + # leak sanitizer requires (address) sanitizer + if (env["use_sanitizer"] == "yes" or env["use_leak_sanitizer"] == "yes"): env.Append(CCFLAGS=['-fsanitize=address', '-fno-omit-frame-pointer']) env.Append(LINKFLAGS=['-fsanitize=address']) env.extra_suffix += "s" - - # if (env["tools"]=="no"): - # #no tools suffix - # env['OBJSUFFIX'] = ".nt"+env['OBJSUFFIX'] - # env['LIBSUFFIX'] = ".nt"+env['LIBSUFFIX'] + if (env["use_leak_sanitizer"] == "yes"): + env.Append(CCFLAGS=['-fsanitize=leak']) + env.Append(LINKFLAGS=['-fsanitize=leak']) if (env["use_lto"] == "yes"): env.Append(CCFLAGS=['-flto']) env.Append(LINKFLAGS=['-flto']) - env.Append(CCFLAGS=['-pipe']) env.Append(LINKFLAGS=['-pipe']) - if (env["target"] == "release"): - env.Prepend(CCFLAGS=['-Ofast']) - if (env["debug_release"] == "yes"): - env.Prepend(CCFLAGS=['-g2']) - - elif (env["target"] == "release_debug"): - - env.Prepend(CCFLAGS=['-O2', '-ffast-math', '-DDEBUG_ENABLED']) - if (env["debug_release"] == "yes"): - env.Prepend(CCFLAGS=['-g2']) - - elif (env["target"] == "debug"): - - env.Prepend(CCFLAGS=['-g2', '-DDEBUG_ENABLED', '-DDEBUG_MEMORY_ENABLED']) + ## Dependencies env.ParseConfig('pkg-config x11 --cflags --libs') - env.ParseConfig('pkg-config xinerama --cflags --libs') env.ParseConfig('pkg-config xcursor --cflags --libs') + env.ParseConfig('pkg-config xinerama --cflags --libs') env.ParseConfig('pkg-config xrandr --cflags --libs') + # 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 @@ -196,47 +183,51 @@ def configure(env): if (env['builtin_libogg'] == 'no'): env.ParseConfig('pkg-config ogg --cflags --libs') - env.Append(CPPFLAGS=['-DOPENGL_ENABLED']) + if (env['builtin_libtheora'] != 'no'): + 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 - if os.system("pkg-config --exists alsa") == 0: + ## Flags + + if (os.system("pkg-config --exists alsa") == 0): # 0 means found print("Enabling ALSA") env.Append(CPPFLAGS=["-DALSA_ENABLED"]) env.ParseConfig('pkg-config alsa --cflags --libs') else: print("ALSA libraries not found, disabling driver") - if (platform.system() == "Linux"): - env.Append(CPPFLAGS=["-DJOYDEV_ENABLED"]) - if (env["udev"] == "yes"): - # pkg-config returns 0 when the lib exists... - found_udev = not os.system("pkg-config --exists libudev") - - if (found_udev): - print("Enabling udev support") - env.Append(CPPFLAGS=["-DUDEV_ENABLED"]) - env.ParseConfig('pkg-config libudev --cflags --libs') - else: - print("libudev development libraries not found, disabling udev support") - if (env["pulseaudio"] == "yes"): - if not os.system("pkg-config --exists libpulse-simple"): + if (os.system("pkg-config --exists libpulse-simple") == 0): # 0 means found print("Enabling PulseAudio") env.Append(CPPFLAGS=["-DPULSEAUDIO_ENABLED"]) env.ParseConfig('pkg-config --cflags --libs libpulse-simple') else: print("PulseAudio development libraries not found, disabling driver") + if (platform.system() == "Linux"): + env.Append(CPPFLAGS=["-DJOYDEV_ENABLED"]) + + if (env["udev"] == "yes"): + if (os.system("pkg-config --exists libudev") == 0): # 0 means found + print("Enabling udev support") + env.Append(CPPFLAGS=["-DUDEV_ENABLED"]) + env.ParseConfig('pkg-config libudev --cflags --libs') + else: + print("libudev development libraries not found, disabling udev support") + + # Linkflags below this line should typically stay the last ones if (env['builtin_zlib'] == 'no'): env.ParseConfig('pkg-config zlib --cflags --libs') - env.Append(CPPFLAGS=['-DX11_ENABLED', '-DUNIX_ENABLED', '-DGLES2_ENABLED', '-DGLES_OVER_GL']) + env.Append(CPPPATH=['#platform/x11']) + env.Append(CPPFLAGS=['-DX11_ENABLED', '-DUNIX_ENABLED', '-DOPENGL_ENABLED', '-DGLES2_ENABLED', '-DGLES_OVER_GL']) env.Append(LIBS=['GL', 'pthread']) if (platform.system() == "Linux"): env.Append(LIBS=['dl']) - # env.Append(CPPFLAGS=['-DMPC_FIXED_POINT']) - # host compiler is default.. + ## Cross-compilation if (is64 and env["bits"] == "32"): env.Append(CPPFLAGS=['-m32']) @@ -245,17 +236,5 @@ def configure(env): env.Append(CPPFLAGS=['-m64']) env.Append(LINKFLAGS=['-m64', '-L/usr/lib/i686-linux-gnu']) - import methods - - # FIXME: Commented out when moving to gles3 - #env.Append(BUILDERS={'GLSL120': env.Builder(action=methods.build_legacygl_headers, suffix='glsl.h', src_suffix='.glsl')}) - #env.Append(BUILDERS={'GLSL': env.Builder(action=methods.build_glsl_headers, suffix='glsl.h', src_suffix='.glsl')}) - #env.Append(BUILDERS={'GLSL120GLES': env.Builder(action=methods.build_gles2_headers, suffix='glsl.h', src_suffix='.glsl')}) - #env.Append( BUILDERS = { 'HLSL9' : env.Builder(action = methods.build_hlsl_dx9_headers, suffix = 'hlsl.h',src_suffix = '.hlsl') } ) - if (env["use_static_cpp"] == "yes"): env.Append(LINKFLAGS=['-static-libstdc++']) - - 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 diff --git a/platform/x11/export/export.cpp b/platform/x11/export/export.cpp index 69784a473..c8d6220ae 100644 --- a/platform/x11/export/export.cpp +++ b/platform/x11/export/export.cpp @@ -48,6 +48,7 @@ void register_x11_exporter() { platform->set_debug_32("linux_x11_32_debug"); platform->set_release_64("linux_x11_64_release"); platform->set_debug_64("linux_x11_64_debug"); + platform->set_os_name("X11"); EditorExport::get_singleton()->add_export_platform(platform); diff --git a/platform/x11/godot_x11.cpp b/platform/x11/godot_x11.cpp index b293b1beb..6f418b213 100644 --- a/platform/x11/godot_x11.cpp +++ b/platform/x11/godot_x11.cpp @@ -28,6 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include <limits.h> +#include <locale.h> #include <stdlib.h> #include <unistd.h> @@ -38,6 +39,8 @@ int main(int argc, char *argv[]) { OS_X11 os; + setlocale(LC_CTYPE, ""); + char *cwd = (char *)malloc(PATH_MAX); getcwd(cwd, PATH_MAX); diff --git a/platform/x11/key_mapping_x11.cpp b/platform/x11/key_mapping_x11.cpp index 362fc3618..1d7eb1692 100644 --- a/platform/x11/key_mapping_x11.cpp +++ b/platform/x11/key_mapping_x11.cpp @@ -94,7 +94,6 @@ static _XTranslatePair _xkeysym_to_keycode[] = { //{ XK_KP_Separator, KEY_COMMA }, { XK_KP_Decimal, KEY_KP_PERIOD }, { XK_KP_Delete, KEY_KP_PERIOD }, - { XK_KP_Enter, KEY_KP_ENTER }, { XK_KP_Multiply, KEY_KP_MULTIPLY }, { XK_KP_Divide, KEY_KP_DIVIDE }, { XK_KP_Subtract, KEY_KP_SUBTRACT }, diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index 2eebc96d2..1dde328ed 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -68,6 +68,8 @@ #undef CursorShape +#include <X11/XKBlib.h> + int OS_X11::get_video_driver_count() const { return 1; } @@ -93,6 +95,7 @@ const char *OS_X11::get_audio_driver_name(int p_driver) const { void OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { + long im_event_mask = 0; last_button_state = 0; xmbstring = NULL; @@ -113,7 +116,32 @@ void OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_au /** XLIB INITIALIZATION **/ x11_display = XOpenDisplay(NULL); - char *modifiers = XSetLocaleModifiers("@im=none"); + Bool xkb_dar = False; + if (x11_display) { + XAutoRepeatOn(x11_display); + xkb_dar = XkbSetDetectableAutoRepeat(x11_display, True, NULL); + } + + char *modifiers = NULL; + + // Try to support IME if detectable auto-repeat is supported + + if (xkb_dar == True) { + +// Xutf8LookupString will be used later instead of XmbLookupString before +// the multibyte sequences can be converted to unicode string. + +#ifdef X_HAVE_UTF8_STRING + modifiers = XSetLocaleModifiers(""); +#endif + } + + if (modifiers == NULL) { + if (is_stdout_verbose()) { + WARN_PRINT("IME is disabled"); + } + modifiers = XSetLocaleModifiers("@im=none"); + } if (modifiers == NULL) { WARN_PRINT("Error setting locale modifiers"); } @@ -153,6 +181,14 @@ void OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_au WARN_PRINT("XOpenIM failed"); xim_style = 0L; } else { + ::XIMCallback im_destroy_callback; + im_destroy_callback.client_data = (::XPointer)(this); + im_destroy_callback.callback = (::XIMProc)(xim_destroy_callback); + if (XSetIMValues(xim, XNDestroyCallback, &im_destroy_callback, + NULL) != NULL) { + WARN_PRINT("Error setting XIM destroy callback"); + } + ::XIMStyles *xim_styles = NULL; xim_style = 0L; char *imvalret = NULL; @@ -242,6 +278,13 @@ void OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_au xev.xclient.data.l[2] = 0; XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureNotifyMask, &xev); + } else if (current_videomode.borderless_window) { + Hints hints; + Atom property; + hints.flags = 2; + hints.decorations = 0; + property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True); + XChangeProperty(x11_display, x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5); } // disable resizable window @@ -303,7 +346,8 @@ void OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_au StructureNotifyMask | SubstructureNotifyMask | SubstructureRedirectMask | FocusChangeMask | PropertyChangeMask | - ColormapChangeMask | OwnerGrabButtonMask; + ColormapChangeMask | OwnerGrabButtonMask | + im_event_mask; XChangeWindowAttributes(x11_display, x11_window, CWEventMask, &new_attr); @@ -327,6 +371,16 @@ void OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_au if (xim && xim_style) { xic = XCreateIC(xim, XNInputStyle, xim_style, XNClientWindow, x11_window, XNFocusWindow, x11_window, (char *)NULL); + if (XGetICValues(xic, XNFilterEvents, &im_event_mask, NULL) != NULL) { + WARN_PRINT("XGetICValues couldn't obtain XNFilterEvents value"); + XDestroyIC(xic); + xic = NULL; + } + if (xic) { + XSetICFocus(xic); + } else { + WARN_PRINT("XCreateIC couldn't create xic"); + } } else { xic = NULL; @@ -443,6 +497,30 @@ void OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_au joypad = memnew(JoypadLinux(input)); #endif _ensure_data_dir(); + + power_manager = memnew(PowerX11); +} + +void OS_X11::xim_destroy_callback(::XIM im, ::XPointer client_data, + ::XPointer call_data) { + + WARN_PRINT("Input method stopped"); + OS_X11 *os = reinterpret_cast<OS_X11 *>(client_data); + os->xim = NULL; + os->xic = NULL; +} + +void OS_X11::set_ime_position(const Point2 &p_pos) { + + if (!xic) + return; + + ::XPoint spot; + spot.x = short(p_pos.x); + spot.y = short(p_pos.y); + XVaNestedList preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &spot, NULL); + XSetICValues(xic, XNPreeditAttributes, preedit_attr, NULL); + XFree(preedit_attr); } void OS_X11::finalize() { @@ -492,8 +570,12 @@ void OS_X11::finalize() { XcursorImageDestroy(img[i]); }; - XDestroyIC(xic); - XCloseIM(xim); + if (xic) { + XDestroyIC(xic); + } + if (xim) { + XCloseIM(xim); + } XCloseDisplay(x11_display); if (xmbstring) @@ -606,6 +688,16 @@ void OS_X11::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) con } void OS_X11::set_wm_fullscreen(bool p_enabled) { + if (p_enabled && !is_window_resizable()) { + // Set the window as resizable to prevent window managers to ignore the fullscreen state flag. + XSizeHints *xsh; + + xsh = XAllocSizeHints(); + xsh->flags = 0L; + XSetWMNormalHints(x11_display, x11_window, xsh); + XFree(xsh); + } + // Using EWMH -- Extened Window Manager Hints XEvent xev; Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False); @@ -621,6 +713,23 @@ 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); + XFlush(x11_display); + + if (!p_enabled && !is_window_resizable()) { + // Reset the non-resizable flags if we un-set these before. + Size2 size = get_window_size(); + XSizeHints *xsh; + + xsh = XAllocSizeHints(); + xsh->flags = PMinSize | PMaxSize; + xsh->min_width = size.x; + xsh->max_width = size.x; + xsh->min_height = size.y; + xsh->max_height = size.y; + + XSetWMNormalHints(x11_display, x11_window, xsh); + XFree(xsh); + } } int OS_X11::get_screen_count() const { @@ -940,6 +1049,25 @@ bool OS_X11::is_window_maximized() const { return false; } +void OS_X11::set_borderless_window(int p_borderless) { + + if (current_videomode.borderless_window == p_borderless) + return; + + current_videomode.borderless_window = p_borderless; + + Hints hints; + Atom property; + hints.flags = 2; + hints.decorations = current_videomode.borderless_window ? 0 : 1; + property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True); + XChangeProperty(x11_display, x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5); +} + +bool OS_X11::get_borderless_window() { + return current_videomode.borderless_window; +} + void OS_X11::request_attention() { // Using EWMH -- Extended Window Manager Hints // @@ -1041,9 +1169,61 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) { xmblen = 8; } + keysym_unicode = keysym_keycode; + if (xkeyevent->type == KeyPress && xic) { Status status; +#ifdef X_HAVE_UTF8_STRING + int utf8len = 8; + char *utf8string = (char *)memalloc(sizeof(char) * utf8len); + int utf8bytes = Xutf8LookupString(xic, xkeyevent, utf8string, + utf8len - 1, &keysym_unicode, &status); + if (status == XBufferOverflow) { + utf8len = utf8bytes + 1; + utf8string = (char *)memrealloc(utf8string, utf8len); + utf8bytes = Xutf8LookupString(xic, xkeyevent, utf8string, + utf8len - 1, &keysym_unicode, &status); + } + utf8string[utf8bytes] = '\0'; + + if (status == XLookupChars) { + bool keypress = xkeyevent->type == KeyPress; + unsigned int keycode = KeyMappingX11::get_keycode(keysym_keycode); + if (keycode >= 'a' && keycode <= 'z') + keycode -= 'a' - 'A'; + + String tmp; + tmp.parse_utf8(utf8string, utf8bytes); + for (int i = 0; i < tmp.length(); i++) { + Ref<InputEventKey> k; + k.instance(); + if (keycode == 0 && tmp[i] == 0) { + continue; + } + + get_key_modifier_state(xkeyevent->state, k); + + k->set_unicode(tmp[i]); + + k->set_pressed(keypress); + + k->set_scancode(keycode); + + k->set_echo(false); + + if (k->get_scancode() == KEY_BACKTAB) { + //make it consistent across platforms. + k->set_scancode(KEY_TAB); + k->set_shift(true); + } + + input->parse_input_event(k); + } + return; + } + memfree(utf8string); +#else do { int mnbytes = XmbLookupString(xic, xkeyevent, xmbstring, xmblen - 1, &keysym_unicode, &status); @@ -1054,6 +1234,7 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) { xmbstring = (char *)memrealloc(xmbstring, xmblen); } } while (status == XBufferOverflow); +#endif } /* Phase 2, obtain a pigui keycode from the keysym */ @@ -1082,11 +1263,6 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) { bool keypress = xkeyevent->type == KeyPress; - if (xkeyevent->type == KeyPress && xic) { - if (XFilterEvent((XEvent *)xkeyevent, x11_window)) - return; - } - if (keycode == 0 && unicode == 0) return; @@ -1108,17 +1284,19 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) { // Echo characters in X11 are a keyrelease and a keypress // one after the other with the (almot) same timestamp. // To detect them, i use XPeekEvent and check that their - // difference in time is below a treshold. + // difference in time is below a threshold. if (xkeyevent->type != KeyPress) { + p_echo = false; + // make sure there are events pending, // so this call won't block. if (XPending(x11_display) > 0) { XEvent peek_event; XPeekEvent(x11_display, &peek_event); - // I'm using a treshold of 5 msecs, + // I'm using a threshold of 5 msecs, // since sometimes there seems to be a little // jitter. I'm still not convinced that all this approach // is correct, but the xorg developers are @@ -1172,6 +1350,18 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) { k->set_metakey(false); } + bool last_is_pressed = Input::get_singleton()->is_key_pressed(k->get_scancode()); + if (k->is_pressed()) { + if (last_is_pressed) { + k->set_echo(true); + } + } else { + //ignore + if (last_is_pressed == false) { + return; + } + } + //printf("key: %x\n",k->get_scancode()); input->parse_input_event(k); } @@ -1253,6 +1443,10 @@ void OS_X11::process_xevents() { XEvent event; XNextEvent(x11_display, &event); + if (XFilterEvent(&event, None)) { + continue; + } + switch (event.type) { case Expose: Main::force_redraw(); @@ -1295,6 +1489,9 @@ void OS_X11::process_xevents() { ButtonPressMask | ButtonReleaseMask | PointerMotionMask, GrabModeAsync, GrabModeAsync, x11_window, None, CurrentTime); } + if (xic) { + XSetICFocus(xic); + } break; case FocusOut: @@ -1308,9 +1505,16 @@ void OS_X11::process_xevents() { } XUngrabPointer(x11_display, CurrentTime); } + if (xic) { + XUnsetICFocus(xic); + } break; case ConfigureNotify: + if (xic) { + // Not portable. + set_ime_position(Point2(0, 1)); + } /* call resizeGLScene only if our window-size changed */ if ((event.xconfigure.width == current_videomode.width) && @@ -1656,7 +1860,7 @@ void OS_X11::set_clipboard(const String &p_text) { XSetSelectionOwner(x11_display, XInternAtom(x11_display, "CLIPBOARD", 0), x11_window, CurrentTime); }; -static String _get_clipboard(Atom p_source, Window x11_window, ::Display *x11_display, String p_internal_clipboard) { +static String _get_clipboard_impl(Atom p_source, Window x11_window, ::Display *x11_display, String p_internal_clipboard, Atom target) { String ret; @@ -1673,7 +1877,7 @@ static String _get_clipboard(Atom p_source, Window x11_window, ::Display *x11_di }; if (Sown != None) { - XConvertSelection(x11_display, p_source, XA_STRING, selection, + XConvertSelection(x11_display, p_source, target, selection, x11_window, CurrentTime); XFlush(x11_display); while (true) { @@ -1713,6 +1917,18 @@ static String _get_clipboard(Atom p_source, Window x11_window, ::Display *x11_di return ret; } +static String _get_clipboard(Atom p_source, Window x11_window, ::Display *x11_display, String p_internal_clipboard) { + String ret; + Atom utf8_atom = XInternAtom(x11_display, "UTF8_STRING", True); + if (utf8_atom != None) { + ret = _get_clipboard_impl(p_source, x11_window, x11_display, p_internal_clipboard, utf8_atom); + } + if (ret == "") { + ret = _get_clipboard_impl(p_source, x11_window, x11_display, p_internal_clipboard, XA_STRING); + } + return ret; +} + String OS_X11::get_clipboard() const { String ret; @@ -1745,6 +1961,11 @@ Error OS_X11::shell_open(String p_uri) { return ok; } +bool OS_X11::_check_internal_feature_support(const String &p_feature) { + + return p_feature == "pc" || p_feature == "s3tc"; +} + String OS_X11::get_system_dir(SystemDir p_dir) const { String xdgparam; diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h index d62186e5b..db70f8f84 100644 --- a/platform/x11/os_x11.h +++ b/platform/x11/os_x11.h @@ -113,6 +113,9 @@ class OS_X11 : public OS_Unix { ::XIC xic; ::XIM xim; ::XIMStyle xim_style; + static void xim_destroy_callback(::XIM im, ::XPointer client_data, + ::XPointer call_data); + Point2i last_mouse_pos; bool last_mouse_pos_valid; Point2i last_click_pos; @@ -247,6 +250,10 @@ public: virtual bool is_window_maximized() const; virtual void request_attention(); + virtual void set_borderless_window(int p_borderless); + virtual bool get_borderless_window(); + virtual void set_ime_position(const Point2 &p_pos); + virtual void move_window_to_foreground(); virtual void alert(const String &p_alert, const String &p_title = "ALERT!"); @@ -262,6 +269,8 @@ public: virtual int get_power_seconds_left(); virtual int get_power_percent_left(); + virtual bool _check_internal_feature_support(const String &p_feature); + void run(); OS_X11(); diff --git a/platform/x11/power_x11.cpp b/platform/x11/power_x11.cpp index 093d24f40..8e69a2223 100644 --- a/platform/x11/power_x11.cpp +++ b/platform/x11/power_x11.cpp @@ -171,25 +171,18 @@ void PowerX11::check_proc_acpi_battery(const char *node, bool *have_battery, boo charge = true; } } else if (String(key) == "remaining capacity") { - char *endptr = NULL; - //const int cvt = (int) strtol(val, &endptr, 10); String sval = val; const int cvt = sval.to_int(); - if (*endptr == ' ') { - remaining = cvt; - } + remaining = cvt; } } ptr = &info[0]; while (make_proc_acpi_key_val(&ptr, &key, &val)) { if (String(key) == "design capacity") { - char *endptr = NULL; String sval = val; const int cvt = sval.to_int(); - if (*endptr == ' ') { - maximum = cvt; - } + maximum = cvt; } } |
