diff options
Diffstat (limited to 'platform')
224 files changed, 6445 insertions, 6059 deletions
diff --git a/platform/SCsub b/platform/SCsub new file mode 100644 index 0000000000..2019a30be7 --- /dev/null +++ b/platform/SCsub @@ -0,0 +1,30 @@ +#!/usr/bin/env python + +from compat import open_utf8 + +Import('env') +platform_sources = [] + +# Register platform-exclusive APIs +reg_apis_inc = '#include "register_platform_apis.h"\n' +reg_apis = 'void register_platform_apis() {\n' +unreg_apis = 'void unregister_platform_apis() {\n' +for platform in env.platform_apis: + platform_dir = env.Dir(platform) + platform_sources.append(platform_dir.File('api/api.cpp')) + reg_apis += '\tregister_' + platform + '_api();\n' + unreg_apis += '\tunregister_' + platform + '_api();\n' + reg_apis_inc += '#include "' + platform + '/api/api.h"\n' +reg_apis_inc += '\n' +reg_apis += '}\n\n' +unreg_apis += '}\n' +with open_utf8('register_platform_apis.gen.cpp', 'w') as f: + f.write(reg_apis_inc) + f.write(reg_apis) + f.write(unreg_apis) +platform_sources.append('register_platform_apis.gen.cpp') + +lib = env.add_library('platform', platform_sources) +env.Prepend(LIBS=lib) + +Export('env') diff --git a/platform/android/AndroidManifest.xml.template b/platform/android/AndroidManifest.xml.template index 9d8eb951c4..a42ceb3c21 100644 --- a/platform/android/AndroidManifest.xml.template +++ b/platform/android/AndroidManifest.xml.template @@ -31,7 +31,7 @@ $$ADD_APPLICATION_CHUNKS$$ </application> - <uses-feature android:glEsVersion="0x00030000"/> + <uses-feature android:glEsVersion="0x00030000" android:required="true" /> $$ADD_PERMISSION_CHUNKS$$ <uses-permission android:name="godot.ACCESS_CHECKIN_PROPERTIES"/> diff --git a/platform/android/SCsub b/platform/android/SCsub index 7fa0262359..8c08289932 100644 --- a/platform/android/SCsub +++ b/platform/android/SCsub @@ -41,10 +41,8 @@ prog = None abspath = env.Dir(".").abspath -gradle_basein = open_utf8(abspath + "/build.gradle.template", "r") -gradle_baseout = open_utf8(abspath + "/java/build.gradle", "w") - -gradle_text = gradle_basein.read() +with open_utf8(abspath + "/build.gradle.template", "r") as gradle_basein: + gradle_text = gradle_basein.read() gradle_maven_flat_text = "" if len(env.android_flat_dirs) > 0: @@ -104,7 +102,7 @@ gradle_asset_dirs_text = "" gradle_default_config_text = "" minSdk = 18 -targetSdk = 23 +targetSdk = 27 for x in env.android_default_config: if x.startswith("minSdkVersion") and int(x.split(" ")[-1]) < minSdk: @@ -131,21 +129,22 @@ gradle_text = gradle_text.replace("$$GRADLE_DEFAULT_CONFIG$$", gradle_default_co gradle_text = gradle_text.replace("$$GRADLE_PLUGINS$$", gradle_plugins) gradle_text = gradle_text.replace("$$GRADLE_CLASSPATH$$", gradle_classpath) -gradle_baseout.write(gradle_text) -gradle_baseout.close() +with open_utf8(abspath + "/java/build.gradle", "w") as gradle_baseout: + gradle_baseout.write(gradle_text) + +with open_utf8(abspath + "/AndroidManifest.xml.template", "r") as pp_basein: + manifest = pp_basein.read() -pp_basein = open_utf8(abspath + "/AndroidManifest.xml.template", "r") -pp_baseout = open_utf8(abspath + "/java/AndroidManifest.xml", "w") -manifest = pp_basein.read() manifest = manifest.replace("$$ADD_APPLICATION_CHUNKS$$", env.android_manifest_chunk) manifest = manifest.replace("$$ADD_PERMISSION_CHUNKS$$", env.android_permission_chunk) manifest = manifest.replace("$$ADD_APPATTRIBUTE_CHUNKS$$", env.android_appattributes_chunk) -pp_baseout.write(manifest) +with open_utf8(abspath + "/java/AndroidManifest.xml", "w") as pp_baseout: + pp_baseout.write(manifest) -env_android.SharedLibrary("#bin/libgodot", [android_objects], SHLIBSUFFIX=env["SHLIBSUFFIX"]) +lib = env_android.add_shared_library("#bin/libgodot", [android_objects], SHLIBSUFFIX=env["SHLIBSUFFIX"]) lib_arch_dir = '' if env['android_arch'] == 'armv6': diff --git a/platform/android/audio_driver_jandroid.cpp b/platform/android/audio_driver_jandroid.cpp index 4a3bdd877d..3d80e76707 100644 --- a/platform/android/audio_driver_jandroid.cpp +++ b/platform/android/audio_driver_jandroid.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "audio_driver_jandroid.h" #include "os/os.h" @@ -85,7 +86,6 @@ Error AudioDriverAndroid::init() { print_line("audio buffer size: " + itos(buffer_size)); } - __android_log_print(ANDROID_LOG_INFO, "godot", "Initializing audio! params: %i,%i ", mix_rate, buffer_size); audioBuffer = env->CallObjectMethod(io, _init_audio, mix_rate, buffer_size); ERR_FAIL_COND_V(audioBuffer == NULL, ERR_INVALID_PARAMETER); @@ -112,29 +112,10 @@ void AudioDriverAndroid::setup(jobject p_io) { jclass c = env->GetObjectClass(io); cls = (jclass)env->NewGlobalRef(c); - __android_log_print(ANDROID_LOG_INFO, "godot", "starting to attempt get methods"); - _init_audio = env->GetMethodID(cls, "audioInit", "(II)Ljava/lang/Object;"); - if (_init_audio != 0) { - __android_log_print(ANDROID_LOG_INFO, "godot", "*******GOT METHOD _init_audio ok!!"); - } else { - __android_log_print(ANDROID_LOG_INFO, "godot", "audioinit ok!"); - } - _write_buffer = env->GetMethodID(cls, "audioWriteShortBuffer", "([S)V"); - if (_write_buffer != 0) { - __android_log_print(ANDROID_LOG_INFO, "godot", "*******GOT METHOD _write_buffer ok!!"); - } - _quit = env->GetMethodID(cls, "audioQuit", "()V"); - if (_quit != 0) { - __android_log_print(ANDROID_LOG_INFO, "godot", "*******GOT METHOD _quit ok!!"); - } - _pause = env->GetMethodID(cls, "audioPause", "(Z)V"); - if (_quit != 0) { - __android_log_print(ANDROID_LOG_INFO, "godot", "*******GOT METHOD _pause ok!!"); - } } void AudioDriverAndroid::thread_func(JNIEnv *env) { @@ -143,7 +124,6 @@ void AudioDriverAndroid::thread_func(JNIEnv *env) { if (cls) { cls = (jclass)env->NewGlobalRef(cls); - __android_log_print(ANDROID_LOG_INFO, "godot", "*******CLASS FOUND!!!"); } jfieldID fid = env->GetStaticFieldID(cls, "io", "Lorg/godotengine/godot/GodotIO;"); jobject ob = env->GetStaticObjectField(cls, fid); @@ -151,9 +131,6 @@ void AudioDriverAndroid::thread_func(JNIEnv *env) { jclass c = env->GetObjectClass(gob); jclass lcls = (jclass)env->NewGlobalRef(c); _write_buffer = env->GetMethodID(lcls, "audioWriteShortBuffer", "([S)V"); - if (_write_buffer != 0) { - __android_log_print(ANDROID_LOG_INFO, "godot", "*******GOT METHOD _write_buffer ok!!"); - } while (!quit) { diff --git a/platform/android/audio_driver_jandroid.h b/platform/android/audio_driver_jandroid.h index 8135641a62..763f0e9b5a 100644 --- a/platform/android/audio_driver_jandroid.h +++ b/platform/android/audio_driver_jandroid.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef AUDIO_DRIVER_ANDROID_H #define AUDIO_DRIVER_ANDROID_H diff --git a/platform/android/audio_driver_opensl.cpp b/platform/android/audio_driver_opensl.cpp index 9ebed84ace..e6bd3ff253 100644 --- a/platform/android/audio_driver_opensl.cpp +++ b/platform/android/audio_driver_opensl.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "audio_driver_opensl.h" #include <string.h> @@ -116,8 +117,6 @@ Error AudioDriverOpenSL::init() { ERR_FAIL_V(ERR_INVALID_PARAMETER); } - print_line("OpenSL Init OK!"); - return OK; } @@ -272,4 +271,5 @@ AudioDriverOpenSL::AudioDriverOpenSL() { s_ad = this; mutex = Mutex::create(); //NULL; pause = false; + active = false; } diff --git a/platform/android/audio_driver_opensl.h b/platform/android/audio_driver_opensl.h index b505c5fe8a..2022bad02a 100644 --- a/platform/android/audio_driver_opensl.h +++ b/platform/android/audio_driver_opensl.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef AUDIO_DRIVER_OPENSL_H #define AUDIO_DRIVER_OPENSL_H diff --git a/platform/android/build.gradle.template b/platform/android/build.gradle.template index 7cb6cf860a..7269e658b4 100644 --- a/platform/android/build.gradle.template +++ b/platform/android/build.gradle.template @@ -15,12 +15,13 @@ allprojects { repositories { jcenter() mavenCentral() + google() $$GRADLE_REPOSITORY_URLS$$ } } dependencies { - compile 'com.android.support:support-v4:23.+' // can be removed if minSdkVersion 16 and modify DownloadNotification.java & V14CustomNotification.java + compile 'com.android.support:support-v4:27.+' // can be removed if minSdkVersion 16 and modify DownloadNotification.java & V14CustomNotification.java $$GRADLE_DEPENDENCIES$$ } @@ -31,8 +32,8 @@ android { disable 'MissingTranslation' } - compileSdkVersion 23 - buildToolsVersion "26.0.1" + compileSdkVersion 27 + buildToolsVersion "27.0.3" useLibrary 'org.apache.http.legacy' packagingOptions { diff --git a/platform/android/detect.py b/platform/android/detect.py index 13fc4ee810..971368db17 100644 --- a/platform/android/detect.py +++ b/platform/android/detect.py @@ -2,6 +2,7 @@ import os import sys import string import platform +from distutils.version import LooseVersion def is_active(): @@ -13,10 +14,13 @@ def get_name(): def can_build(): - return ("ANDROID_NDK_ROOT" in os.environ) +def get_platform(platform): + return int(platform.split("-")[1]) + + def get_opts(): from SCons.Variables import BoolVariable, EnumVariable @@ -25,7 +29,7 @@ def get_opts(): ('ndk_platform', 'Target platform (android-<api>, e.g. "android-18")', "android-18"), EnumVariable('android_arch', 'Target architecture', "armv7", ('armv7', 'armv6', 'arm64v8', 'x86')), BoolVariable('android_neon', 'Enable NEON support (armv7 only)', True), - BoolVariable('android_stl', 'Enable Android STL support (for modules)', False), + BoolVariable('android_stl', 'Enable Android STL support (for modules)', True) ] @@ -123,6 +127,9 @@ def configure(env): else: env.extra_suffix = ".armv7" + env.extra_suffix elif env["android_arch"] == "arm64v8": + if get_platform(env["ndk_platform"]) < 21: + print("WARNING: android_arch=arm64v8 is not supported by ndk_platform lower than andorid-21; setting ndk_platform=android-21") + env["ndk_platform"] = "android-21" env['ARCH'] = 'arch-arm64' target_subpath = "aarch64-linux-android-4.9" abi_subpath = "aarch64-linux-android" @@ -159,12 +166,13 @@ def configure(env): elif (sys.platform.startswith('win')): if (platform.machine().endswith('64')): host_subpath = "windows-x86_64" - if env["android_arch"] == "arm64v8": - mt_link = False else: mt_link = False host_subpath = "windows" + if env["android_arch"] == "arm64v8": + mt_link = False + 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" @@ -172,20 +180,39 @@ def configure(env): # For Clang to find NDK tools in preference of those system-wide env.PrependENVPath('PATH', tools_path) - env['CC'] = compiler_path + '/clang' - env['CXX'] = compiler_path + '/clang++' + ccache_path = os.environ.get("CCACHE") + if ccache_path == None: + env['CC'] = compiler_path + '/clang' + env['CXX'] = compiler_path + '/clang++' + else: + # there aren't any ccache wrappers available for Android, + # to enable caching we need to prepend the path to the ccache binary + env['CC'] = ccache_path + ' ' + compiler_path + '/clang' + env['CXX'] = ccache_path + ' ' + compiler_path + '/clang++' env['AR'] = tools_path + "/ar" env['RANLIB'] = tools_path + "/ranlib" env['AS'] = tools_path + "/as" - sysroot = env["ANDROID_NDK_ROOT"] + "/platforms/" + env['ndk_platform'] + "/" + env['ARCH'] common_opts = ['-fno-integrated-as', '-gcc-toolchain', gcc_toolchain_path] + lib_sysroot = env["ANDROID_NDK_ROOT"] + "/platforms/" + env['ndk_platform'] + "/" + env['ARCH'] + ## Compile flags - env.Append(CPPFLAGS=["-isystem", sysroot + "/usr/include"]) + ndk_version = get_ndk_version(env["ANDROID_NDK_ROOT"]) + if ndk_version != None and LooseVersion(ndk_version) >= LooseVersion("15.0.4075724"): + print("Using NDK unified headers") + sysroot = env["ANDROID_NDK_ROOT"] + "/sysroot" + env.Append(CPPFLAGS=["-isystem", sysroot + "/usr/include"]) + env.Append(CPPFLAGS=["-isystem", sysroot + "/usr/include/" + abi_subpath]) + # For unified headers this define has to be set manually + env.Append(CPPFLAGS=["-D__ANDROID_API__=" + str(get_platform(env['ndk_platform']))]) + else: + print("Using NDK deprecated headers") + env.Append(CPPFLAGS=["-isystem", lib_sysroot + "/usr/include"]) + env.Append(CPPFLAGS='-fpic -ffunction-sections -funwind-tables -fstack-protector-strong -fvisibility=hidden -fno-strict-aliasing'.split()) - env.Append(CPPFLAGS='-DNO_STATVFS -DGLES2_ENABLED'.split()) + env.Append(CPPFLAGS='-DNO_STATVFS -DGLES_ENABLED'.split()) env['neon_enabled'] = False if env['android_arch'] == 'x86': @@ -224,7 +251,7 @@ def configure(env): ## Link flags - env['LINKFLAGS'] = ['-shared', '--sysroot=' + sysroot, '-Wl,--warn-shared-textrel'] + env['LINKFLAGS'] = ['-shared', '--sysroot=' + lib_sysroot, '-Wl,--warn-shared-textrel'] if env["android_arch"] == "armv7": env.Append(LINKFLAGS='-Wl,--fix-cortex-a8'.split()) env.Append(LINKFLAGS='-Wl,--no-undefined -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now'.split()) @@ -234,10 +261,10 @@ def configure(env): env.Append(LINKFLAGS=target_opts) env.Append(LINKFLAGS=common_opts) - env.Append(LIBPATH=[env["ANDROID_NDK_ROOT"] + '/toolchains/arm-linux-androideabi-4.9/prebuilt/' + + env.Append(LIBPATH=[env["ANDROID_NDK_ROOT"] + '/toolchains/' + target_subpath + '/prebuilt/' + host_subpath + '/lib/gcc/' + abi_subpath + '/4.9.x']) env.Append(LIBPATH=[env["ANDROID_NDK_ROOT"] + - '/toolchains/arm-linux-androideabi-4.9/prebuilt/' + host_subpath + '/' + abi_subpath + '/lib']) + '/toolchains/' + target_subpath + '/prebuilt/' + host_subpath + '/' + abi_subpath + '/lib']) env.Append(CPPPATH=['#platform/android']) env.Append(CPPFLAGS=['-DANDROID_ENABLED', '-DUNIX_ENABLED', '-DNO_FCNTL', '-DMPC_FIXED_POINT']) @@ -248,3 +275,18 @@ def configure(env): if (env["android_arch"] == "armv6" or env["android_arch"] == "armv7"): env.Append(CFLAGS=["-DOPUS_ARM_OPT"]) env.opus_fixed_point = "yes" + +# Return NDK version string in source.properties (adapted from the Chromium project). +def get_ndk_version(path): + if path == None: + return None + prop_file_path = os.path.join(path, "source.properties") + try: + with open(prop_file_path) as prop_file: + for line in prop_file: + key_value = list(map(lambda x: x.strip(), line.split("="))) + if key_value[0] == "Pkg.Revision": + return key_value[1] + except: + print("Could not read source prop file '%s'" % prop_file_path) + return None diff --git a/platform/android/dir_access_android.cpp b/platform/android/dir_access_android.cpp index 9653a585b1..402da4527e 100644 --- a/platform/android/dir_access_android.cpp +++ b/platform/android/dir_access_android.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifdef ANDROID_NATIVE_ACTIVITY #include "dir_access_android.h" #include "file_access_android.h" diff --git a/platform/android/dir_access_android.h b/platform/android/dir_access_android.h index 2f63befc32..085d7160cd 100644 --- a/platform/android/dir_access_android.h +++ b/platform/android/dir_access_android.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef DIR_ACCESS_ANDROID_H #define DIR_ACCESS_ANDROID_H diff --git a/platform/android/dir_access_jandroid.cpp b/platform/android/dir_access_jandroid.cpp index 26e4684056..3e40b59de9 100644 --- a/platform/android/dir_access_jandroid.cpp +++ b/platform/android/dir_access_jandroid.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef ANDROID_NATIVE_ACTIVITY #include "dir_access_jandroid.h" @@ -129,7 +130,6 @@ Error DirAccessJAndroid::change_dir(String p_dir) { else new_dir = current_dir.plus_file(p_dir); - //print_line("new dir is: "+new_dir); //test if newdir exists new_dir = new_dir.simplify_path(); @@ -225,28 +225,14 @@ void DirAccessJAndroid::setup(jobject p_io) { JNIEnv *env = ThreadAndroid::get_env(); io = p_io; - __android_log_print(ANDROID_LOG_INFO, "godot", "STEP7"); jclass c = env->GetObjectClass(io); cls = (jclass)env->NewGlobalRef(c); - __android_log_print(ANDROID_LOG_INFO, "godot", "STEP8"); _dir_open = env->GetMethodID(cls, "dir_open", "(Ljava/lang/String;)I"); - if (_dir_open != 0) { - __android_log_print(ANDROID_LOG_INFO, "godot", "*******GOT METHOD _dir_open ok!!"); - } _dir_next = env->GetMethodID(cls, "dir_next", "(I)Ljava/lang/String;"); - if (_dir_next != 0) { - __android_log_print(ANDROID_LOG_INFO, "godot", "*******GOT METHOD _dir_next ok!!"); - } _dir_close = env->GetMethodID(cls, "dir_close", "(I)V"); - if (_dir_close != 0) { - __android_log_print(ANDROID_LOG_INFO, "godot", "*******GOT METHOD _dir_close ok!!"); - } _dir_is_dir = env->GetMethodID(cls, "dir_is_dir", "(I)Z"); - if (_dir_is_dir != 0) { - __android_log_print(ANDROID_LOG_INFO, "godot", "*******GOT METHOD _dir_is_dir ok!!"); - } //(*env)->CallVoidMethod(env,obj,aMethodID, myvar); } diff --git a/platform/android/dir_access_jandroid.h b/platform/android/dir_access_jandroid.h index ad89be3581..8dc52ab9c8 100644 --- a/platform/android/dir_access_jandroid.h +++ b/platform/android/dir_access_jandroid.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef DIR_ACCESS_JANDROID_H #define DIR_ACCESS_JANDROID_H diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index 79be1501a7..6b4d0ff8c4 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* 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" @@ -192,6 +193,19 @@ static const char *android_perms[] = { NULL }; +struct LauncherIcon { + char *option_id; + char *export_path; +}; + +static const LauncherIcon launcher_icons[] = { + { "launcher_icons/xxxhdpi_192x192", "res/drawable-xxxhdpi-v4/icon.png" }, + { "launcher_icons/xxhdpi_144x144", "res/drawable-xxhdpi-v4/icon.png" }, + { "launcher_icons/xhdpi_96x96", "res/drawable-xhdpi-v4/icon.png" }, + { "launcher_icons/hdpi_72x72", "res/drawable-hdpi-v4/icon.png" }, + { "launcher_icons/mdpi_48x48", "res/drawable-mdpi-v4/icon.png" } +}; + class EditorExportAndroid : public EditorExportPlatform { GDCLASS(EditorExportAndroid, EditorExportPlatform) @@ -243,7 +257,7 @@ class EditorExportAndroid : public EditorExportPlatform { if (dpos == -1) continue; d = d.substr(0, dpos).strip_edges(); - //print_line("found devuce: "+d); + //print_line("found device: "+d); ldevices.push_back(d); } @@ -339,10 +353,11 @@ class EditorExportAndroid : public EditorExportPlatform { ea->device_lock->unlock(); } + uint64_t sleep = OS::get_singleton()->get_power_state() == OS::POWERSTATE_ON_BATTERY ? 1000 : 100; uint64_t wait = 3000000; uint64_t time = OS::get_singleton()->get_ticks_usec(); while (OS::get_singleton()->get_ticks_usec() - time < wait) { - OS::get_singleton()->delay_usec(1000); + OS::get_singleton()->delay_usec(1000 * sleep); if (ea->quit_request) break; } @@ -370,7 +385,7 @@ class EditorExportAndroid : public EditorExportPlatform { } if (aname == "") { - aname = _MKSTR(VERSION_NAME); + aname = VERSION_NAME; } return aname; @@ -467,56 +482,80 @@ class EditorExportAndroid : public EditorExportPlatform { return zipfi; } - static Set<String> get_abis() { - Set<String> abis; - abis.insert("armeabi"); - abis.insert("armeabi-v7a"); - abis.insert("arm64-v8a"); - abis.insert("x86"); - abis.insert("x86_64"); - abis.insert("mips"); - abis.insert("mips64"); + static Vector<String> get_abis() { + // mips and armv6 are dead (especially for games), so not including them + Vector<String> abis; + abis.push_back("armeabi-v7a"); + abis.push_back("arm64-v8a"); + abis.push_back("x86"); + abis.push_back("x86_64"); return abis; } - static Error save_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total) { - APKExportData *ed = (APKExportData *)p_userdata; - String dst_path = p_path; - static Set<String> android_abis = get_abis(); - - if (dst_path.ends_with(".so")) { - String abi = dst_path.get_base_dir().get_file().strip_edges(); // parent dir name - if (android_abis.has(abi)) { - dst_path = "lib/" + abi + "/" + dst_path.get_file(); - } else { - String err = "Dynamic libraries must be located in the folder named after Android ABI they were compiled for. " + - p_path + " does not follow this convention."; - ERR_PRINT(err.utf8().get_data()); - return ERR_FILE_BAD_PATH; - } - } else { - dst_path = dst_path.replace_first("res://", "assets/"); - } - + static Error store_in_apk(APKExportData *ed, const String &p_path, const Vector<uint8_t> &p_data, int compression_method = Z_DEFLATED) { zip_fileinfo zipfi = get_zip_fileinfo(); - zipOpenNewFileInZip(ed->apk, - dst_path.utf8().get_data(), + p_path.utf8().get_data(), &zipfi, NULL, 0, NULL, 0, NULL, - _should_compress_asset(p_path, p_data) ? Z_DEFLATED : 0, + compression_method, Z_DEFAULT_COMPRESSION); zipWriteInFileInZip(ed->apk, p_data.ptr(), p_data.size()); zipCloseFileInZip(ed->apk); + + return OK; + } + + static Error save_apk_so(void *p_userdata, const SharedObject &p_so) { + if (!p_so.path.get_file().begins_with("lib")) { + String err = "Android .so file names must start with \"lib\", but got: " + p_so.path; + ERR_PRINT(err.utf8().get_data()); + return FAILED; + } + APKExportData *ed = (APKExportData *)p_userdata; + Vector<String> abis = get_abis(); + bool exported = false; + for (int i = 0; i < p_so.tags.size(); ++i) { + // shared objects can be fat (compatible with multiple ABIs) + int start_pos = 0; + int abi_index = abis.find(p_so.tags[i]); + if (abi_index != -1) { + exported = true; + start_pos = abi_index + 1; + String abi = abis[abi_index]; + String dst_path = "lib/" + abi + "/" + p_so.path.get_file(); + Vector<uint8_t> array = FileAccess::get_file_as_array(p_so.path); + Error store_err = store_in_apk(ed, dst_path, array); + ERR_FAIL_COND_V(store_err, store_err); + } + } + if (!exported) { + String abis_string = String(" ").join(abis); + String err = "Cannot determine ABI for library \"" + p_so.path + "\". One of the supported ABIs must be used as a tag: " + abis_string; + ERR_PRINT(err.utf8().get_data()); + return FAILED; + } + return OK; + } + + static Error save_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total) { + APKExportData *ed = (APKExportData *)p_userdata; + String dst_path = p_path.replace_first("res://", "assets/"); + + store_in_apk(ed, dst_path, p_data, _should_compress_asset(p_path, p_data) ? Z_DEFLATED : 0); ed->ep->step("File: " + p_path, 3 + p_file * 100 / p_total); return OK; } + static Error ignore_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total) { + return OK; + } + void _fix_manifest(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_manifest, bool p_give_internet) { // Leaving the unused types commented because looking these constants up @@ -935,6 +974,18 @@ class EditorExportAndroid : public EditorExportPlatform { //printf("end\n"); } + static Vector<String> get_enabled_abis(const Ref<EditorExportPreset> &p_preset) { + Vector<String> abis = get_abis(); + Vector<String> enabled_abis; + for (int i = 0; i < abis.size(); ++i) { + bool is_enabled = p_preset->get("architectures/" + abis[i]); + if (is_enabled) { + enabled_abis.push_back(abis[i]); + } + } + return enabled_abis; + } + public: enum { MAX_USER_PERMISSIONS = 20 @@ -945,16 +996,22 @@ public: public: virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) { - int api = p_preset->get("graphics/api"); + // Re-enable when a GLES 2.0 backend is read + /*int api = p_preset->get("graphics/api"); if (api == 0) r_features->push_back("etc"); - else - r_features->push_back("etc2"); + else*/ + r_features->push_back("etc2"); + + Vector<String> abis = get_enabled_abis(p_preset); + for (int i = 0; i < abis.size(); ++i) { + r_features->push_back(abis[i]); + } } 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::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"), "")); @@ -964,16 +1021,18 @@ public: r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "version/name"), "1.0")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/unique_name"), "org.godotengine.$genname")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/name"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/icon", PROPERTY_HINT_FILE, "png"), "")); 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/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)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_normal"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_large"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_xlarge"), true)); + + for (int i = 0; i < sizeof(launcher_icons) / sizeof(launcher_icons[0]); ++i) { + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_icons[i].option_id, PROPERTY_HINT_FILE, "png"), "")); + } + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release", PROPERTY_HINT_GLOBAL_FILE, "keystore"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_user"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_password"), "")); @@ -981,6 +1040,13 @@ public: r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "apk_expansion/SALT"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "apk_expansion/public_key", PROPERTY_HINT_MULTILINE_TEXT), "")); + Vector<String> abis = get_abis(); + for (int i = 0; i < abis.size(); ++i) { + String abi = abis[i]; + bool is_default = (abi == "armeabi-v7a"); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "architectures/" + abi), is_default)); + } + const char **perms = android_perms; while (*perms) { @@ -1066,7 +1132,7 @@ public: if (use_reverse) p_debug_flags |= DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST; - String export_to = EditorSettings::get_singleton()->get_settings_path() + "/tmp/tmpexport.apk"; + String export_to = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmpexport.apk"); Error err = export_project(p_preset, true, export_to, p_debug_flags); if (err) { device_lock->unlock(); @@ -1245,7 +1311,7 @@ public: return valid; } - virtual String get_binary_extension() const { + virtual String get_binary_extension(const Ref<EditorExportPreset> &p_preset) const { return "apk"; } @@ -1291,13 +1357,9 @@ public: zlib_filefunc_def io2 = io; FileAccess *dst_f = NULL; io2.opaque = &dst_f; - String unaligned_path = EditorSettings::get_singleton()->get_settings_path() + "/tmp/tmpexport-unaligned.apk"; + String unaligned_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmpexport-unaligned.apk"); zipFile unaligned_apk = zipOpen2(unaligned_path.utf8().get_data(), APPEND_STATUS_CREATE, NULL, &io2); - bool export_x86 = p_preset->get("architecture/x86"); - bool export_arm = p_preset->get("architecture/arm"); - bool export_arm64 = p_preset->get("architecture/arm64"); - bool use_32_fb = p_preset->get("graphics/32_bits_framebuffer"); bool immersive = p_preset->get("screen/immersive_mode"); @@ -1317,6 +1379,8 @@ public: String release_username = p_preset->get("keystore/release_user"); String release_password = p_preset->get("keystore/release_password"); + Vector<String> enabled_abis = get_enabled_abis(p_preset); + while (ret == UNZ_OK) { //get filename @@ -1333,7 +1397,7 @@ public: //read unzOpenCurrentFile(pkg); - unzReadCurrentFile(pkg, data.ptr(), data.size()); + unzReadCurrentFile(pkg, data.ptrw(), data.size()); unzCloseCurrentFile(pkg); //write @@ -1349,23 +1413,20 @@ public: } if (file == "res/drawable/icon.png") { - - String icon = p_preset->get("package/icon"); - icon = icon.strip_edges(); bool found = false; - - if (icon != "" && icon.ends_with(".png")) { - - FileAccess *f = FileAccess::open(icon, FileAccess::READ); - if (f) { - - data.resize(f->get_len()); - f->get_buffer(data.ptr(), data.size()); - memdelete(f); - found = true; + for (int i = 0; i < sizeof(launcher_icons) / sizeof(launcher_icons[0]); ++i) { + String icon_path = String(p_preset->get(launcher_icons[i].option_id)).strip_edges(); + if (icon_path != "" && icon_path.ends_with(".png")) { + FileAccess *f = FileAccess::open(icon_path, FileAccess::READ); + if (f) { + data.resize(f->get_len()); + f->get_buffer(data.ptrw(), data.size()); + memdelete(f); + found = true; + break; + } } } - if (!found) { String appicon = ProjectSettings::get_singleton()->get("application/config/icon"); @@ -1373,32 +1434,32 @@ public: FileAccess *f = FileAccess::open(appicon, FileAccess::READ); if (f) { data.resize(f->get_len()); - f->get_buffer(data.ptr(), data.size()); + f->get_buffer(data.ptrw(), data.size()); memdelete(f); } } } } - if (file == "lib/x86/*.so" && !export_x86) { - skip = true; - } - - if (file.match("lib/armeabi*/*.so") && !export_arm) { - skip = true; - } - - if (file.match("lib/arm64*/*.so") && !export_arm64) { - skip = true; + if (file.ends_with(".so")) { + bool enabled = false; + for (int i = 0; i < enabled_abis.size(); ++i) { + if (file.begins_with("lib/" + enabled_abis[i] + "/")) { + enabled = true; + break; + } + } + if (!enabled) { + skip = true; + } } if (file.begins_with("META-INF") && _signed) { skip = true; } - print_line("ADDING: " + file); - if (!skip) { + print_line("ADDING: " + file); // Respect decision on compression made by AAPT for the export template const bool uncompressed = info.compression_method == 0; @@ -1447,6 +1508,10 @@ public: cl.push_back(passwd); }*/ + APKExportData ed; + ed.ep = &ep; + ed.apk = unaligned_apk; + err = export_project_files(p_preset, ignore_apk_file, &ed, save_apk_so); } else { //all files @@ -1472,7 +1537,20 @@ public: ed.ep = &ep; ed.apk = unaligned_apk; - err = export_project_files(p_preset, save_apk_file, &ed); + err = export_project_files(p_preset, save_apk_file, &ed, save_apk_so); + } + } + + if (!err) { + APKExportData ed; + ed.ep = &ep; + ed.apk = unaligned_apk; + for (int i = 0; i < sizeof(launcher_icons) / sizeof(launcher_icons[0]); ++i) { + String icon_path = String(p_preset->get(launcher_icons[i].option_id)).strip_edges(); + if (icon_path != "" && icon_path.ends_with(".png") && FileAccess::exists(icon_path)) { + Vector<uint8_t> data = FileAccess::get_file_as_array(icon_path); + store_in_apk(&ed, launcher_icons[i].export_path, data); + } } } @@ -1489,12 +1567,15 @@ public: encode_uint32(cl.size(), &clf[0]); for (int i = 0; i < cl.size(); i++) { + print_line(itos(i) + " param: " + cl[i]); 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]); + int length = txt.length(); + if (!length) + continue; + clf.resize(base + 4 + length); + encode_uint32(length, &clf[base]); + copymem(&clf[base + 4], txt.ptr(), length); } zip_fileinfo zipfi = get_zip_fileinfo(); @@ -1635,7 +1716,7 @@ public: int method, level; unzOpenCurrentFile2(tmp_unaligned, &method, &level, 1); // raw read long file_offset = unzGetCurrentFileZStreamPos64(tmp_unaligned); - unzReadCurrentFile(tmp_unaligned, data.ptr(), data.size()); + unzReadCurrentFile(tmp_unaligned, data.ptrw(), data.size()); unzCloseCurrentFile(tmp_unaligned); // align diff --git a/platform/android/export/export.h b/platform/android/export/export.h index 40ce129dcd..9d66626866 100644 --- a/platform/android/export/export.h +++ b/platform/android/export/export.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,4 +27,5 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + void register_android_exporter(); diff --git a/platform/android/file_access_android.cpp b/platform/android/file_access_android.cpp index 0fdf9002d7..c2eed50e4c 100644 --- a/platform/android/file_access_android.cpp +++ b/platform/android/file_access_android.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "file_access_android.h" #include "print_string.h" diff --git a/platform/android/file_access_android.h b/platform/android/file_access_android.h index c8fedbe684..03f4c59521 100644 --- a/platform/android/file_access_android.h +++ b/platform/android/file_access_android.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef FILE_ACCESS_ANDROID_H #define FILE_ACCESS_ANDROID_H diff --git a/platform/android/file_access_jandroid.cpp b/platform/android/file_access_jandroid.cpp index 980afd8f46..214e273d9b 100644 --- a/platform/android/file_access_jandroid.cpp +++ b/platform/android/file_access_jandroid.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef ANDROID_NATIVE_ACTIVITY #include "file_access_jandroid.h" @@ -189,43 +190,16 @@ void FileAccessJAndroid::setup(jobject p_io) { io = p_io; JNIEnv *env = ThreadAndroid::get_env(); - __android_log_print(ANDROID_LOG_INFO, "godot", "STEP5"); - jclass c = env->GetObjectClass(io); - __android_log_print(ANDROID_LOG_INFO, "godot", "STEP6"); cls = (jclass)env->NewGlobalRef(c); _file_open = env->GetMethodID(cls, "file_open", "(Ljava/lang/String;Z)I"); - if (_file_open != 0) { - __android_log_print(ANDROID_LOG_INFO, "godot", "*******GOT METHOD _file_open ok!!"); - } _file_get_size = env->GetMethodID(cls, "file_get_size", "(I)I"); - if (_file_get_size != 0) { - __android_log_print(ANDROID_LOG_INFO, "godot", "*******GOT METHOD _file_get_size ok!!"); - } _file_tell = env->GetMethodID(cls, "file_tell", "(I)I"); - if (_file_tell != 0) { - __android_log_print(ANDROID_LOG_INFO, "godot", "*******GOT METHOD _file_tell ok!!"); - } _file_eof = env->GetMethodID(cls, "file_eof", "(I)Z"); - - if (_file_eof != 0) { - __android_log_print(ANDROID_LOG_INFO, "godot", "*******GOT METHOD _file_eof ok!!"); - } _file_seek = env->GetMethodID(cls, "file_seek", "(II)V"); - if (_file_seek != 0) { - __android_log_print(ANDROID_LOG_INFO, "godot", "*******GOT METHOD _file_seek ok!!"); - } _file_read = env->GetMethodID(cls, "file_read", "(II)[B"); - if (_file_read != 0) { - __android_log_print(ANDROID_LOG_INFO, "godot", "*******GOT METHOD _file_read ok!!"); - } _file_close = env->GetMethodID(cls, "file_close", "(I)V"); - if (_file_close != 0) { - __android_log_print(ANDROID_LOG_INFO, "godot", "*******GOT METHOD _file_close ok!!"); - } - - //(*env)->CallVoidMethod(env,obj,aMethodID, myvar); } FileAccessJAndroid::FileAccessJAndroid() { diff --git a/platform/android/file_access_jandroid.h b/platform/android/file_access_jandroid.h index 368d2c98fa..72f81ee02e 100644 --- a/platform/android/file_access_jandroid.h +++ b/platform/android/file_access_jandroid.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef FILE_ACCESS_JANDROID_H #define FILE_ACCESS_JANDROID_H diff --git a/platform/android/globals/global_defaults.cpp b/platform/android/globals/global_defaults.cpp index c73b578154..a315f80452 100644 --- a/platform/android/globals/global_defaults.cpp +++ b/platform/android/globals/global_defaults.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,16 +27,9 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "global_defaults.h" #include "project_settings.h" void register_android_global_defaults() { - - /* GLOBAL_DEF("rasterizer.Android/use_fragment_lighting",false); - GLOBAL_DEF("rasterizer.Android/fp16_framebuffer",false); - GLOBAL_DEF("display.Android/driver","GLES2"); - //GLOBAL_DEF("rasterizer.Android/trilinear_mipmap_filter",false); - - 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/globals/global_defaults.h b/platform/android/globals/global_defaults.h index 8c918414d3..99da2dd527 100644 --- a/platform/android/globals/global_defaults.h +++ b/platform/android/globals/global_defaults.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,4 +27,5 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + void register_android_global_defaults(); diff --git a/platform/android/godot_android.cpp b/platform/android/godot_android.cpp index 8235683496..0e5f4fb93a 100644 --- a/platform/android/godot_android.cpp +++ b/platform/android/godot_android.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,25 +27,26 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef ANDROID_NATIVE_ACTIVITY -#include <errno.h> -#include <jni.h> - -#include <EGL/egl.h> -#include <GLES2/gl2.h> +#ifdef ANDROID_NATIVE_ACTIVITY +#include "engine.h" #include "file_access_android.h" #include "main/main.h" #include "os_android.h" #include "project_settings.h" + +#include <EGL/egl.h> #include <android/log.h> #include <android/sensor.h> #include <android/window.h> #include <android_native_app_glue.h> +#include <errno.h> +#include <jni.h> #include <stdlib.h> #include <string.h> #include <unistd.h> + #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "godot", __VA_ARGS__)) #define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "godot", __VA_ARGS__)) @@ -75,14 +76,11 @@ public: virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error) { - print_line("attempt to call " + String(p_method)); - r_error.error = Variant::CallError::CALL_OK; Map<StringName, MethodData>::Element *E = method_map.find(p_method); if (!E) { - print_line("no exists"); r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD; return Variant(); } @@ -90,7 +88,6 @@ public: int ac = E->get().argtypes.size(); if (ac < p_argcount) { - print_line("fewargs"); r_error.error = Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; r_error.argument = ac; return Variant(); @@ -98,7 +95,6 @@ public: if (ac > p_argcount) { - print_line("manyargs"); r_error.error = Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; r_error.argument = ac; return Variant(); @@ -180,26 +176,21 @@ public: } } - print_line("calling method!!"); - Variant ret; switch (E->get().ret_type) { case Variant::NIL: { - print_line("call void"); env->CallVoidMethodA(instance, E->get().method, v); } break; case Variant::BOOL: { ret = env->CallBooleanMethodA(instance, E->get().method, v); - print_line("call bool"); } break; case Variant::INT: { ret = env->CallIntMethodA(instance, E->get().method, v); - print_line("call int"); } break; case Variant::REAL: { @@ -254,13 +245,10 @@ public: } break; default: { - print_line("failure.."); ERR_FAIL_V(Variant()); } break; } - print_line("success"); - return ret; } @@ -388,7 +376,6 @@ static int engine_init_display(struct engine *engine, bool p_gl2) { eglQuerySurface(display, surface, EGL_WIDTH, &w); eglQuerySurface(display, surface, EGL_HEIGHT, &h); - print_line("INIT VIDEO MODE: " + itos(w) + "," + itos(h)); //engine->os->set_egl_extensions(eglQueryString(display,EGL_EXTENSIONS)); engine->os->init_video_mode(w, h); @@ -669,6 +656,14 @@ static void engine_handle_cmd(struct android_app *app, int32_t cmd) { ASensorEventQueue_setEventRate(engine->sensorEventQueue, engine->accelerometerSensor, (1000L / 60) * 1000); } + // start monitoring gravity + if (engine->gravitySensor != NULL) { + ASensorEventQueue_enableSensor(engine->sensorEventQueue, + engine->gravitySensor); + // We'd like to get 60 events per second (in us). + ASensorEventQueue_setEventRate(engine->sensorEventQueue, + engine->gravitySensor, (1000L / 60) * 1000); + } // Also start monitoring the magnetometer. if (engine->magnetometerSensor != NULL) { ASensorEventQueue_enableSensor(engine->sensorEventQueue, @@ -694,6 +689,10 @@ static void engine_handle_cmd(struct android_app *app, int32_t cmd) { ASensorEventQueue_disableSensor(engine->sensorEventQueue, engine->accelerometerSensor); } + if (engine->gravitySensor != NULL) { + ASensorEventQueue_disableSensor(engine->sensorEventQueue, + engine->gravitySensor); + } if (engine->magnetometerSensor != NULL) { ASensorEventQueue_disableSensor(engine->sensorEventQueue, engine->magnetometerSensor); @@ -729,6 +728,8 @@ void android_main(struct android_app *app) { engine.sensorManager = ASensorManager_getInstance(); engine.accelerometerSensor = ASensorManager_getDefaultSensor(engine.sensorManager, ASENSOR_TYPE_ACCELEROMETER); + engine.gravitySensor = ASensorManager_getDefaultSensor(engine.sensorManager, + ASENSOR_TYPE_GRAVITY); engine.magnetometerSensor = ASensorManager_getDefaultSensor(engine.sensorManager, ASENSOR_TYPE_MAGNETIC_FIELD); engine.gyroscopeSensor = ASensorManager_getDefaultSensor(engine.sensorManager, @@ -828,7 +829,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_Godot_registerSingleton(JNIEnv s->set_instance(env->NewGlobalRef(p_object)); jni_singletons[singname] = s; - ProjectSettings::get_singleton()->add_singleton(ProjectSettings::Singleton(singname, s)); + Engine::get_singleton()->add_singleton(Engine::Singleton(singname, s)); } static Variant::Type get_jni_type(const String &p_type) { @@ -913,7 +914,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_Godot_registerMethod(JNIEnv *e int stringCount = env->GetArrayLength(args); - print_line("Singl: " + singname + " Method: " + mname + " RetVal: " + retval); for (int i = 0; i < stringCount; i++) { jstring string = (jstring)env->GetObjectArrayElement(args, i); @@ -925,11 +925,10 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_Godot_registerMethod(JNIEnv *e cs += ")"; cs += get_jni_sig(retval); jclass cls = env->GetObjectClass(s->get_instance()); - print_line("METHOD: " + mname + " sig: " + cs); jmethodID mid = env->GetMethodID(cls, mname.ascii().get_data(), cs.ascii().get_data()); if (!mid) { - print_line("FAILED GETTING METHOID " + mname); + print_line("FAILED GETTING METHOD ID " + mname); } s->add_method(mname, mid, types, get_jni_type(retval)); diff --git a/platform/android/java/gradle/wrapper/gradle-wrapper.properties b/platform/android/java/gradle/wrapper/gradle-wrapper.properties index ee6901c9d7..fe37fa74a9 100644 --- a/platform/android/java/gradle/wrapper/gradle-wrapper.properties +++ b/platform/android/java/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip diff --git a/platform/android/java/gradlew.bat b/platform/android/java/gradlew.bat index aec99730b4..8a0b282aa6 100644 --- a/platform/android/java/gradlew.bat +++ b/platform/android/java/gradlew.bat @@ -1,90 +1,90 @@ -@if "%DEBUG%" == "" @echo off
-@rem ##########################################################################
-@rem
-@rem Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
-
-set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto init
-
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto init
-
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:init
-@rem Get command-line arguments, handling Windowz variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-if "%@eval[2+2]" == "4" goto 4NT_args
-
-:win9xME_args
-@rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-goto execute
-
-:4NT_args
-@rem Get arguments from the 4NT Shell from JP Software
-set CMD_LINE_ARGS=%$
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
-
-:end
-@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
+@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/platform/android/java/src/org/godotengine/godot/Dictionary.java b/platform/android/java/src/org/godotengine/godot/Dictionary.java index ed91fedd85..de6b4af568 100644 --- a/platform/android/java/src/org/godotengine/godot/Dictionary.java +++ b/platform/android/java/src/org/godotengine/godot/Dictionary.java @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,12 +27,12 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + package org.godotengine.godot; import java.util.HashMap; import java.util.Set; - public class Dictionary extends HashMap<String, Object> { protected String[] keys_cache; @@ -40,7 +40,7 @@ public class Dictionary extends HashMap<String, Object> { public String[] get_keys() { String[] ret = new String[size()]; - int i=0; + int i = 0; Set<String> keys = keySet(); for (String key : keys) { @@ -54,7 +54,7 @@ public class Dictionary extends HashMap<String, Object> { public Object[] get_values() { Object[] ret = new Object[size()]; - int i=0; + int i = 0; Set<String> keys = keySet(); for (String key : keys) { @@ -71,7 +71,7 @@ public class Dictionary extends HashMap<String, Object> { public void set_values(Object[] vals) { - int i=0; + int i = 0; for (String key : keys_cache) { put(key, vals[i]); i++; diff --git a/platform/android/java/src/org/godotengine/godot/Godot.java b/platform/android/java/src/org/godotengine/godot/Godot.java index 59fefc498f..90848e6a90 100644 --- a/platform/android/java/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/src/org/godotengine/godot/Godot.java @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + package org.godotengine.godot; import android.R; @@ -90,35 +91,34 @@ import android.os.Bundle; import android.os.Messenger; import android.os.SystemClock; - -public class Godot extends Activity implements SensorEventListener, IDownloaderClient -{ +public class Godot extends Activity implements SensorEventListener, IDownloaderClient { static final int MAX_SINGLETONS = 64; private IStub mDownloaderClientStub; - private IDownloaderService mRemoteService; - private TextView mStatusText; - private TextView mProgressFraction; - private TextView mProgressPercent; - private TextView mAverageSpeed; - private TextView mTimeRemaining; - private ProgressBar mPB; - - private View mDashboard; - private View mCellMessage; - - private Button mPauseButton; - private Button mWiFiSettingsButton; - - private boolean use_32_bits=false; - private boolean use_immersive=false; - private boolean mStatePaused; - private int mState; - private boolean keep_screen_on=true; + private IDownloaderService mRemoteService; + private TextView mStatusText; + private TextView mProgressFraction; + private TextView mProgressPercent; + private TextView mAverageSpeed; + private TextView mTimeRemaining; + private ProgressBar mPB; + + private View mDashboard; + private View mCellMessage; + + private Button mPauseButton; + private Button mWiFiSettingsButton; + + private boolean use_32_bits = false; + private boolean use_immersive = false; + private boolean mStatePaused; + private int mState; + private boolean keep_screen_on = true; static private Intent mCurrentIntent; - @Override public void onNewIntent(Intent intent) { + @Override + public void onNewIntent(Intent intent) { mCurrentIntent = intent; } @@ -127,44 +127,39 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC } private void setState(int newState) { - if (mState != newState) { - mState = newState; - mStatusText.setText(Helpers.getDownloaderStringResourceIDFromState(newState)); - } - } - - private void setButtonPausedState(boolean paused) { - mStatePaused = paused; - int stringResourceID = paused ? com.godot.game.R.string.text_button_resume : - com.godot.game.R.string.text_button_pause; - mPauseButton.setText(stringResourceID); - } + if (mState != newState) { + mState = newState; + mStatusText.setText(Helpers.getDownloaderStringResourceIDFromState(newState)); + } + } + + private void setButtonPausedState(boolean paused) { + mStatePaused = paused; + int stringResourceID = paused ? com.godot.game.R.string.text_button_resume : + com.godot.game.R.string.text_button_pause; + mPauseButton.setText(stringResourceID); + } static public class SingletonBase { protected void registerClass(String p_name, String[] p_methods) { - GodotLib.singleton(p_name,this); + GodotLib.singleton(p_name, this); Class clazz = getClass(); Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { - boolean found=false; - Log.d("XXX","METHOD: %s\n" + method.getName()); + boolean found = false; for (String s : p_methods) { - Log.d("XXX", "METHOD CMP WITH: %s\n" + s); if (s.equals(method.getName())) { - found=true; - Log.d("XXX","METHOD CMP VALID"); + found = true; break; } } if (!found) continue; - Log.d("XXX","METHOD FOUND: %s\n" + method.getName()); - List<String> ptr = new ArrayList<String>(); Class[] paramTypes = method.getParameterTypes(); @@ -175,17 +170,13 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC String[] pt = new String[ptr.size()]; ptr.toArray(pt); - GodotLib.method(p_name,method.getName(),method.getReturnType().getName(),pt); - - + GodotLib.method(p_name, method.getName(), method.getReturnType().getName(), pt); } - Godot.singletons[Godot.singleton_count++]=this; + Godot.singletons[Godot.singleton_count++] = this; } protected void onMainActivityResult(int requestCode, int resultCode, Intent data) { - - } protected void onMainPause() {} @@ -200,61 +191,58 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC public void registerMethods() {} } -/* + /* protected List<SingletonBase> singletons = new ArrayList<SingletonBase>(); protected void instanceSingleton(SingletonBase s) { s.registerMethods(); singletons.add(s); } - -*/ + */ private String[] command_line; private boolean use_apk_expansion; public GodotView mView; - private boolean godot_initialized=false; - + private boolean godot_initialized = false; private SensorManager mSensorManager; private Sensor mAccelerometer; + private Sensor mGravity; private Sensor mMagnetometer; private Sensor mGyroscope; public FrameLayout layout; public RelativeLayout adLayout; - static public GodotIO io; public static void setWindowTitle(String title) { //setTitle(title); } - static SingletonBase singletons[] = new SingletonBase[MAX_SINGLETONS]; - static int singleton_count=0; - + static int singleton_count = 0; public interface ResultCallback { public void callback(int requestCode, int resultCode, Intent data); - }; + } public ResultCallback result_callback; private PaymentsManager mPaymentsManager = null; - @Override protected void onActivityResult (int requestCode, int resultCode, Intent data) { - if(requestCode == PaymentsManager.REQUEST_CODE_FOR_PURCHASE){ + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == PaymentsManager.REQUEST_CODE_FOR_PURCHASE) { mPaymentsManager.processPurchaseResponse(resultCode, data); - }else if (result_callback != null) { + } else if (result_callback != null) { result_callback.callback(requestCode, resultCode, data); result_callback = null; }; - for(int i=0;i<singleton_count;i++) { + for (int i = 0; i < singleton_count; i++) { - singletons[i].onMainActivityResult(requestCode,resultCode,data); + singletons[i].onMainActivityResult(requestCode, resultCode, data); } }; @@ -264,38 +252,37 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC //setContentView(mView); layout = new FrameLayout(this); - layout.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT)); + layout.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); setContentView(layout); // GodotEditText layout GodotEditText edittext = new GodotEditText(this); - edittext.setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.WRAP_CONTENT)); - // ...add to FrameLayout - layout.addView(edittext); + edittext.setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT)); + // ...add to FrameLayout + layout.addView(edittext); - mView = new GodotView(getApplication(),io,use_gl2,use_32_bits, this); - layout.addView(mView,new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT)); + mView = new GodotView(getApplication(), io, use_gl2, use_32_bits, this); + layout.addView(mView, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); edittext.setView(mView); io.setEdit(edittext); final Godot godot = this; mView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - Point fullSize = new Point(); - godot.getWindowManager().getDefaultDisplay().getSize(fullSize); - Rect gameSize = new Rect(); - godot.mView.getWindowVisibleDisplayFrame(gameSize); - - final int keyboardHeight = fullSize.y - gameSize.bottom; - Log.d("GODOT", "setVirtualKeyboardHeight: " + keyboardHeight); - GodotLib.setVirtualKeyboardHeight(keyboardHeight); - } + @Override + public void onGlobalLayout() { + Point fullSize = new Point(); + godot.getWindowManager().getDefaultDisplay().getSize(fullSize); + Rect gameSize = new Rect(); + godot.mView.getWindowVisibleDisplayFrame(gameSize); + + final int keyboardHeight = fullSize.y - gameSize.bottom; + GodotLib.setVirtualKeyboardHeight(keyboardHeight); + } }); // Ad layout adLayout = new RelativeLayout(this); - adLayout.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT)); + adLayout.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); layout.addView(adLayout); final String[] current_command_line = command_line; @@ -312,12 +299,11 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC }); } }); - } public void setKeepScreenOn(final boolean p_enabled) { keep_screen_on = p_enabled; - if (mView != null){ + if (mView != null) { runOnUiThread(new Runnable() { @Override public void run() { @@ -334,12 +320,12 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC AlertDialog.Builder builder = new AlertDialog.Builder(getInstance()); builder.setMessage(message).setTitle(title); builder.setPositiveButton( - "OK", - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - dialog.cancel(); - } - }); + "OK", + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + } + }); AlertDialog dialog = builder.create(); dialog.show(); } @@ -348,93 +334,76 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC private static Godot _self; - public static Godot getInstance(){ + public static Godot getInstance() { return Godot._self; } - private String[] getCommandLine() { - InputStream is; - try { - is = getAssets().open("_cl_"); - byte[] len = new byte[4]; - int r = is.read(len); - if (r<4) { - Log.d("XXX","**ERROR** Wrong cmdline length.\n"); - Log.d("GODOT", "**ERROR** Wrong cmdline length.\n"); - return new String[0]; - } - int argc=((int)(len[3]&0xFF)<<24) | ((int)(len[2]&0xFF)<<16) | ((int)(len[1]&0xFF)<<8) | ((int)(len[0]&0xFF)); - String[] cmdline = new String[argc]; - - for(int i=0;i<argc;i++) { - r = is.read(len); - if (r<4) { - - Log.d("GODOT", "**ERROR** Wrong cmdline param length.\n"); - return new String[0]; - } - int strlen=((int)(len[3]&0xFF)<<24) | ((int)(len[2]&0xFF)<<16) | ((int)(len[1]&0xFF)<<8) | ((int)(len[0]&0xFF)); - if (strlen>65535) { - Log.d("GODOT", "**ERROR** Wrong command len\n"); - return new String[0]; - } - byte[] arg = new byte[strlen]; - r = is.read(arg); - if (r==strlen) { - cmdline[i]=new String(arg,"UTF-8"); - } + InputStream is; + try { + is = getAssets().open("_cl_"); + byte[] len = new byte[4]; + int r = is.read(len); + if (r < 4) { + return new String[0]; + } + int argc = ((int)(len[3] & 0xFF) << 24) | ((int)(len[2] & 0xFF) << 16) | ((int)(len[1] & 0xFF) << 8) | ((int)(len[0] & 0xFF)); + String[] cmdline = new String[argc]; + + for (int i = 0; i < argc; i++) { + r = is.read(len); + if (r < 4) { + + return new String[0]; + } + int strlen = ((int)(len[3] & 0xFF) << 24) | ((int)(len[2] & 0xFF) << 16) | ((int)(len[1] & 0xFF) << 8) | ((int)(len[0] & 0xFF)); + if (strlen > 65535) { + return new String[0]; + } + byte[] arg = new byte[strlen]; + r = is.read(arg); + if (r == strlen) { + cmdline[i] = new String(arg, "UTF-8"); + } } return cmdline; } catch (Exception e) { - e.printStackTrace(); - Log.d("GODOT", "**ERROR** Exception " + e.getClass().getName() + ":" + e.getMessage()); + e.printStackTrace(); return new String[0]; } - - } - String expansion_pack_path; - private void initializeGodot() { - if (expansion_pack_path!=null) { + if (expansion_pack_path != null) { String[] new_cmdline; - int cll=0; - if (command_line!=null) { - Log.d("GODOT", "initializeGodot: command_line: is not null" ); - new_cmdline = new String[ command_line.length + 2 ]; - cll=command_line.length; - for(int i=0;i<command_line.length;i++) { - new_cmdline[i]=command_line[i]; + int cll = 0; + if (command_line != null) { + new_cmdline = new String[command_line.length + 2]; + cll = command_line.length; + for (int i = 0; i < command_line.length; i++) { + new_cmdline[i] = command_line[i]; } } else { - Log.d("GODOT", "initializeGodot: command_line: is null" ); - new_cmdline = new String[ 2 ]; + new_cmdline = new String[2]; } - new_cmdline[cll]="--main_pack"; - new_cmdline[cll+1]=expansion_pack_path; - command_line=new_cmdline; + new_cmdline[cll] = "--main-pack"; + new_cmdline[cll + 1] = expansion_pack_path; + command_line = new_cmdline; } io = new GodotIO(this); io.unique_id = Secure.getString(getContentResolver(), Secure.ANDROID_ID); - GodotLib.io=io; - Log.d("GODOT", "command_line is null? " + ((command_line == null)?"yes":"no")); - /*if(command_line != null){ - Log.d("GODOT", "Command Line:"); - for(int w=0;w <command_line.length;w++){ - Log.d("GODOT"," " + command_line[w]); - } - }*/ - mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); + GodotLib.io = io; + mSensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE); mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME); + mGravity = mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY); + mSensorManager.registerListener(this, mGravity, SensorManager.SENSOR_DELAY_GAME); mMagnetometer = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); mSensorManager.registerListener(this, mMagnetometer, SensorManager.SENSOR_DELAY_GAME); mGyroscope = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE); @@ -446,23 +415,18 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC mPaymentsManager = PaymentsManager.createManager(this).initService(); - godot_initialized=true; - + godot_initialized = true; } @Override public void onServiceConnected(Messenger m) { - mRemoteService = DownloaderServiceMarshaller.CreateProxy(m); - mRemoteService.onClientUpdated(mDownloaderClientStub.getMessenger()); + mRemoteService = DownloaderServiceMarshaller.CreateProxy(m); + mRemoteService.onClientUpdated(mDownloaderClientStub.getMessenger()); } - - @Override protected void onCreate(Bundle icicle) { - Log.d("GODOT", "** GODOT ACTIVITY CREATED HERE ***\n"); - super.onCreate(icicle); _self = this; Window window = getWindow(); @@ -471,67 +435,65 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC //check for apk expansion API if (true) { - boolean md5mismatch = false; + boolean md5mismatch = false; command_line = getCommandLine(); - String main_pack_md5=null; - String main_pack_key=null; + String main_pack_md5 = null; + String main_pack_key = null; List<String> new_args = new LinkedList<String>(); + for (int i = 0; i < command_line.length; i++) { - for(int i=0;i<command_line.length;i++) { - - boolean has_extra = i< command_line.length -1; + boolean has_extra = i < command_line.length - 1; if (command_line[i].equals("--use_depth_32")) { - use_32_bits=true; + use_32_bits = true; } else if (command_line[i].equals("--use_immersive")) { - use_immersive=true; - if(Build.VERSION.SDK_INT >= 19.0){ // check if the application runs on an android 4.4+ + use_immersive = true; + if (Build.VERSION.SDK_INT >= 19.0) { // check if the application runs on an android 4.4+ window.getDecorView().setSystemUiVisibility( - View.SYSTEM_UI_FLAG_LAYOUT_STABLE - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar - | View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar - | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); + View.SYSTEM_UI_FLAG_LAYOUT_STABLE | + View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | + View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | // hide nav bar + View.SYSTEM_UI_FLAG_FULLSCREEN | // hide status bar + View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); UiChangeListener(); } } else if (command_line[i].equals("--use_apk_expansion")) { - use_apk_expansion=true; + use_apk_expansion = true; } else if (has_extra && command_line[i].equals("--apk_expansion_md5")) { - main_pack_md5=command_line[i+1]; + main_pack_md5 = command_line[i + 1]; i++; } else if (has_extra && command_line[i].equals("--apk_expansion_key")) { - main_pack_key=command_line[i+1]; + main_pack_key = command_line[i + 1]; SharedPreferences prefs = getSharedPreferences("app_data_keys", MODE_PRIVATE); Editor editor = prefs.edit(); editor.putString("store_public_key", main_pack_key); editor.commit(); i++; - } else if (command_line[i].trim().length()!=0){ + } else if (command_line[i].trim().length() != 0) { new_args.add(command_line[i]); } } - if (new_args.isEmpty()){ - command_line=null; - }else{ + if (new_args.isEmpty()) { + command_line = null; + } else { command_line = new_args.toArray(new String[new_args.size()]); - } - if (use_apk_expansion && main_pack_md5!=null && main_pack_key!=null) { + } + if (use_apk_expansion && main_pack_md5 != null && main_pack_key != null) { //check that environment is ok! - if (!Environment.getExternalStorageState().equals( Environment.MEDIA_MOUNTED )) { - Log.d("GODOT", "**ERROR! No media mounted!"); + if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { //show popup and die } // Build the full path to the app's expansion files try { expansion_pack_path = Helpers.getSaveFilePath(getApplicationContext()); - expansion_pack_path+="/"+"main."+getPackageManager().getPackageInfo(getPackageName(), 0).versionCode+"."+this.getPackageName()+".obb"; + expansion_pack_path += "/main." + getPackageManager().getPackageInfo(getPackageName(), 0).versionCode + "." + this.getPackageName() + ".obb"; } catch (Exception e) { e.printStackTrace(); } @@ -539,72 +501,60 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC File f = new File(expansion_pack_path); boolean pack_valid = true; - Log.d("GODOT","**PACK** - Path "+expansion_pack_path); if (!f.exists()) { - pack_valid=false; - Log.d("GODOT","**PACK** - File does not exist"); + pack_valid = false; - } else if( obbIsCorrupted(expansion_pack_path, main_pack_md5)){ - Log.d("GODOT", "**PACK** - Expansion pack (obb) is corrupted"); + } else if (obbIsCorrupted(expansion_pack_path, main_pack_md5)) { pack_valid = false; - try{ - f.delete(); - }catch(Exception e){ - Log.d("GODOT", "**PACK** - Error deleting corrupted expansion pack (obb)"); + try { + f.delete(); + } catch (Exception e) { } } if (!pack_valid) { - Log.d("GODOT", "Pack Invalid, try re-downloading."); Intent notifierIntent = new Intent(this, this.getClass()); notifierIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | - Intent.FLAG_ACTIVITY_CLEAR_TOP); + Intent.FLAG_ACTIVITY_CLEAR_TOP); - PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, - notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT); + PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, + notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT); - int startResult; + int startResult; try { - Log.d("GODOT", "INITIALIZING DOWNLOAD"); startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired( getApplicationContext(), pendingIntent, GodotDownloaderService.class); - Log.d("GODOT", "DOWNLOAD SERVICE FINISHED:" + startResult); - - if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) { - Log.d("GODOT", "DOWNLOAD REQUIRED"); - // This is where you do set up to display the download - // progress (next step) - mDownloaderClientStub = DownloaderClientMarshaller.CreateStub(this, - GodotDownloaderService.class); - - setContentView(com.godot.game.R.layout.downloading_expansion); - mPB = (ProgressBar) findViewById(com.godot.game.R.id.progressBar); - mStatusText = (TextView) findViewById(com.godot.game.R.id.statusText); - mProgressFraction = (TextView) findViewById(com.godot.game.R.id.progressAsFraction); - mProgressPercent = (TextView) findViewById(com.godot.game.R.id.progressAsPercentage); - mAverageSpeed = (TextView) findViewById(com.godot.game.R.id.progressAverageSpeed); - mTimeRemaining = (TextView) findViewById(com.godot.game.R.id.progressTimeRemaining); - mDashboard = findViewById(com.godot.game.R.id.downloaderDashboard); - mCellMessage = findViewById(com.godot.game.R.id.approveCellular); - mPauseButton = (Button) findViewById(com.godot.game.R.id.pauseButton); - mWiFiSettingsButton = (Button) findViewById(com.godot.game.R.id.wifiSettingsButton); - - return; - } else{ - Log.d("GODOT", "NO DOWNLOAD REQUIRED"); - } + + if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) { + // This is where you do set up to display the download + // progress (next step) + mDownloaderClientStub = DownloaderClientMarshaller.CreateStub(this, + GodotDownloaderService.class); + + setContentView(com.godot.game.R.layout.downloading_expansion); + mPB = (ProgressBar)findViewById(com.godot.game.R.id.progressBar); + mStatusText = (TextView)findViewById(com.godot.game.R.id.statusText); + mProgressFraction = (TextView)findViewById(com.godot.game.R.id.progressAsFraction); + mProgressPercent = (TextView)findViewById(com.godot.game.R.id.progressAsPercentage); + mAverageSpeed = (TextView)findViewById(com.godot.game.R.id.progressAverageSpeed); + mTimeRemaining = (TextView)findViewById(com.godot.game.R.id.progressTimeRemaining); + mDashboard = findViewById(com.godot.game.R.id.downloaderDashboard); + mCellMessage = findViewById(com.godot.game.R.id.approveCellular); + mPauseButton = (Button)findViewById(com.godot.game.R.id.pauseButton); + mWiFiSettingsButton = (Button)findViewById(com.godot.game.R.id.wifiSettingsButton); + + return; + } else { + } } catch (NameNotFoundException e) { // TODO Auto-generated catch block - Log.d("GODOT", "Error downloading expansion package:" + e.getMessage()); } - } - } } @@ -612,28 +562,26 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC initializeGodot(); - //instanceSingleton( new GodotFacebook(this) ); - - } + @Override + protected void onDestroy() { - @Override protected void onDestroy(){ - - if(mPaymentsManager != null ) mPaymentsManager.destroy(); - for(int i=0;i<singleton_count;i++) { + if (mPaymentsManager != null) mPaymentsManager.destroy(); + for (int i = 0; i < singleton_count; i++) { singletons[i].onMainDestroy(); } super.onDestroy(); } - @Override protected void onPause() { + @Override + protected void onPause() { super.onPause(); - if (!godot_initialized){ + if (!godot_initialized) { if (null != mDownloaderClientStub) { - mDownloaderClientStub.disconnect(this); - } + mDownloaderClientStub.disconnect(this); + } return; } mView.onPause(); @@ -645,17 +593,18 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC }); mSensorManager.unregisterListener(this); - for(int i=0;i<singleton_count;i++) { + for (int i = 0; i < singleton_count; i++) { singletons[i].onMainPause(); } } - @Override protected void onResume() { + @Override + protected void onResume() { super.onResume(); - if (!godot_initialized){ + if (!godot_initialized) { if (null != mDownloaderClientStub) { - mDownloaderClientStub.connect(this); - } + mDownloaderClientStub.connect(this); + } return; } @@ -667,62 +616,62 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC } }); mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME); + mSensorManager.registerListener(this, mGravity, SensorManager.SENSOR_DELAY_GAME); mSensorManager.registerListener(this, mMagnetometer, SensorManager.SENSOR_DELAY_GAME); mSensorManager.registerListener(this, mGyroscope, SensorManager.SENSOR_DELAY_GAME); - if(use_immersive && Build.VERSION.SDK_INT >= 19.0){ // check if the application runs on an android 4.4+ + if (use_immersive && Build.VERSION.SDK_INT >= 19.0) { // check if the application runs on an android 4.4+ Window window = getWindow(); window.getDecorView().setSystemUiVisibility( - View.SYSTEM_UI_FLAG_LAYOUT_STABLE - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar - | View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar - | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); + View.SYSTEM_UI_FLAG_LAYOUT_STABLE | + View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | + View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | // hide nav bar + View.SYSTEM_UI_FLAG_FULLSCREEN | // hide status bar + View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); } - for(int i=0;i<singleton_count;i++) { + for (int i = 0; i < singleton_count; i++) { singletons[i].onMainResume(); } - - - } public void UiChangeListener() { final View decorView = getWindow().getDecorView(); - decorView.setOnSystemUiVisibilityChangeListener (new View.OnSystemUiVisibilityChangeListener() { + decorView.setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() { @Override public void onSystemUiVisibilityChange(int visibility) { if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) { decorView.setSystemUiVisibility( - View.SYSTEM_UI_FLAG_LAYOUT_STABLE - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_FULLSCREEN - | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); + View.SYSTEM_UI_FLAG_LAYOUT_STABLE | + View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | + View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_FULLSCREEN | + View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); } } }); } - @Override public void onSensorChanged(SensorEvent event) { - Display display = ((WindowManager) getSystemService(WINDOW_SERVICE)).getDefaultDisplay(); + @Override + public void onSensorChanged(SensorEvent event) { + Display display = ((WindowManager)getSystemService(WINDOW_SERVICE)).getDefaultDisplay(); int displayRotation = display.getRotation(); float[] adjustedValues = new float[3]; final int axisSwap[][] = { - { 1, -1, 0, 1 }, // ROTATION_0 - {-1, -1, 1, 0 }, // ROTATION_90 - {-1, 1, 0, 1 }, // ROTATION_180 - { 1, 1, 1, 0 } }; // ROTATION_270 + { 1, -1, 0, 1 }, // ROTATION_0 + { -1, -1, 1, 0 }, // ROTATION_90 + { -1, 1, 0, 1 }, // ROTATION_180 + { 1, 1, 1, 0 } + }; // ROTATION_270 final int[] as = axisSwap[displayRotation]; - adjustedValues[0] = (float)as[0] * event.values[ as[2] ]; - adjustedValues[1] = (float)as[1] * event.values[ as[3] ]; - adjustedValues[2] = event.values[2]; + adjustedValues[0] = (float)as[0] * event.values[as[2]]; + adjustedValues[1] = (float)as[1] * event.values[as[3]]; + adjustedValues[2] = event.values[2]; final float x = adjustedValues[0]; final float y = adjustedValues[1]; @@ -734,24 +683,28 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC @Override public void run() { if (typeOfSensor == Sensor.TYPE_ACCELEROMETER) { - GodotLib.accelerometer(x,y,z); + GodotLib.accelerometer(-x, y, -z); + } + if (typeOfSensor == Sensor.TYPE_GRAVITY) { + GodotLib.gravity(-x, y, -z); } if (typeOfSensor == Sensor.TYPE_MAGNETIC_FIELD) { - GodotLib.magnetometer(x,y,z); + GodotLib.magnetometer(-x, y, -z); } if (typeOfSensor == Sensor.TYPE_GYROSCOPE) { - GodotLib.gyroscope(x,y,z); + GodotLib.gyroscope(x, -y, z); } } }); } } - @Override public final void onAccuracyChanged(Sensor sensor, int accuracy) { + @Override + public final void onAccuracyChanged(Sensor sensor, int accuracy) { // Do something here if sensor accuracy changes. } -/* + /* @Override public boolean dispatchKeyEvent(KeyEvent event) { if (event.getKeyCode()==KeyEvent.KEYCODE_BACK) { @@ -765,18 +718,18 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC return false; } -*/ + */ - @Override public void onBackPressed() { + @Override + public void onBackPressed() { boolean shouldQuit = true; - for(int i=0;i<singleton_count;i++) { + for (int i = 0; i < singleton_count; i++) { if (singletons[i].onMainBackPressed()) { shouldQuit = false; } } - System.out.printf("** BACK REQUEST!\n"); if (shouldQuit && mView != null) { mView.queueEvent(new Runnable() { @Override @@ -792,85 +745,79 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC System.exit(0); } + private boolean obbIsCorrupted(String f, String main_pack_md5) { + try { - private boolean obbIsCorrupted(String f, String main_pack_md5){ - - try { - - InputStream fis = new FileInputStream(f); - - // Create MD5 Hash - byte[] buffer = new byte[16384]; + InputStream fis = new FileInputStream(f); - MessageDigest complete = MessageDigest.getInstance("MD5"); - int numRead; - do { - numRead = fis.read(buffer); - if (numRead > 0) { - complete.update(buffer, 0, numRead); - } - } while (numRead != -1); + // Create MD5 Hash + byte[] buffer = new byte[16384]; + MessageDigest complete = MessageDigest.getInstance("MD5"); + int numRead; + do { + numRead = fis.read(buffer); + if (numRead > 0) { + complete.update(buffer, 0, numRead); + } + } while (numRead != -1); - fis.close(); - byte[] messageDigest = complete.digest(); + fis.close(); + byte[] messageDigest = complete.digest(); - // Create Hex String - StringBuffer hexString = new StringBuffer(); - for (int i=0; i<messageDigest.length; i++) { - String s = Integer.toHexString(0xFF & messageDigest[i]); + // Create Hex String + StringBuffer hexString = new StringBuffer(); + for (int i = 0; i < messageDigest.length; i++) { + String s = Integer.toHexString(0xFF & messageDigest[i]); - if (s.length()==1) { - s="0"+s; - } - hexString.append(s); - } - String md5str = hexString.toString(); + if (s.length() == 1) { + s = "0" + s; + } + hexString.append(s); + } + String md5str = hexString.toString(); - //Log.d("GODOT","**PACK** - My MD5: "+hexString+" - APK md5: "+main_pack_md5); - if (!md5str.equals(main_pack_md5)) { - Log.d("GODOT","**PACK MD5 MISMATCH???** - MD5 Found: "+md5str+" "+Integer.toString(md5str.length())+" - MD5 Expected: "+main_pack_md5+" "+Integer.toString(main_pack_md5.length())); - return true; - } - return false; - } catch (Exception e) { - e.printStackTrace(); - Log.d("GODOT","**PACK FAIL**"); - return true; - } + if (!md5str.equals(main_pack_md5)) { + return true; + } + return false; + } catch (Exception e) { + e.printStackTrace(); + return true; + } } //@Override public boolean dispatchTouchEvent (MotionEvent event) { public boolean gotTouchEvent(final MotionEvent event) { - super.onTouchEvent(event); - final int evcount=event.getPointerCount(); - if (evcount==0) + final int evcount = event.getPointerCount(); + if (evcount == 0) return true; if (mView != null) { - final int[] arr = new int[event.getPointerCount()*3]; + final int[] arr = new int[event.getPointerCount() * 3]; - for(int i=0;i<event.getPointerCount();i++) { + for (int i = 0; i < event.getPointerCount(); i++) { - arr[i*3+0]=(int)event.getPointerId(i); - arr[i*3+1]=(int)event.getX(i); - arr[i*3+2]=(int)event.getY(i); + arr[i * 3 + 0] = (int)event.getPointerId(i); + arr[i * 3 + 1] = (int)event.getX(i); + arr[i * 3 + 2] = (int)event.getY(i); } + final int pointer_idx = event.getPointerId(event.getActionIndex()); //System.out.printf("gaction: %d\n",event.getAction()); final int action = event.getAction() & MotionEvent.ACTION_MASK; mView.queueEvent(new Runnable() { @Override public void run() { - switch(action) { + switch (action) { case MotionEvent.ACTION_DOWN: { - GodotLib.touch(0,0,evcount,arr); + GodotLib.touch(0, 0, evcount, arr); //System.out.printf("action down at: %f,%f\n", event.getX(),event.getY()); } break; case MotionEvent.ACTION_MOVE: { - GodotLib.touch(1,0,evcount,arr); + GodotLib.touch(1, 0, evcount, arr); /* for(int i=0;i<event.getPointerCount();i++) { System.out.printf("%d - moved to: %f,%f\n",i, event.getX(i),event.getY(i)); @@ -878,19 +825,16 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC */ } break; case MotionEvent.ACTION_POINTER_UP: { - final int indexPointUp = event.getActionIndex(); - final int pointer_idx = event.getPointerId(indexPointUp); - GodotLib.touch(4,pointer_idx,evcount,arr); + GodotLib.touch(4, pointer_idx, evcount, arr); //System.out.printf("%d - s.up at: %f,%f\n",pointer_idx, event.getX(pointer_idx),event.getY(pointer_idx)); } break; case MotionEvent.ACTION_POINTER_DOWN: { - int pointer_idx = event.getActionIndex(); - GodotLib.touch(3,pointer_idx,evcount,arr); + GodotLib.touch(3, pointer_idx, evcount, arr); //System.out.printf("%d - s.down at: %f,%f\n",pointer_idx, event.getX(pointer_idx),event.getY(pointer_idx)); } break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: { - GodotLib.touch(2,0,evcount,arr); + GodotLib.touch(2, 0, evcount, arr); /* for(int i=0;i<event.getPointerCount();i++) { System.out.printf("%d - up! %f,%f\n",i, event.getX(i),event.getY(i)); @@ -904,14 +848,16 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC return true; } - @Override public boolean onKeyMultiple(final int inKeyCode, int repeatCount, KeyEvent event) { + @Override + public boolean onKeyMultiple(final int inKeyCode, int repeatCount, KeyEvent event) { String s = event.getCharacters(); if (s == null || s.length() == 0) return super.onKeyMultiple(inKeyCode, repeatCount, event); final char[] cc = s.toCharArray(); int cnt = 0; - for (int i = cc.length; --i >= 0; cnt += cc[i] != 0 ? 1 : 0); + for (int i = cc.length; --i >= 0; cnt += cc[i] != 0 ? 1 : 0) + ; if (cnt == 0) return super.onKeyMultiple(inKeyCode, repeatCount, event); final Activity me = this; queueEvent(new Runnable() { @@ -932,7 +878,6 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC private void queueEvent(Runnable runnable) { // TODO Auto-generated method stub - } public PaymentsManager getPaymentsManager() { @@ -945,7 +890,6 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC } */ - // Audio /** @@ -953,110 +897,95 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC * to show the state as being indeterminate at times. This sample can be * considered a guideline. */ - @Override - public void onDownloadStateChanged(int newState) { - Log.d("GODOT", "onDownloadStateChanged:" + newState); - setState(newState); - boolean showDashboard = true; - boolean showCellMessage = false; - boolean paused; - boolean indeterminate; - switch (newState) { - case IDownloaderClient.STATE_IDLE: - Log.d("GODOT", "STATE IDLE"); - // STATE_IDLE means the service is listening, so it's - // safe to start making calls via mRemoteService. - paused = false; - indeterminate = true; - break; - case IDownloaderClient.STATE_CONNECTING: - case IDownloaderClient.STATE_FETCHING_URL: - Log.d("GODOT", "STATE CONNECTION / FETCHING URL"); - showDashboard = true; - paused = false; - indeterminate = true; - break; - case IDownloaderClient.STATE_DOWNLOADING: - Log.d("GODOT", "STATE DOWNLOADING"); - paused = false; - showDashboard = true; - indeterminate = false; - break; - - case IDownloaderClient.STATE_FAILED_CANCELED: - case IDownloaderClient.STATE_FAILED: - case IDownloaderClient.STATE_FAILED_FETCHING_URL: - case IDownloaderClient.STATE_FAILED_UNLICENSED: - Log.d("GODOT", "MANY TYPES OF FAILING"); - paused = true; - showDashboard = false; - indeterminate = false; - break; - case IDownloaderClient.STATE_PAUSED_NEED_CELLULAR_PERMISSION: - case IDownloaderClient.STATE_PAUSED_WIFI_DISABLED_NEED_CELLULAR_PERMISSION: - Log.d("GODOT", "PAUSED FOR SOME STUPID REASON"); - showDashboard = false; - paused = true; - indeterminate = false; - showCellMessage = true; - break; - - case IDownloaderClient.STATE_PAUSED_BY_REQUEST: - Log.d("GODOT", "PAUSED BY STUPID USER"); - paused = true; - indeterminate = false; - break; - case IDownloaderClient.STATE_PAUSED_ROAMING: - case IDownloaderClient.STATE_PAUSED_SDCARD_UNAVAILABLE: - Log.d("GODOT", "PAUSED BY ROAMING WTF!?"); - paused = true; - indeterminate = false; - break; - case IDownloaderClient.STATE_COMPLETED: - Log.d("GODOT", "COMPLETED"); - showDashboard = false; - paused = false; - indeterminate = false; -// validateXAPKZipFiles(); - initializeGodot(); - return; - default: - Log.d("GODOT", "DEFAULT ????"); - paused = true; - indeterminate = true; - showDashboard = true; - } - int newDashboardVisibility = showDashboard ? View.VISIBLE : View.GONE; - if (mDashboard.getVisibility() != newDashboardVisibility) { - mDashboard.setVisibility(newDashboardVisibility); - } - int cellMessageVisibility = showCellMessage ? View.VISIBLE : View.GONE; - if (mCellMessage.getVisibility() != cellMessageVisibility) { - mCellMessage.setVisibility(cellMessageVisibility); - } - - mPB.setIndeterminate(indeterminate); - setButtonPausedState(paused); - } + @Override + public void onDownloadStateChanged(int newState) { + setState(newState); + boolean showDashboard = true; + boolean showCellMessage = false; + boolean paused; + boolean indeterminate; + switch (newState) { + case IDownloaderClient.STATE_IDLE: + // STATE_IDLE means the service is listening, so it's + // safe to start making calls via mRemoteService. + paused = false; + indeterminate = true; + break; + case IDownloaderClient.STATE_CONNECTING: + case IDownloaderClient.STATE_FETCHING_URL: + showDashboard = true; + paused = false; + indeterminate = true; + break; + case IDownloaderClient.STATE_DOWNLOADING: + paused = false; + showDashboard = true; + indeterminate = false; + break; + + case IDownloaderClient.STATE_FAILED_CANCELED: + case IDownloaderClient.STATE_FAILED: + case IDownloaderClient.STATE_FAILED_FETCHING_URL: + case IDownloaderClient.STATE_FAILED_UNLICENSED: + paused = true; + showDashboard = false; + indeterminate = false; + break; + case IDownloaderClient.STATE_PAUSED_NEED_CELLULAR_PERMISSION: + case IDownloaderClient.STATE_PAUSED_WIFI_DISABLED_NEED_CELLULAR_PERMISSION: + showDashboard = false; + paused = true; + indeterminate = false; + showCellMessage = true; + break; + + case IDownloaderClient.STATE_PAUSED_BY_REQUEST: + paused = true; + indeterminate = false; + break; + case IDownloaderClient.STATE_PAUSED_ROAMING: + case IDownloaderClient.STATE_PAUSED_SDCARD_UNAVAILABLE: + paused = true; + indeterminate = false; + break; + case IDownloaderClient.STATE_COMPLETED: + showDashboard = false; + paused = false; + indeterminate = false; + initializeGodot(); + return; + default: + paused = true; + indeterminate = true; + showDashboard = true; + } + int newDashboardVisibility = showDashboard ? View.VISIBLE : View.GONE; + if (mDashboard.getVisibility() != newDashboardVisibility) { + mDashboard.setVisibility(newDashboardVisibility); + } + int cellMessageVisibility = showCellMessage ? View.VISIBLE : View.GONE; + if (mCellMessage.getVisibility() != cellMessageVisibility) { + mCellMessage.setVisibility(cellMessageVisibility); + } + mPB.setIndeterminate(indeterminate); + setButtonPausedState(paused); + } @Override public void onDownloadProgress(DownloadProgressInfo progress) { mAverageSpeed.setText(getString(com.godot.game.R.string.kilobytes_per_second, - Helpers.getSpeedString(progress.mCurrentSpeed))); - mTimeRemaining.setText(getString(com.godot.game.R.string.time_remaining, - Helpers.getTimeRemaining(progress.mTimeRemaining))); - - progress.mOverallTotal = progress.mOverallTotal; - mPB.setMax((int) (progress.mOverallTotal >> 8)); - mPB.setProgress((int) (progress.mOverallProgress >> 8)); - mProgressPercent.setText(Long.toString(progress.mOverallProgress - * 100 / - progress.mOverallTotal) + "%"); - mProgressFraction.setText(Helpers.getDownloadProgressString - (progress.mOverallProgress, - progress.mOverallTotal)); - + Helpers.getSpeedString(progress.mCurrentSpeed))); + mTimeRemaining.setText(getString(com.godot.game.R.string.time_remaining, + Helpers.getTimeRemaining(progress.mTimeRemaining))); + + progress.mOverallTotal = progress.mOverallTotal; + mPB.setMax((int)(progress.mOverallTotal >> 8)); + mPB.setProgress((int)(progress.mOverallProgress >> 8)); + mProgressPercent.setText(Long.toString(progress.mOverallProgress * 100 / + progress.mOverallTotal) + + "%"); + mProgressFraction.setText(Helpers.getDownloadProgressString(progress.mOverallProgress, + progress.mOverallTotal)); } - } diff --git a/platform/android/java/src/org/godotengine/godot/GodotDownloaderAlarmReceiver.java b/platform/android/java/src/org/godotengine/godot/GodotDownloaderAlarmReceiver.java index 2c668dd586..4701bac9df 100644 --- a/platform/android/java/src/org/godotengine/godot/GodotDownloaderAlarmReceiver.java +++ b/platform/android/java/src/org/godotengine/godot/GodotDownloaderAlarmReceiver.java @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + package org.godotengine.godot; import com.google.android.vending.expansion.downloader.DownloaderClientMarshaller; @@ -46,14 +47,14 @@ import android.util.Log; */ public class GodotDownloaderAlarmReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - Log.d("GODOT", "Alarma recivida"); - try { - DownloaderClientMarshaller.startDownloadServiceIfRequired(context, intent, GodotDownloaderService.class); - } catch (NameNotFoundException e) { - e.printStackTrace(); - Log.d("GODOT", "Exception: " + e.getClass().getName() + ":" + e.getMessage()); + @Override + public void onReceive(Context context, Intent intent) { + Log.d("GODOT", "Alarma recivida"); + try { + DownloaderClientMarshaller.startDownloadServiceIfRequired(context, intent, GodotDownloaderService.class); + } catch (NameNotFoundException e) { + e.printStackTrace(); + Log.d("GODOT", "Exception: " + e.getClass().getName() + ":" + e.getMessage()); + } } - } } diff --git a/platform/android/java/src/org/godotengine/godot/GodotDownloaderService.java b/platform/android/java/src/org/godotengine/godot/GodotDownloaderService.java index 97ba7826fb..3a94354843 100644 --- a/platform/android/java/src/org/godotengine/godot/GodotDownloaderService.java +++ b/platform/android/java/src/org/godotengine/godot/GodotDownloaderService.java @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + package org.godotengine.godot; import android.content.Context; @@ -40,46 +41,45 @@ import com.google.android.vending.expansion.downloader.impl.DownloaderService; * DownloaderService from the Downloader library. */ public class GodotDownloaderService extends DownloaderService { - // stuff for LVL -- MODIFY FOR YOUR APPLICATION! - private static final String BASE64_PUBLIC_KEY = "REPLACE THIS WITH YOUR PUBLIC KEY"; - // used by the preference obfuscater - private static final byte[] SALT = new byte[] { - 1, 43, -12, -1, 54, 98, - -100, -12, 43, 2, -8, -4, 9, 5, -106, -108, -33, 45, -1, 84 - }; + // stuff for LVL -- MODIFY FOR YOUR APPLICATION! + private static final String BASE64_PUBLIC_KEY = "REPLACE THIS WITH YOUR PUBLIC KEY"; + // used by the preference obfuscater + private static final byte[] SALT = new byte[] { + 1, 43, -12, -1, 54, 98, + -100, -12, 43, 2, -8, -4, 9, 5, -106, -108, -33, 45, -1, 84 + }; - /** + /** * This public key comes from your Android Market publisher account, and it * used by the LVL to validate responses from Market on your behalf. */ - @Override - public String getPublicKey() { - SharedPreferences prefs = getApplicationContext().getSharedPreferences("app_data_keys", Context.MODE_PRIVATE); - Log.d("GODOT", "getting public key:" + prefs.getString("store_public_key", null)); - return prefs.getString("store_public_key", null); - - //return BASE64_PUBLIC_KEY; - } + @Override + public String getPublicKey() { + SharedPreferences prefs = getApplicationContext().getSharedPreferences("app_data_keys", Context.MODE_PRIVATE); + Log.d("GODOT", "getting public key:" + prefs.getString("store_public_key", null)); + return prefs.getString("store_public_key", null); + + //return BASE64_PUBLIC_KEY; + } - /** + /** * This is used by the preference obfuscater to make sure that your * obfuscated preferences are different than the ones used by other * applications. */ - @Override - public byte[] getSALT() { - return SALT; - } + @Override + public byte[] getSALT() { + return SALT; + } - /** + /** * Fill this in with the class name for your alarm receiver. We do this * because receivers must be unique across all of Android (it's a good idea * to make sure that your receiver is in your unique package) */ - @Override - public String getAlarmReceiverClassName() { - Log.d("GODOT", "getAlarmReceiverClassName()"); - return GodotDownloaderAlarmReceiver.class.getName(); - } - + @Override + public String getAlarmReceiverClassName() { + Log.d("GODOT", "getAlarmReceiverClassName()"); + return GodotDownloaderAlarmReceiver.class.getName(); + } } diff --git a/platform/android/java/src/org/godotengine/godot/GodotIO.java b/platform/android/java/src/org/godotengine/godot/GodotIO.java index 989fd2b609..a95c508d21 100644 --- a/platform/android/java/src/org/godotengine/godot/GodotIO.java +++ b/platform/android/java/src/org/godotengine/godot/GodotIO.java @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + package org.godotengine.godot; import java.util.HashMap; import java.util.Locale; @@ -56,7 +57,6 @@ import org.godotengine.godot.input.*; public class GodotIO { - AssetManager am; Godot activity; GodotEditText edit; @@ -64,35 +64,32 @@ public class GodotIO { Context applicationContext; MediaPlayer mediaPlayer; - final int SCREEN_LANDSCAPE=0; - final int SCREEN_PORTRAIT=1; - final int SCREEN_REVERSE_LANDSCAPE=2; - final int SCREEN_REVERSE_PORTRAIT=3; - final int SCREEN_SENSOR_LANDSCAPE=4; - final int SCREEN_SENSOR_PORTRAIT=5; - final int SCREEN_SENSOR=6; + final int SCREEN_LANDSCAPE = 0; + final int SCREEN_PORTRAIT = 1; + final int SCREEN_REVERSE_LANDSCAPE = 2; + final int SCREEN_REVERSE_PORTRAIT = 3; + final int SCREEN_SENSOR_LANDSCAPE = 4; + final int SCREEN_SENSOR_PORTRAIT = 5; + final int SCREEN_SENSOR = 6; ///////////////////////// /// FILES ///////////////////////// - public int last_file_id=1; + public int last_file_id = 1; class AssetData { - - public boolean eof=false; + public boolean eof = false; public String path; public InputStream is; public int len; public int pos; } + HashMap<Integer, AssetData> streams; - HashMap<Integer,AssetData> streams; - - - public int file_open(String path,boolean write) { + public int file_open(String path, boolean write) { //System.out.printf("file_open: Attempt to Open %s\n",path); @@ -100,7 +97,6 @@ public class GodotIO { if (write) return -1; - AssetData ad = new AssetData(); try { @@ -113,76 +109,73 @@ public class GodotIO { } try { - ad.len=ad.is.available(); + ad.len = ad.is.available(); } catch (Exception e) { - System.out.printf("Exception availabling on file_open: %s\n",path); + System.out.printf("Exception availabling on file_open: %s\n", path); return -1; } - ad.path=path; - ad.pos=0; + ad.path = path; + ad.pos = 0; ++last_file_id; - streams.put(last_file_id,ad); + streams.put(last_file_id, ad); return last_file_id; } public int file_get_size(int id) { if (!streams.containsKey(id)) { - System.out.printf("file_get_size: Invalid file id: %d\n",id); + System.out.printf("file_get_size: Invalid file id: %d\n", id); return -1; } return streams.get(id).len; - } - public void file_seek(int id,int bytes) { + public void file_seek(int id, int bytes) { if (!streams.containsKey(id)) { - System.out.printf("file_get_size: Invalid file id: %d\n",id); + System.out.printf("file_get_size: Invalid file id: %d\n", id); return; } //seek sucks AssetData ad = streams.get(id); - if (bytes>ad.len) - bytes=ad.len; - if (bytes<0) - bytes=0; + if (bytes > ad.len) + bytes = ad.len; + if (bytes < 0) + bytes = 0; try { - if (bytes > (int)ad.pos) { - int todo=bytes-(int)ad.pos; - while(todo>0) { - todo-=ad.is.skip(todo); - } - ad.pos=bytes; - } else if (bytes<(int)ad.pos) { + if (bytes > (int)ad.pos) { + int todo = bytes - (int)ad.pos; + while (todo > 0) { + todo -= ad.is.skip(todo); + } + ad.pos = bytes; + } else if (bytes < (int)ad.pos) { - ad.is=am.open(ad.path); + ad.is = am.open(ad.path); - ad.pos=bytes; - int todo=bytes; - while(todo>0) { - todo-=ad.is.skip(todo); + ad.pos = bytes; + int todo = bytes; + while (todo > 0) { + todo -= ad.is.skip(todo); + } } - } - ad.eof=false; + ad.eof = false; } catch (IOException e) { - System.out.printf("Exception on file_seek: %s\n",e); + System.out.printf("Exception on file_seek: %s\n", e); return; } - - } public int file_tell(int id) { if (!streams.containsKey(id)) { - System.out.printf("file_read: Can't tell eof for invalid file id: %d\n",id); + System.out.printf("file_read: Can't tell eof for invalid file id: %d\n", id); return 0; } @@ -192,7 +185,7 @@ public class GodotIO { public boolean file_eof(int id) { if (!streams.containsKey(id)) { - System.out.printf("file_read: Can't check eof for invalid file id: %d\n",id); + System.out.printf("file_read: Can't check eof for invalid file id: %d\n", id); return false; } @@ -203,73 +196,65 @@ public class GodotIO { public byte[] file_read(int id, int bytes) { if (!streams.containsKey(id)) { - System.out.printf("file_read: Can't read invalid file id: %d\n",id); + System.out.printf("file_read: Can't read invalid file id: %d\n", id); return new byte[0]; } - AssetData ad = streams.get(id); if (ad.pos + bytes > ad.len) { - bytes=ad.len-ad.pos; - ad.eof=true; + bytes = ad.len - ad.pos; + ad.eof = true; } - - if (bytes==0) { + if (bytes == 0) { return new byte[0]; } - - - byte[] buf1=new byte[bytes]; - int r=0; + byte[] buf1 = new byte[bytes]; + int r = 0; try { r = ad.is.read(buf1); } catch (IOException e) { - System.out.printf("Exception on file_read: %s\n",e); + System.out.printf("Exception on file_read: %s\n", e); return new byte[bytes]; } - if (r==0) { + if (r == 0) { return new byte[0]; } - ad.pos+=r; + ad.pos += r; - if (r<bytes) { + if (r < bytes) { - byte[] buf2=new byte[r]; - for(int i=0;i<r;i++) - buf2[i]=buf1[i]; + byte[] buf2 = new byte[r]; + for (int i = 0; i < r; i++) + buf2[i] = buf1[i]; return buf2; } else { return buf1; } - } public void file_close(int id) { if (!streams.containsKey(id)) { - System.out.printf("file_close: Can't close invalid file id: %d\n",id); + System.out.printf("file_close: Can't close invalid file id: %d\n", id); return; } streams.remove(id); - } - ///////////////////////// /// DIRECTORIES ///////////////////////// - class AssetDir { public String[] files; @@ -277,49 +262,48 @@ public class GodotIO { public String path; } - public int last_dir_id=1; + public int last_dir_id = 1; - HashMap<Integer,AssetDir> dirs; + HashMap<Integer, AssetDir> dirs; public int dir_open(String path) { AssetDir ad = new AssetDir(); - ad.current=0; - ad.path=path; + ad.current = 0; + ad.path = path; try { ad.files = am.list(path); // no way to find path is directory or file exactly. // but if ad.files.length==0, then it's an empty directory or file. - if (ad.files.length==0) { + if (ad.files.length == 0) { return -1; } } catch (IOException e) { - System.out.printf("Exception on dir_open: %s\n",e); + System.out.printf("Exception on dir_open: %s\n", e); return -1; } //System.out.printf("Opened dir: %s\n",path); ++last_dir_id; - dirs.put(last_dir_id,ad); + dirs.put(last_dir_id, ad); return last_dir_id; - } public boolean dir_is_dir(int id) { if (!dirs.containsKey(id)) { - System.out.printf("dir_next: invalid dir id: %d\n",id); + System.out.printf("dir_next: invalid dir id: %d\n", id); return false; } AssetDir ad = dirs.get(id); //System.out.printf("go next: %d,%d\n",ad.current,ad.files.length); int idx = ad.current; - if (idx>0) + if (idx > 0) idx--; - if (idx>=ad.files.length) + if (idx >= ad.files.length) return false; String fname = ad.files[idx]; @@ -327,7 +311,7 @@ public class GodotIO { if (ad.path.equals("")) am.open(fname); else - am.open(ad.path+"/"+fname); + am.open(ad.path + "/" + fname); return false; } catch (Exception e) { return true; @@ -337,46 +321,41 @@ public class GodotIO { public String dir_next(int id) { if (!dirs.containsKey(id)) { - System.out.printf("dir_next: invalid dir id: %d\n",id); + System.out.printf("dir_next: invalid dir id: %d\n", id); return ""; } AssetDir ad = dirs.get(id); //System.out.printf("go next: %d,%d\n",ad.current,ad.files.length); - if (ad.current>=ad.files.length) { + if (ad.current >= ad.files.length) { ad.current++; return ""; } String r = ad.files[ad.current]; ad.current++; return r; - } public void dir_close(int id) { if (!dirs.containsKey(id)) { - System.out.printf("dir_close: invalid dir id: %d\n",id); + System.out.printf("dir_close: invalid dir id: %d\n", id); return; } dirs.remove(id); } - - GodotIO(Godot p_activity) { - am=p_activity.getAssets(); - activity=p_activity; - streams=new HashMap<Integer,AssetData>(); - dirs=new HashMap<Integer,AssetDir>(); + am = p_activity.getAssets(); + activity = p_activity; + streams = new HashMap<Integer, AssetData>(); + dirs = new HashMap<Integer, AssetDir>(); applicationContext = activity.getApplicationContext(); - } - ///////////////////////// // AUDIO ///////////////////////// @@ -400,7 +379,7 @@ public class GodotIO { desiredFrames = Math.max(desiredFrames, (AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize); mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, - channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM); + channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM); audioStartThread(); @@ -412,10 +391,10 @@ public class GodotIO { public void audioStartThread() { mAudioThread = new Thread(new Runnable() { - public void run() { - mAudioTrack.play(); - GodotLib.audio(); - } + public void run() { + mAudioTrack.play(); + GodotLib.audio(); + } }); // I'd take REALTIME if I could get it! @@ -424,15 +403,15 @@ public class GodotIO { } public void audioWriteShortBuffer(short[] buffer) { - for (int i = 0; i < buffer.length; ) { + for (int i = 0; i < buffer.length;) { int result = mAudioTrack.write(buffer, i, buffer.length - i); if (result > 0) { i += result; } else if (result == 0) { try { - Thread.sleep(1); - } catch(InterruptedException e) { - // Nom nom + Thread.sleep(1); + } catch (InterruptedException e) { + // Nom nom } } else { Log.w("Godot", "Godot audio: error return from write(short)"); @@ -441,18 +420,16 @@ public class GodotIO { } } - - public void audioQuit() { if (mAudioThread != null) { try { mAudioThread.join(); - } catch(Exception e) { + } catch (Exception e) { Log.v("Godot", "Problem stopping audio thread: " + e); } mAudioThread = null; - //Log.v("Godot", "Finished waiting for audio thread"); + //Log.v("Godot", "Finished waiting for audio thread"); } if (mAudioTrack != null) { @@ -473,20 +450,18 @@ public class GodotIO { // MISCELLANEOUS OS IO ///////////////////////// - - public int openURI(String p_uri) { try { Log.v("MyApp", "TRYING TO OPEN URI: " + p_uri); String path = p_uri; - String type=""; + String type = ""; if (path.startsWith("/")) { //absolute path to filesystem, prepend file:// - path="file://"+path; + path = "file://" + path; if (p_uri.endsWith(".png") || p_uri.endsWith(".jpg") || p_uri.endsWith(".gif") || p_uri.endsWith(".webp")) { - type="image/*"; + type = "image/*"; } } @@ -531,7 +506,7 @@ public class GodotIO { } public void showKeyboard(String p_existing_text) { - if(edit != null) + if (edit != null) edit.showKeyboard(p_existing_text); //InputMethodManager inputMgr = (InputMethodManager)activity.getSystemService(Context.INPUT_METHOD_SERVICE); @@ -539,21 +514,21 @@ public class GodotIO { }; public void hideKeyboard() { - if(edit != null) + if (edit != null) edit.hideKeyboard(); - InputMethodManager inputMgr = (InputMethodManager)activity.getSystemService(Context.INPUT_METHOD_SERVICE); - View v = activity.getCurrentFocus(); - if (v != null) { - inputMgr.hideSoftInputFromWindow(v.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); - } else { - inputMgr.hideSoftInputFromWindow(new View(activity).getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); - } + InputMethodManager inputMgr = (InputMethodManager)activity.getSystemService(Context.INPUT_METHOD_SERVICE); + View v = activity.getCurrentFocus(); + if (v != null) { + inputMgr.hideSoftInputFromWindow(v.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); + } else { + inputMgr.hideSoftInputFromWindow(new View(activity).getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); + } }; public void setScreenOrientation(int p_orientation) { - switch(p_orientation) { + switch (p_orientation) { case SCREEN_LANDSCAPE: { activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); @@ -576,16 +551,14 @@ public class GodotIO { case SCREEN_SENSOR: { activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); } break; - } }; - + public void setEdit(GodotEditText _edit) { edit = _edit; } - public void playVideo(String p_path) - { + public void playVideo(String p_path) { Uri filePath = Uri.parse(p_path); mediaPlayer = new MediaPlayer(); @@ -594,11 +567,9 @@ public class GodotIO { mediaPlayer.setDataSource(applicationContext, filePath); mediaPlayer.prepare(); mediaPlayer.start(); + } catch (IOException e) { + System.out.println("IOError while playing video"); } - catch(IOException e) - { - System.out.println("IOError while playing video"); - } } public boolean isVideoPlaying() { @@ -621,49 +592,47 @@ public class GodotIO { } } - - public static final int SYSTEM_DIR_DESKTOP=0; - public static final int SYSTEM_DIR_DCIM=1; - public static final int SYSTEM_DIR_DOCUMENTS=2; - public static final int SYSTEM_DIR_DOWNLOADS=3; - public static final int SYSTEM_DIR_MOVIES=4; - public static final int SYSTEM_DIR_MUSIC=5; - public static final int SYSTEM_DIR_PICTURES=6; - public static final int SYSTEM_DIR_RINGTONES=7; - + public static final int SYSTEM_DIR_DESKTOP = 0; + public static final int SYSTEM_DIR_DCIM = 1; + public static final int SYSTEM_DIR_DOCUMENTS = 2; + public static final int SYSTEM_DIR_DOWNLOADS = 3; + public static final int SYSTEM_DIR_MOVIES = 4; + public static final int SYSTEM_DIR_MUSIC = 5; + public static final int SYSTEM_DIR_PICTURES = 6; + public static final int SYSTEM_DIR_RINGTONES = 7; public String getSystemDir(int idx) { - String what=""; - switch(idx) { + String what = ""; + switch (idx) { case SYSTEM_DIR_DESKTOP: { //what=Environment.DIRECTORY_DOCUMENTS; - what=Environment.DIRECTORY_DOWNLOADS; + what = Environment.DIRECTORY_DOWNLOADS; } break; case SYSTEM_DIR_DCIM: { - what=Environment.DIRECTORY_DCIM; + what = Environment.DIRECTORY_DCIM; } break; case SYSTEM_DIR_DOCUMENTS: { - what=Environment.DIRECTORY_DOWNLOADS; + what = Environment.DIRECTORY_DOWNLOADS; //what=Environment.DIRECTORY_DOCUMENTS; } break; case SYSTEM_DIR_DOWNLOADS: { - what=Environment.DIRECTORY_DOWNLOADS; + what = Environment.DIRECTORY_DOWNLOADS; } break; case SYSTEM_DIR_MOVIES: { - what=Environment.DIRECTORY_MOVIES; + what = Environment.DIRECTORY_MOVIES; } break; case SYSTEM_DIR_MUSIC: { - what=Environment.DIRECTORY_MUSIC; + what = Environment.DIRECTORY_MUSIC; } break; case SYSTEM_DIR_PICTURES: { - what=Environment.DIRECTORY_PICTURES; + what = Environment.DIRECTORY_PICTURES; } break; case SYSTEM_DIR_RINGTONES: { - what=Environment.DIRECTORY_RINGTONES; + what = Environment.DIRECTORY_RINGTONES; } break; } @@ -676,10 +645,9 @@ public class GodotIO { protected static final String PREFS_FILE = "device_id.xml"; protected static final String PREFS_DEVICE_ID = "device_id"; - public static String unique_id=""; + public static String unique_id = ""; public String getUniqueID() { - return unique_id; + return unique_id; } - } diff --git a/platform/android/java/src/org/godotengine/godot/GodotLib.java b/platform/android/java/src/org/godotengine/godot/GodotLib.java index e0ed4cd38c..45eb188327 100644 --- a/platform/android/java/src/org/godotengine/godot/GodotLib.java +++ b/platform/android/java/src/org/godotengine/godot/GodotLib.java @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,48 +27,48 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + package org.godotengine.godot; // Wrapper for native library public class GodotLib { + public static GodotIO io; - public static GodotIO io; - - static { - System.loadLibrary("godot_android"); - } + static { + System.loadLibrary("godot_android"); + } - /** + /** * @param width the current view width * @param height the current view height */ - public static native void initialize(Godot p_instance,boolean need_reload_hook,Object p_asset_manager, boolean use_apk_expansion); - public static native void setup(String[] p_cmdline); - public static native void resize(int width, int height,boolean reload); - public static native void newcontext(boolean p_32_bits); - public static native void back(); - public static native void step(); - public static native void touch(int what,int pointer,int howmany, int[] arr); - public static native void accelerometer(float x, float y, float z); - public static native void magnetometer(float x, float y, float z); - public static native void gyroscope(float x, float y, float z); - public static native void key(int p_scancode, int p_unicode_char, boolean p_pressed); - public static native void joybutton(int p_device, int p_but, boolean p_pressed); - public static native void joyaxis(int p_device, int p_axis, float p_value); - public static native void joyhat(int p_device, int p_hat_x, int p_hat_y); - public static native void joyconnectionchanged(int p_device, boolean p_connected, String p_name); - public static native void focusin(); - public static native void focusout(); - public static native void audio(); - public static native void singleton(String p_name,Object p_object); - public static native void method(String p_sname,String p_name,String p_ret,String[] p_params); - public static native String getGlobal(String p_key); + public static native void initialize(Godot p_instance, boolean need_reload_hook, Object p_asset_manager, boolean use_apk_expansion); + public static native void setup(String[] p_cmdline); + public static native void resize(int width, int height, boolean reload); + public static native void newcontext(boolean p_32_bits); + public static native void back(); + public static native void step(); + public static native void touch(int what, int pointer, int howmany, int[] arr); + public static native void accelerometer(float x, float y, float z); + public static native void gravity(float x, float y, float z); + public static native void magnetometer(float x, float y, float z); + public static native void gyroscope(float x, float y, float z); + public static native void key(int p_scancode, int p_unicode_char, boolean p_pressed); + public static native void joybutton(int p_device, int p_but, boolean p_pressed); + public static native void joyaxis(int p_device, int p_axis, float p_value); + public static native void joyhat(int p_device, int p_hat_x, int p_hat_y); + public static native void joyconnectionchanged(int p_device, boolean p_connected, String p_name); + public static native void focusin(); + public static native void focusout(); + public static native void audio(); + public static native void singleton(String p_name, Object p_object); + public static native void method(String p_sname, String p_name, String p_ret, String[] p_params); + public static native String getGlobal(String p_key); public static native void callobject(int p_ID, String p_method, Object[] p_params); public static native void calldeferred(int p_ID, String p_method, Object[] p_params); public static native void setVirtualKeyboardHeight(int p_height); - } diff --git a/platform/android/java/src/org/godotengine/godot/GodotPaymentV3.java b/platform/android/java/src/org/godotengine/godot/GodotPaymentV3.java index 8fe79fdfc7..d72c590378 100644 --- a/platform/android/java/src/org/godotengine/godot/GodotPaymentV3.java +++ b/platform/android/java/src/org/godotengine/godot/GodotPaymentV3.java @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + package org.godotengine.godot; import android.app.Activity; @@ -40,7 +41,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; - public class GodotPaymentV3 extends Godot.SingletonBase { private Godot activity; @@ -67,8 +67,8 @@ public class GodotPaymentV3 extends Godot.SingletonBase { public GodotPaymentV3(Activity p_activity) { - registerClass("GodotPayments", new String[]{"purchase", "setPurchaseCallbackId", "setPurchaseValidationUrlPrefix", "setTransactionId", "getSignature", "consumeUnconsumedPurchases", "requestPurchased", "setAutoConsume", "consume", "querySkuDetails"}); - activity = (Godot) p_activity; + registerClass("GodotPayments", new String[] { "purchase", "setPurchaseCallbackId", "setPurchaseValidationUrlPrefix", "setTransactionId", "getSignature", "consumeUnconsumedPurchases", "requestPurchased", "setAutoConsume", "consume", "querySkuDetails", "isConnected" }); + activity = (Godot)p_activity; mPaymentManager = activity.getPaymentsManager(); mPaymentManager.setBaseSingleton(this); } @@ -89,32 +89,32 @@ public class GodotPaymentV3 extends Godot.SingletonBase { } public void callbackSuccess(String ticket, String signature, String sku) { - GodotLib.calldeferred(purchaseCallbackId, "purchase_success", new Object[]{ticket, signature, sku}); + GodotLib.calldeferred(purchaseCallbackId, "purchase_success", new Object[] { ticket, signature, sku }); } public void callbackSuccessProductMassConsumed(String ticket, String signature, String sku) { Log.d(this.getClass().getName(), "callbackSuccessProductMassConsumed > " + ticket + "," + signature + "," + sku); - GodotLib.calldeferred(purchaseCallbackId, "consume_success", new Object[]{ticket, signature, sku}); + GodotLib.calldeferred(purchaseCallbackId, "consume_success", new Object[] { ticket, signature, sku }); } public void callbackSuccessNoUnconsumedPurchases() { - GodotLib.calldeferred(purchaseCallbackId, "consume_not_required", new Object[]{}); + GodotLib.calldeferred(purchaseCallbackId, "consume_not_required", new Object[] {}); } public void callbackFailConsume() { - GodotLib.calldeferred(purchaseCallbackId, "consume_fail", new Object[]{}); + GodotLib.calldeferred(purchaseCallbackId, "consume_fail", new Object[] {}); } public void callbackFail() { - GodotLib.calldeferred(purchaseCallbackId, "purchase_fail", new Object[]{}); + GodotLib.calldeferred(purchaseCallbackId, "purchase_fail", new Object[] {}); } public void callbackCancel() { - GodotLib.calldeferred(purchaseCallbackId, "purchase_cancel", new Object[]{}); + GodotLib.calldeferred(purchaseCallbackId, "purchase_cancel", new Object[] {}); } public void callbackAlreadyOwned(String sku) { - GodotLib.calldeferred(purchaseCallbackId, "purchase_owned", new Object[]{sku}); + GodotLib.calldeferred(purchaseCallbackId, "purchase_owned", new Object[] { sku }); } public int getPurchaseCallbackId() { @@ -161,7 +161,20 @@ public class GodotPaymentV3 extends Godot.SingletonBase { // callback for requestPurchased() public void callbackPurchased(String receipt, String signature, String sku) { - GodotLib.calldeferred(purchaseCallbackId, "has_purchased", new Object[]{receipt, signature, sku}); + GodotLib.calldeferred(purchaseCallbackId, "has_purchased", new Object[] { receipt, signature, sku }); + } + + public void callbackDisconnected() { + GodotLib.calldeferred(purchaseCallbackId, "iap_disconnected", new Object[]{}); + } + + public void callbackConnected() { + GodotLib.calldeferred(purchaseCallbackId, "iap_connected", new Object[]{}); + } + + // true if connected, false otherwise + public boolean isConnected() { + return mPaymentManager.isConnected(); } // consume item automatically after purchase. default is true. @@ -210,10 +223,10 @@ public class GodotPaymentV3 extends Godot.SingletonBase { } public void completeSkuDetail() { - GodotLib.calldeferred(purchaseCallbackId, "sku_details_complete", new Object[]{mSkuDetails}); + GodotLib.calldeferred(purchaseCallbackId, "sku_details_complete", new Object[] { mSkuDetails }); } public void errorSkuDetail(String errorMessage) { - GodotLib.calldeferred(purchaseCallbackId, "sku_details_error", new Object[]{errorMessage}); + GodotLib.calldeferred(purchaseCallbackId, "sku_details_error", new Object[] { errorMessage }); } } diff --git a/platform/android/java/src/org/godotengine/godot/GodotView.java b/platform/android/java/src/org/godotengine/godot/GodotView.java index b807b952d4..0222758c2b 100644 --- a/platform/android/java/src/org/godotengine/godot/GodotView.java +++ b/platform/android/java/src/org/godotengine/godot/GodotView.java @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + package org.godotengine.godot; import android.content.Context; import android.graphics.PixelFormat; @@ -77,20 +78,19 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { private static Context ctx; private static GodotIO io; - private static boolean firsttime=true; - private static boolean use_gl3=false; - private static boolean use_32=false; + private static boolean firsttime = true; + private static boolean use_gl3 = false; + private static boolean use_32 = false; private Godot activity; - private InputManagerCompat mInputManager; - public GodotView(Context context,GodotIO p_io,boolean p_use_gl3, boolean p_use_32_bits, Godot p_activity) { + public GodotView(Context context, GodotIO p_io, boolean p_use_gl3, boolean p_use_32_bits, Godot p_activity) { super(context); - ctx=context; - io=p_io; - use_gl3=p_use_gl3; - use_32=p_use_32_bits; + ctx = context; + io = p_io; + use_gl3 = p_use_gl3; + use_32 = p_use_32_bits; activity = p_activity; @@ -101,15 +101,16 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { mInputManager = InputManagerCompat.Factory.getInputManager(this.getContext()); mInputManager.registerInputDeviceListener(this, null); init(false, 16, 0); - } + } - public GodotView(Context context, boolean translucent, int depth, int stencil) { + public GodotView(Context context, boolean translucent, int depth, int stencil) { super(context); init(translucent, depth, stencil); - } - - @Override public boolean onTouchEvent (MotionEvent event) { + } + @Override + public boolean onTouchEvent(MotionEvent event) { + super.onTouchEvent(event); return activity.gotTouchEvent(event); }; @@ -196,16 +197,17 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { ArrayList<joystick> joy_devices = new ArrayList<joystick>(); private int find_joy_device(int device_id) { - for (int i=0; i<joy_devices.size(); i++) { + for (int i = 0; i < joy_devices.size(); i++) { if (joy_devices.get(i).device_id == device_id) { - return i; + return i; } } onInputDeviceAdded(device_id); return joy_devices.size() - 1; } - @Override public void onInputDeviceAdded(int deviceId) { + @Override + public void onInputDeviceAdded(int deviceId) { joystick joy = new joystick(); joy.device_id = deviceId; final int id = joy_devices.size(); @@ -219,8 +221,7 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { for (InputDevice.MotionRange range : ranges) { if (range.getAxis() == MotionEvent.AXIS_HAT_X || range.getAxis() == MotionEvent.AXIS_HAT_Y) { joy.hats.add(range); - } - else { + } else { joy.axes.add(range); } } @@ -231,9 +232,10 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { GodotLib.joyconnectionchanged(id, true, name); } }); - } + } - @Override public void onInputDeviceRemoved(int deviceId) { + @Override + public void onInputDeviceRemoved(int deviceId) { final int id = find_joy_device(deviceId); joy_devices.remove(id); queueEvent(new Runnable() { @@ -244,10 +246,11 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { }); } - @Override public void onInputDeviceChanged(int deviceId) { - + @Override + public void onInputDeviceChanged(int deviceId) { } - @Override public boolean onKeyUp(final int keyCode, KeyEvent event) { + @Override + public boolean onKeyUp(final int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { return true; @@ -282,7 +285,8 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { return super.onKeyUp(keyCode, event); }; - @Override public boolean onKeyDown(final int keyCode, KeyEvent event) { + @Override + public boolean onKeyDown(final int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { activity.onBackPressed(); @@ -326,7 +330,8 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { return super.onKeyDown(keyCode, event); } - @Override public boolean onGenericMotionEvent(MotionEvent event) { + @Override + public boolean onGenericMotionEvent(MotionEvent event) { if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK && event.getAction() == MotionEvent.ACTION_MOVE) { @@ -335,7 +340,7 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { for (int i = 0; i < joy.axes.size(); i++) { InputDevice.MotionRange range = joy.axes.get(i); - final float value = (event.getAxisValue(range.getAxis()) - range.getMin() ) / range.getRange() * 2.0f - 1.0f; + final float value = (event.getAxisValue(range.getAxis()) - range.getMin()) / range.getRange() * 2.0f - 1.0f; //Log.e(TAG, String.format("axis event: %d, value %f", i, value)); final int idx = i; queueEvent(new Runnable() { @@ -346,9 +351,9 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { }); } - for (int i = 0; i < joy.hats.size(); i+=2) { + for (int i = 0; i < joy.hats.size(); i += 2) { final int hatX = Math.round(event.getAxisValue(joy.hats.get(i).getAxis())); - final int hatY = Math.round(event.getAxisValue(joy.hats.get(i+1).getAxis())); + final int hatY = Math.round(event.getAxisValue(joy.hats.get(i + 1).getAxis())); //Log.e(TAG, String.format("HAT EVENT %d, %d", hatX, hatY)); queueEvent(new Runnable() { @Override @@ -363,8 +368,7 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { return super.onGenericMotionEvent(event); }; - - private void init(boolean translucent, int depth, int stencil) { + private void init(boolean translucent, int depth, int stencil) { this.setFocusableInTouchMode(true); /* By default, GLSurfaceView() creates a RGB_565 opaque surface. @@ -388,14 +392,14 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { */ if (use_32) { - setEGLConfigChooser( translucent ? - new FallbackConfigChooser(8, 8, 8, 8, 24, stencil, new ConfigChooser(8, 8, 8, 8, 16, stencil)) : - new FallbackConfigChooser(8, 8, 8, 8, 24, stencil, new ConfigChooser(5, 6, 5, 0, 16, stencil)) ); + setEGLConfigChooser(translucent ? + new FallbackConfigChooser(8, 8, 8, 8, 24, stencil, new ConfigChooser(8, 8, 8, 8, 16, stencil)) : + new FallbackConfigChooser(8, 8, 8, 8, 24, stencil, new ConfigChooser(5, 6, 5, 0, 16, stencil))); } else { - setEGLConfigChooser( translucent ? - new ConfigChooser(8, 8, 8, 8, 16, stencil) : - new ConfigChooser(5, 6, 5, 0, 16, stencil) ); + setEGLConfigChooser(translucent ? + new ConfigChooser(8, 8, 8, 8, 16, stencil) : + new ConfigChooser(5, 6, 5, 0, 16, stencil)); } /* Set the renderer responsible for frame rendering */ @@ -403,33 +407,33 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { } private static class ContextFactory implements GLSurfaceView.EGLContextFactory { - private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098; - public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) { - if (use_gl3) - Log.w(TAG, "creating OpenGL ES 3.0 context :"); - else - Log.w(TAG, "creating OpenGL ES 2.0 context :"); - - checkEglError("Before eglCreateContext", egl); - int[] attrib_list2 = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE }; - int[] attrib_list3 = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL10.EGL_NONE }; - EGLContext context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, use_gl3?attrib_list3:attrib_list2); - checkEglError("After eglCreateContext", egl); - return context; - } + private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098; + public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) { + if (use_gl3) + Log.w(TAG, "creating OpenGL ES 3.0 context :"); + else + Log.w(TAG, "creating OpenGL ES 2.0 context :"); + + checkEglError("Before eglCreateContext", egl); + int[] attrib_list2 = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE }; + int[] attrib_list3 = { EGL_CONTEXT_CLIENT_VERSION, 3, EGL10.EGL_NONE }; + EGLContext context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, use_gl3 ? attrib_list3 : attrib_list2); + checkEglError("After eglCreateContext", egl); + return context; + } - public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) { - egl.eglDestroyContext(display, context); + public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) { + egl.eglDestroyContext(display, context); + } } - } - private static void checkEglError(String prompt, EGL10 egl) { - int error; - while ((error = egl.eglGetError()) != EGL10.EGL_SUCCESS) { - Log.e(TAG, String.format("%s: EGL error: 0x%x", prompt, error)); + private static void checkEglError(String prompt, EGL10 egl) { + int error; + while ((error = egl.eglGetError()) != EGL10.EGL_SUCCESS) { + Log.e(TAG, String.format("%s: EGL error: 0x%x", prompt, error)); + } } - } - /* Fallback if 32bit View is not supported*/ + /* Fallback if 32bit View is not supported*/ private static class FallbackConfigChooser extends ConfigChooser { private ConfigChooser fallback; @@ -438,17 +442,17 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { this.fallback = fallback; } - @Override + @Override public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs) { EGLConfig ec = super.chooseConfig(egl, display, configs); if (ec == null) { - Log.w(TAG, "Trying ConfigChooser fallback"); - ec = fallback.chooseConfig(egl, display, configs); - use_32=false; + Log.w(TAG, "Trying ConfigChooser fallback"); + ec = fallback.chooseConfig(egl, display, configs); + use_32 = false; } return ec; - } - } + } + } private static class ConfigChooser implements GLSurfaceView.EGLConfigChooser { @@ -467,46 +471,46 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { */ private static int EGL_OPENGL_ES2_BIT = 4; private static int[] s_configAttribs2 = - { - EGL10.EGL_RED_SIZE, 4, - EGL10.EGL_GREEN_SIZE, 4, - EGL10.EGL_BLUE_SIZE, 4, - // EGL10.EGL_DEPTH_SIZE, 16, - // EGL10.EGL_STENCIL_SIZE, EGL10.EGL_DONT_CARE, - EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL10.EGL_NONE - }; + { + EGL10.EGL_RED_SIZE, 4, + EGL10.EGL_GREEN_SIZE, 4, + EGL10.EGL_BLUE_SIZE, 4, + // EGL10.EGL_DEPTH_SIZE, 16, + // EGL10.EGL_STENCIL_SIZE, EGL10.EGL_DONT_CARE, + EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL10.EGL_NONE + }; private static int[] s_configAttribs3 = - { - EGL10.EGL_RED_SIZE, 4, - EGL10.EGL_GREEN_SIZE, 4, - EGL10.EGL_BLUE_SIZE, 4, - // EGL10.EGL_DEPTH_SIZE, 16, - // EGL10.EGL_STENCIL_SIZE, EGL10.EGL_DONT_CARE, - EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, //apparently there is no EGL_OPENGL_ES3_BIT - EGL10.EGL_NONE - }; + { + EGL10.EGL_RED_SIZE, 4, + EGL10.EGL_GREEN_SIZE, 4, + EGL10.EGL_BLUE_SIZE, 4, + // EGL10.EGL_DEPTH_SIZE, 16, + // EGL10.EGL_STENCIL_SIZE, EGL10.EGL_DONT_CARE, + EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, //apparently there is no EGL_OPENGL_ES3_BIT + EGL10.EGL_NONE + }; public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { /* Get the number of minimally matching EGL configurations */ int[] num_config = new int[1]; - egl.eglChooseConfig(display, use_gl3?s_configAttribs3:s_configAttribs2, null, 0, num_config); + egl.eglChooseConfig(display, use_gl3 ? s_configAttribs3 : s_configAttribs2, null, 0, num_config); int numConfigs = num_config[0]; if (numConfigs <= 0) { - throw new IllegalArgumentException("No configs match configSpec"); + throw new IllegalArgumentException("No configs match configSpec"); } /* Allocate then read the array of minimally matching EGL configs */ EGLConfig[] configs = new EGLConfig[numConfigs]; - egl.eglChooseConfig(display, use_gl3?s_configAttribs3:s_configAttribs2, configs, numConfigs, num_config); + egl.eglChooseConfig(display, use_gl3 ? s_configAttribs3 : s_configAttribs2, configs, numConfigs, num_config); if (DEBUG) { - printConfigs(egl, display, configs); + printConfigs(egl, display, configs); } /* Now return the "best" one */ @@ -514,54 +518,54 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { } public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, - EGLConfig[] configs) { - for(EGLConfig config : configs) { - int d = findConfigAttrib(egl, display, config, - EGL10.EGL_DEPTH_SIZE, 0); - int s = findConfigAttrib(egl, display, config, - EGL10.EGL_STENCIL_SIZE, 0); - - // We need at least mDepthSize and mStencilSize bits - if (d < mDepthSize || s < mStencilSize) - continue; - - // We want an *exact* match for red/green/blue/alpha - int r = findConfigAttrib(egl, display, config, - EGL10.EGL_RED_SIZE, 0); - int g = findConfigAttrib(egl, display, config, - EGL10.EGL_GREEN_SIZE, 0); - int b = findConfigAttrib(egl, display, config, - EGL10.EGL_BLUE_SIZE, 0); - int a = findConfigAttrib(egl, display, config, - EGL10.EGL_ALPHA_SIZE, 0); - - if (r == mRedSize && g == mGreenSize && b == mBlueSize && a == mAlphaSize) - return config; + EGLConfig[] configs) { + for (EGLConfig config : configs) { + int d = findConfigAttrib(egl, display, config, + EGL10.EGL_DEPTH_SIZE, 0); + int s = findConfigAttrib(egl, display, config, + EGL10.EGL_STENCIL_SIZE, 0); + + // We need at least mDepthSize and mStencilSize bits + if (d < mDepthSize || s < mStencilSize) + continue; + + // We want an *exact* match for red/green/blue/alpha + int r = findConfigAttrib(egl, display, config, + EGL10.EGL_RED_SIZE, 0); + int g = findConfigAttrib(egl, display, config, + EGL10.EGL_GREEN_SIZE, 0); + int b = findConfigAttrib(egl, display, config, + EGL10.EGL_BLUE_SIZE, 0); + int a = findConfigAttrib(egl, display, config, + EGL10.EGL_ALPHA_SIZE, 0); + + if (r == mRedSize && g == mGreenSize && b == mBlueSize && a == mAlphaSize) + return config; } return null; } private int findConfigAttrib(EGL10 egl, EGLDisplay display, - EGLConfig config, int attribute, int defaultValue) { + EGLConfig config, int attribute, int defaultValue) { if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) { - return mValue[0]; + return mValue[0]; } return defaultValue; } private void printConfigs(EGL10 egl, EGLDisplay display, - EGLConfig[] configs) { + EGLConfig[] configs) { int numConfigs = configs.length; Log.w(TAG, String.format("%d configurations", numConfigs)); for (int i = 0; i < numConfigs; i++) { - Log.w(TAG, String.format("Configuration %d:\n", i)); - printConfig(egl, display, configs[i]); + Log.w(TAG, String.format("Configuration %d:\n", i)); + printConfig(egl, display, configs[i]); } } private void printConfig(EGL10 egl, EGLDisplay display, - EGLConfig config) { + EGLConfig config) { int[] attributes = { EGL10.EGL_BUFFER_SIZE, EGL10.EGL_ALPHA_SIZE, @@ -634,14 +638,15 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { }; int[] value = new int[1]; for (int i = 0; i < attributes.length; i++) { - int attribute = attributes[i]; - String name = names[i]; - if ( egl.eglGetConfigAttrib(display, config, attribute, value)) { - Log.w(TAG, String.format(" %s: %d\n", name, value[0])); - } else { - // Log.w(TAG, String.format(" %s: failed\n", name)); - while (egl.eglGetError() != EGL10.EGL_SUCCESS); - } + int attribute = attributes[i]; + String name = names[i]; + if (egl.eglGetConfigAttrib(display, config, attribute, value)) { + Log.w(TAG, String.format(" %s: %d\n", name, value[0])); + } else { + // Log.w(TAG, String.format(" %s: failed\n", name)); + while (egl.eglGetError() != EGL10.EGL_SUCCESS) + ; + } } } @@ -657,19 +662,18 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { private static class Renderer implements GLSurfaceView.Renderer { - public void onDrawFrame(GL10 gl) { GodotLib.step(); - for(int i=0;i<Godot.singleton_count;i++) { + for (int i = 0; i < Godot.singleton_count; i++) { Godot.singletons[i].onGLDrawFrame(gl); } } public void onSurfaceChanged(GL10 gl, int width, int height) { - GodotLib.resize(width, height,!firsttime); - firsttime=false; - for(int i=0;i<Godot.singleton_count;i++) { + GodotLib.resize(width, height, !firsttime); + firsttime = false; + for (int i = 0; i < Godot.singleton_count; i++) { Godot.singletons[i].onGLSurfaceChanged(gl, width, height); } } diff --git a/platform/android/java/src/org/godotengine/godot/input/GodotEditText.java b/platform/android/java/src/org/godotengine/godot/input/GodotEditText.java index d8a3ac5591..53fcf5ef70 100644 --- a/platform/android/java/src/org/godotengine/godot/input/GodotEditText.java +++ b/platform/android/java/src/org/godotengine/godot/input/GodotEditText.java @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + package org.godotengine.godot.input; import android.content.Context; import android.util.AttributeSet; @@ -70,42 +71,37 @@ public class GodotEditText extends EditText { super(context, attrs, defStyle); this.initView(); } - + protected void initView() { - this.setPadding(0, 0, 0, 0); + this.setPadding(0, 0, 0, 0); this.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI); - + sHandler = new Handler() { @Override public void handleMessage(final Message msg) { switch (msg.what) { - case HANDLER_OPEN_IME_KEYBOARD: - { - GodotEditText edit = (GodotEditText) msg.obj; - String text = edit.mOriginText; - if (edit.requestFocus()) - { - edit.removeTextChangedListener(edit.mInputWrapper); - edit.setText(""); - edit.append(text); - edit.mInputWrapper.setOriginText(text); - edit.addTextChangedListener(edit.mInputWrapper); - final InputMethodManager imm = (InputMethodManager) mView.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - imm.showSoftInput(edit, 0); - } - } - break; - - case HANDLER_CLOSE_IME_KEYBOARD: - { - GodotEditText edit = (GodotEditText) msg.obj; - - edit.removeTextChangedListener(mInputWrapper); - final InputMethodManager imm = (InputMethodManager) mView.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(edit.getWindowToken(), 0); - edit.mView.requestFocus(); + case HANDLER_OPEN_IME_KEYBOARD: { + GodotEditText edit = (GodotEditText)msg.obj; + String text = edit.mOriginText; + if (edit.requestFocus()) { + edit.removeTextChangedListener(edit.mInputWrapper); + edit.setText(""); + edit.append(text); + edit.mInputWrapper.setOriginText(text); + edit.addTextChangedListener(edit.mInputWrapper); + final InputMethodManager imm = (InputMethodManager)mView.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.showSoftInput(edit, 0); } - break; + } break; + + case HANDLER_CLOSE_IME_KEYBOARD: { + GodotEditText edit = (GodotEditText)msg.obj; + + edit.removeTextChangedListener(mInputWrapper); + final InputMethodManager imm = (InputMethodManager)mView.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(edit.getWindowToken(), 0); + edit.mView.requestFocus(); + } break; } } }; @@ -116,7 +112,7 @@ public class GodotEditText extends EditText { // =========================================================== public void setView(final GodotView view) { this.mView = view; - if(mInputWrapper == null) + if (mInputWrapper == null) mInputWrapper = new GodotTextInputWrapper(mView, this); this.setOnEditorActionListener(mInputWrapper); view.requestFocus(); @@ -125,7 +121,7 @@ public class GodotEditText extends EditText { // =========================================================== // Methods for/from SuperClass/Interfaces // =========================================================== - @Override + @Override public boolean onKeyDown(final int keyCode, final KeyEvent keyEvent) { super.onKeyDown(keyCode, keyEvent); @@ -142,7 +138,7 @@ public class GodotEditText extends EditText { // =========================================================== public void showKeyboard(String p_existing_text) { this.mOriginText = p_existing_text; - + final Message msg = new Message(); msg.what = HANDLER_OPEN_IME_KEYBOARD; msg.obj = this; @@ -155,7 +151,7 @@ public class GodotEditText extends EditText { msg.obj = this; sHandler.sendMessage(msg); } - + // =========================================================== // Inner and Anonymous Classes // =========================================================== diff --git a/platform/android/java/src/org/godotengine/godot/input/GodotTextInputWrapper.java b/platform/android/java/src/org/godotengine/godot/input/GodotTextInputWrapper.java index ac424ab9f8..5d13f17ffb 100644 --- a/platform/android/java/src/org/godotengine/godot/input/GodotTextInputWrapper.java +++ b/platform/android/java/src/org/godotengine/godot/input/GodotTextInputWrapper.java @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + package org.godotengine.godot.input; import android.content.Context; import android.text.Editable; @@ -67,7 +68,7 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene private boolean isFullScreenEdit() { final TextView textField = this.mEdit; - final InputMethodManager imm = (InputMethodManager) textField.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + final InputMethodManager imm = (InputMethodManager)textField.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); return imm.isFullscreenMode(); } @@ -81,7 +82,6 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene @Override public void afterTextChanged(final Editable s) { - } @Override @@ -103,13 +103,16 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene public void onTextChanged(final CharSequence pCharSequence, final int start, final int before, final int count) { //Log.d(TAG, "onTextChanged(" + pCharSequence + ")start: " + start + ",count: " + count + ",before: " + before); + final int[] newChars = new int[count]; + for (int i = start; i < start + count; ++i) { + newChars[i - start] = pCharSequence.charAt(i); + } mView.queueEvent(new Runnable() { @Override public void run() { - for (int i = start; i < start + count; ++i) { - final int ch = pCharSequence.charAt(i); - GodotLib.key(0, ch, true); - GodotLib.key(0, ch, false); + for (int i = 0; i < count; ++i) { + GodotLib.key(0, newChars[i], true); + GodotLib.key(0, newChars[i], false); } } }); diff --git a/platform/android/java/src/org/godotengine/godot/input/InputManagerCompat.java b/platform/android/java/src/org/godotengine/godot/input/InputManagerCompat.java index 4615d2fbb5..0a876d2b7f 100644 --- a/platform/android/java/src/org/godotengine/godot/input/InputManagerCompat.java +++ b/platform/android/java/src/org/godotengine/godot/input/InputManagerCompat.java @@ -23,118 +23,118 @@ import android.view.InputDevice; import android.view.MotionEvent; public interface InputManagerCompat { - /** - * Gets information about the input device with the specified id. - * - * @param id The device id - * @return The input device or null if not found - */ - public InputDevice getInputDevice(int id); + /** + * Gets information about the input device with the specified id. + * + * @param id The device id + * @return The input device or null if not found + */ + public InputDevice getInputDevice(int id); - /** - * Gets the ids of all input devices in the system. - * - * @return The input device ids. - */ - public int[] getInputDeviceIds(); + /** + * Gets the ids of all input devices in the system. + * + * @return The input device ids. + */ + public int[] getInputDeviceIds(); - /** - * Registers an input device listener to receive notifications about when - * input devices are added, removed or changed. - * - * @param listener The listener to register. - * @param handler The handler on which the listener should be invoked, or - * null if the listener should be invoked on the calling thread's - * looper. - */ - public void registerInputDeviceListener(InputManagerCompat.InputDeviceListener listener, - Handler handler); + /** + * Registers an input device listener to receive notifications about when + * input devices are added, removed or changed. + * + * @param listener The listener to register. + * @param handler The handler on which the listener should be invoked, or + * null if the listener should be invoked on the calling thread's + * looper. + */ + public void registerInputDeviceListener(InputManagerCompat.InputDeviceListener listener, + Handler handler); - /** - * Unregisters an input device listener. - * - * @param listener The listener to unregister. - */ - public void unregisterInputDeviceListener(InputManagerCompat.InputDeviceListener listener); + /** + * Unregisters an input device listener. + * + * @param listener The listener to unregister. + */ + public void unregisterInputDeviceListener(InputManagerCompat.InputDeviceListener listener); - /* - * The following three calls are to simulate V16 behavior on pre-Jellybean - * devices. If you don't call them, your callback will never be called - * pre-API 16. - */ + /* + * The following three calls are to simulate V16 behavior on pre-Jellybean + * devices. If you don't call them, your callback will never be called + * pre-API 16. + */ - /** - * Pass the motion events to the InputManagerCompat. This is used to - * optimize for polling for controllers. If you do not pass these events in, - * polling will cause regular object creation. - * - * @param event the motion event from the app - */ - public void onGenericMotionEvent(MotionEvent event); + /** + * Pass the motion events to the InputManagerCompat. This is used to + * optimize for polling for controllers. If you do not pass these events in, + * polling will cause regular object creation. + * + * @param event the motion event from the app + */ + public void onGenericMotionEvent(MotionEvent event); - /** - * Tell the V9 input manager that it should stop polling for disconnected - * devices. You can call this during onPause in your activity, although you - * might want to call it whenever your game is not active (or whenever you - * don't care about being notified of new input devices) - */ - public void onPause(); + /** + * Tell the V9 input manager that it should stop polling for disconnected + * devices. You can call this during onPause in your activity, although you + * might want to call it whenever your game is not active (or whenever you + * don't care about being notified of new input devices) + */ + public void onPause(); - /** - * Tell the V9 input manager that it should start polling for disconnected - * devices. You can call this during onResume in your activity, although you - * might want to call it less often (only when the gameplay is actually - * active) - */ - public void onResume(); + /** + * Tell the V9 input manager that it should start polling for disconnected + * devices. You can call this during onResume in your activity, although you + * might want to call it less often (only when the gameplay is actually + * active) + */ + public void onResume(); - public interface InputDeviceListener { - /** - * Called whenever the input manager detects that a device has been - * added. This will only be called in the V9 version when a motion event - * is detected. - * - * @param deviceId The id of the input device that was added. - */ - void onInputDeviceAdded(int deviceId); + public interface InputDeviceListener { + /** + * Called whenever the input manager detects that a device has been + * added. This will only be called in the V9 version when a motion event + * is detected. + * + * @param deviceId The id of the input device that was added. + */ + void onInputDeviceAdded(int deviceId); - /** - * Called whenever the properties of an input device have changed since - * they were last queried. This will not be called for the V9 version of - * the API. - * - * @param deviceId The id of the input device that changed. - */ - void onInputDeviceChanged(int deviceId); + /** + * Called whenever the properties of an input device have changed since + * they were last queried. This will not be called for the V9 version of + * the API. + * + * @param deviceId The id of the input device that changed. + */ + void onInputDeviceChanged(int deviceId); - /** - * Called whenever the input manager detects that a device has been - * removed. For the V9 version, this can take some time depending on the - * poll rate. - * - * @param deviceId The id of the input device that was removed. - */ - void onInputDeviceRemoved(int deviceId); - } + /** + * Called whenever the input manager detects that a device has been + * removed. For the V9 version, this can take some time depending on the + * poll rate. + * + * @param deviceId The id of the input device that was removed. + */ + void onInputDeviceRemoved(int deviceId); + } - /** - * Use this to construct a compatible InputManager. - */ - public static class Factory { + /** + * Use this to construct a compatible InputManager. + */ + public static class Factory { - /** - * Constructs and returns a compatible InputManger - * - * @param context the Context that will be used to get the system - * service from - * @return a compatible implementation of InputManager - */ - public static InputManagerCompat getInputManager(Context context) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - return new InputManagerV16(context); - } else { - return new InputManagerV9(); - } - } - } + /** + * Constructs and returns a compatible InputManger + * + * @param context the Context that will be used to get the system + * service from + * @return a compatible implementation of InputManager + */ + public static InputManagerCompat getInputManager(Context context) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + return new InputManagerV16(context); + } else { + return new InputManagerV9(); + } + } + } } diff --git a/platform/android/java/src/org/godotengine/godot/input/InputManagerV16.java b/platform/android/java/src/org/godotengine/godot/input/InputManagerV16.java index f05701f455..3b88609cc9 100644 --- a/platform/android/java/src/org/godotengine/godot/input/InputManagerV16.java +++ b/platform/android/java/src/org/godotengine/godot/input/InputManagerV16.java @@ -30,78 +30,74 @@ import java.util.Map; @TargetApi(Build.VERSION_CODES.JELLY_BEAN) public class InputManagerV16 implements InputManagerCompat { - private final InputManager mInputManager; - private final Map<InputManagerCompat.InputDeviceListener, V16InputDeviceListener> mListeners; - - public InputManagerV16(Context context) { - mInputManager = (InputManager) context.getSystemService(Context.INPUT_SERVICE); - mListeners = new HashMap<InputManagerCompat.InputDeviceListener, V16InputDeviceListener>(); - } - - @Override - public InputDevice getInputDevice(int id) { - return mInputManager.getInputDevice(id); - } - - @Override - public int[] getInputDeviceIds() { - return mInputManager.getInputDeviceIds(); - } - - static class V16InputDeviceListener implements InputManager.InputDeviceListener { - final InputManagerCompat.InputDeviceListener mIDL; - - public V16InputDeviceListener(InputDeviceListener idl) { - mIDL = idl; - } - - @Override - public void onInputDeviceAdded(int deviceId) { - mIDL.onInputDeviceAdded(deviceId); - } - - @Override - public void onInputDeviceChanged(int deviceId) { - mIDL.onInputDeviceChanged(deviceId); - } - - @Override - public void onInputDeviceRemoved(int deviceId) { - mIDL.onInputDeviceRemoved(deviceId); - } - - } - - @Override - public void registerInputDeviceListener(InputDeviceListener listener, Handler handler) { - V16InputDeviceListener v16Listener = new V16InputDeviceListener(listener); - mInputManager.registerInputDeviceListener(v16Listener, handler); - mListeners.put(listener, v16Listener); - } - - @Override - public void unregisterInputDeviceListener(InputDeviceListener listener) { - V16InputDeviceListener curListener = mListeners.remove(listener); - if (null != curListener) - { - mInputManager.unregisterInputDeviceListener(curListener); - } - - } - - @Override - public void onGenericMotionEvent(MotionEvent event) { - // unused in V16 - } - - @Override - public void onPause() { - // unused in V16 - } - - @Override - public void onResume() { - // unused in V16 - } - + private final InputManager mInputManager; + private final Map<InputManagerCompat.InputDeviceListener, V16InputDeviceListener> mListeners; + + public InputManagerV16(Context context) { + mInputManager = (InputManager)context.getSystemService(Context.INPUT_SERVICE); + mListeners = new HashMap<InputManagerCompat.InputDeviceListener, V16InputDeviceListener>(); + } + + @Override + public InputDevice getInputDevice(int id) { + return mInputManager.getInputDevice(id); + } + + @Override + public int[] getInputDeviceIds() { + return mInputManager.getInputDeviceIds(); + } + + static class V16InputDeviceListener implements InputManager.InputDeviceListener { + final InputManagerCompat.InputDeviceListener mIDL; + + public V16InputDeviceListener(InputDeviceListener idl) { + mIDL = idl; + } + + @Override + public void onInputDeviceAdded(int deviceId) { + mIDL.onInputDeviceAdded(deviceId); + } + + @Override + public void onInputDeviceChanged(int deviceId) { + mIDL.onInputDeviceChanged(deviceId); + } + + @Override + public void onInputDeviceRemoved(int deviceId) { + mIDL.onInputDeviceRemoved(deviceId); + } + } + + @Override + public void registerInputDeviceListener(InputDeviceListener listener, Handler handler) { + V16InputDeviceListener v16Listener = new V16InputDeviceListener(listener); + mInputManager.registerInputDeviceListener(v16Listener, handler); + mListeners.put(listener, v16Listener); + } + + @Override + public void unregisterInputDeviceListener(InputDeviceListener listener) { + V16InputDeviceListener curListener = mListeners.remove(listener); + if (null != curListener) { + mInputManager.unregisterInputDeviceListener(curListener); + } + } + + @Override + public void onGenericMotionEvent(MotionEvent event) { + // unused in V16 + } + + @Override + public void onPause() { + // unused in V16 + } + + @Override + public void onResume() { + // unused in V16 + } } diff --git a/platform/android/java/src/org/godotengine/godot/input/InputManagerV9.java b/platform/android/java/src/org/godotengine/godot/input/InputManagerV9.java index 0334c00997..a1418c5899 100644 --- a/platform/android/java/src/org/godotengine/godot/input/InputManagerV9.java +++ b/platform/android/java/src/org/godotengine/godot/input/InputManagerV9.java @@ -31,181 +31,179 @@ import java.util.Map; import java.util.Queue; public class InputManagerV9 implements InputManagerCompat { - private static final String LOG_TAG = "InputManagerV9"; - private static final int MESSAGE_TEST_FOR_DISCONNECT = 101; - private static final long CHECK_ELAPSED_TIME = 3000L; - - private static final int ON_DEVICE_ADDED = 0; - private static final int ON_DEVICE_CHANGED = 1; - private static final int ON_DEVICE_REMOVED = 2; - - private final SparseArray<long[]> mDevices; - private final Map<InputDeviceListener, Handler> mListeners; - private final Handler mDefaultHandler; - - private static class PollingMessageHandler extends Handler { - private final WeakReference<InputManagerV9> mInputManager; - - PollingMessageHandler(InputManagerV9 im) { - mInputManager = new WeakReference<InputManagerV9>(im); - } - - @Override - public void handleMessage(Message msg) { - super.handleMessage(msg); - switch (msg.what) { - case MESSAGE_TEST_FOR_DISCONNECT: - InputManagerV9 imv = mInputManager.get(); - if (null != imv) { - long time = SystemClock.elapsedRealtime(); - int size = imv.mDevices.size(); - for (int i = 0; i < size; i++) { - long[] lastContact = imv.mDevices.valueAt(i); - if (null != lastContact) { - if (time - lastContact[0] > CHECK_ELAPSED_TIME) { - // check to see if the device has been - // disconnected - int id = imv.mDevices.keyAt(i); - if (null == InputDevice.getDevice(id)) { - // disconnected! - imv.notifyListeners(ON_DEVICE_REMOVED, id); - imv.mDevices.remove(id); - } else { - lastContact[0] = time; - } - } - } - } - sendEmptyMessageDelayed(MESSAGE_TEST_FOR_DISCONNECT, - CHECK_ELAPSED_TIME); - } - break; - } - } - - } - - public InputManagerV9() { - mDevices = new SparseArray<long[]>(); - mListeners = new HashMap<InputDeviceListener, Handler>(); - mDefaultHandler = new PollingMessageHandler(this); - // as a side-effect, populates our collection of watched - // input devices - getInputDeviceIds(); - } - - @Override - public InputDevice getInputDevice(int id) { - return InputDevice.getDevice(id); - } - - @Override - public int[] getInputDeviceIds() { - // add any hitherto unknown devices to our - // collection of watched input devices - int[] activeDevices = InputDevice.getDeviceIds(); - long time = SystemClock.elapsedRealtime(); - for ( int id : activeDevices ) { - long[] lastContact = mDevices.get(id); - if ( null == lastContact ) { - // we have a new device - mDevices.put(id, new long[] { time }); - } - } - return activeDevices; - } - - @Override - public void registerInputDeviceListener(InputDeviceListener listener, Handler handler) { - mListeners.remove(listener); - if (handler == null) { - handler = mDefaultHandler; - } - mListeners.put(listener, handler); - } - - @Override - public void unregisterInputDeviceListener(InputDeviceListener listener) { - mListeners.remove(listener); - } - - private void notifyListeners(int why, int deviceId) { - // the state of some device has changed - if (!mListeners.isEmpty()) { - // yes... this will cause an object to get created... hopefully - // it won't happen very often - for (InputDeviceListener listener : mListeners.keySet()) { - Handler handler = mListeners.get(listener); - DeviceEvent odc = DeviceEvent.getDeviceEvent(why, deviceId, listener); - handler.post(odc); - } - } - } - - private static class DeviceEvent implements Runnable { - private int mMessageType; - private int mId; - private InputDeviceListener mListener; - private static Queue<DeviceEvent> sEventQueue = new ArrayDeque<DeviceEvent>(); - - private DeviceEvent() { - } - - static DeviceEvent getDeviceEvent(int messageType, int id, - InputDeviceListener listener) { - DeviceEvent curChanged = sEventQueue.poll(); - if (null == curChanged) { - curChanged = new DeviceEvent(); - } - curChanged.mMessageType = messageType; - curChanged.mId = id; - curChanged.mListener = listener; - return curChanged; - } - - @Override - public void run() { - switch (mMessageType) { - case ON_DEVICE_ADDED: - mListener.onInputDeviceAdded(mId); - break; - case ON_DEVICE_CHANGED: - mListener.onInputDeviceChanged(mId); - break; - case ON_DEVICE_REMOVED: - mListener.onInputDeviceRemoved(mId); - break; - default: - Log.e(LOG_TAG, "Unknown Message Type"); - break; - } - // dump this runnable back in the queue - sEventQueue.offer(this); - } - } - - @Override - public void onGenericMotionEvent(MotionEvent event) { - // detect new devices - int id = event.getDeviceId(); - long[] timeArray = mDevices.get(id); - if (null == timeArray) { - notifyListeners(ON_DEVICE_ADDED, id); - timeArray = new long[1]; - mDevices.put(id, timeArray); - } - long time = SystemClock.elapsedRealtime(); - timeArray[0] = time; - } - - @Override - public void onPause() { - mDefaultHandler.removeMessages(MESSAGE_TEST_FOR_DISCONNECT); - } - - @Override - public void onResume() { - mDefaultHandler.sendEmptyMessage(MESSAGE_TEST_FOR_DISCONNECT); - } - + private static final String LOG_TAG = "InputManagerV9"; + private static final int MESSAGE_TEST_FOR_DISCONNECT = 101; + private static final long CHECK_ELAPSED_TIME = 3000L; + + private static final int ON_DEVICE_ADDED = 0; + private static final int ON_DEVICE_CHANGED = 1; + private static final int ON_DEVICE_REMOVED = 2; + + private final SparseArray<long[]> mDevices; + private final Map<InputDeviceListener, Handler> mListeners; + private final Handler mDefaultHandler; + + private static class PollingMessageHandler extends Handler { + private final WeakReference<InputManagerV9> mInputManager; + + PollingMessageHandler(InputManagerV9 im) { + mInputManager = new WeakReference<InputManagerV9>(im); + } + + @Override + public void handleMessage(Message msg) { + super.handleMessage(msg); + switch (msg.what) { + case MESSAGE_TEST_FOR_DISCONNECT: + InputManagerV9 imv = mInputManager.get(); + if (null != imv) { + long time = SystemClock.elapsedRealtime(); + int size = imv.mDevices.size(); + for (int i = 0; i < size; i++) { + long[] lastContact = imv.mDevices.valueAt(i); + if (null != lastContact) { + if (time - lastContact[0] > CHECK_ELAPSED_TIME) { + // check to see if the device has been + // disconnected + int id = imv.mDevices.keyAt(i); + if (null == InputDevice.getDevice(id)) { + // disconnected! + imv.notifyListeners(ON_DEVICE_REMOVED, id); + imv.mDevices.remove(id); + } else { + lastContact[0] = time; + } + } + } + } + sendEmptyMessageDelayed(MESSAGE_TEST_FOR_DISCONNECT, + CHECK_ELAPSED_TIME); + } + break; + } + } + } + + public InputManagerV9() { + mDevices = new SparseArray<long[]>(); + mListeners = new HashMap<InputDeviceListener, Handler>(); + mDefaultHandler = new PollingMessageHandler(this); + // as a side-effect, populates our collection of watched + // input devices + getInputDeviceIds(); + } + + @Override + public InputDevice getInputDevice(int id) { + return InputDevice.getDevice(id); + } + + @Override + public int[] getInputDeviceIds() { + // add any hitherto unknown devices to our + // collection of watched input devices + int[] activeDevices = InputDevice.getDeviceIds(); + long time = SystemClock.elapsedRealtime(); + for (int id : activeDevices) { + long[] lastContact = mDevices.get(id); + if (null == lastContact) { + // we have a new device + mDevices.put(id, new long[] { time }); + } + } + return activeDevices; + } + + @Override + public void registerInputDeviceListener(InputDeviceListener listener, Handler handler) { + mListeners.remove(listener); + if (handler == null) { + handler = mDefaultHandler; + } + mListeners.put(listener, handler); + } + + @Override + public void unregisterInputDeviceListener(InputDeviceListener listener) { + mListeners.remove(listener); + } + + private void notifyListeners(int why, int deviceId) { + // the state of some device has changed + if (!mListeners.isEmpty()) { + // yes... this will cause an object to get created... hopefully + // it won't happen very often + for (InputDeviceListener listener : mListeners.keySet()) { + Handler handler = mListeners.get(listener); + DeviceEvent odc = DeviceEvent.getDeviceEvent(why, deviceId, listener); + handler.post(odc); + } + } + } + + private static class DeviceEvent implements Runnable { + private int mMessageType; + private int mId; + private InputDeviceListener mListener; + private static Queue<DeviceEvent> sEventQueue = new ArrayDeque<DeviceEvent>(); + + private DeviceEvent() { + } + + static DeviceEvent getDeviceEvent(int messageType, int id, + InputDeviceListener listener) { + DeviceEvent curChanged = sEventQueue.poll(); + if (null == curChanged) { + curChanged = new DeviceEvent(); + } + curChanged.mMessageType = messageType; + curChanged.mId = id; + curChanged.mListener = listener; + return curChanged; + } + + @Override + public void run() { + switch (mMessageType) { + case ON_DEVICE_ADDED: + mListener.onInputDeviceAdded(mId); + break; + case ON_DEVICE_CHANGED: + mListener.onInputDeviceChanged(mId); + break; + case ON_DEVICE_REMOVED: + mListener.onInputDeviceRemoved(mId); + break; + default: + Log.e(LOG_TAG, "Unknown Message Type"); + break; + } + // dump this runnable back in the queue + sEventQueue.offer(this); + } + } + + @Override + public void onGenericMotionEvent(MotionEvent event) { + // detect new devices + int id = event.getDeviceId(); + long[] timeArray = mDevices.get(id); + if (null == timeArray) { + notifyListeners(ON_DEVICE_ADDED, id); + timeArray = new long[1]; + mDevices.put(id, timeArray); + } + long time = SystemClock.elapsedRealtime(); + timeArray[0] = time; + } + + @Override + public void onPause() { + mDefaultHandler.removeMessages(MESSAGE_TEST_FOR_DISCONNECT); + } + + @Override + public void onResume() { + mDefaultHandler.sendEmptyMessage(MESSAGE_TEST_FOR_DISCONNECT); + } } diff --git a/platform/android/java/src/org/godotengine/godot/payments/ConsumeTask.java b/platform/android/java/src/org/godotengine/godot/payments/ConsumeTask.java index 8622f4ccff..afe5f81b6d 100644 --- a/platform/android/java/src/org/godotengine/godot/payments/ConsumeTask.java +++ b/platform/android/java/src/org/godotengine/godot/payments/ConsumeTask.java @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + package org.godotengine.godot.payments; import com.android.vending.billing.IInAppBillingService; @@ -39,33 +40,32 @@ import android.util.Log; abstract public class ConsumeTask { private Context context; - + private IInAppBillingService mService; - public ConsumeTask(IInAppBillingService mService, Context context ){ + public ConsumeTask(IInAppBillingService mService, Context context) { this.context = context; this.mService = mService; } - - public void consume(final String sku){ + public void consume(final String sku) { //Log.d("XXX", "Consuming product " + sku); PaymentsCache pc = new PaymentsCache(context); Boolean isBlocked = pc.getConsumableFlag("block", sku); String _token = pc.getConsumableValue("token", sku); - //Log.d("XXX", "token " + _token); - if(!isBlocked && _token == null){ + //Log.d("XXX", "token " + _token); + if (!isBlocked && _token == null) { //_token = "inapp:"+context.getPackageName()+":android.test.purchased"; //Log.d("XXX", "Consuming product " + sku + " with token " + _token); - }else if(!isBlocked){ + } else if (!isBlocked) { //Log.d("XXX", "It is not blocked ¿?"); return; - }else if(_token == null){ + } else if (_token == null) { //Log.d("XXX", "No token available"); this.error("No token for sku:" + sku); return; } final String token = _token; - new AsyncTask<String, String, String>(){ + new AsyncTask<String, String, String>() { @Override protected String doInBackground(String... params) { @@ -73,28 +73,27 @@ abstract public class ConsumeTask { //Log.d("XXX", "Requesting to release item."); int response = mService.consumePurchase(3, context.getPackageName(), token); //Log.d("XXX", "release response code: " + response); - if(response == 0 || response == 8){ + if (response == 0 || response == 8) { return null; } } catch (RemoteException e) { return e.getMessage(); - } return "Some error"; } - - protected void onPostExecute(String param){ - if(param == null){ - success( new PaymentsCache(context).getConsumableValue("ticket", sku) ); - }else{ + + protected void onPostExecute(String param) { + if (param == null) { + success(new PaymentsCache(context).getConsumableValue("ticket", sku)); + } else { error(param); } } - - }.execute(); + + } + .execute(); } - + abstract protected void success(String ticket); abstract protected void error(String message); - } diff --git a/platform/android/java/src/org/godotengine/godot/payments/GenericConsumeTask.java b/platform/android/java/src/org/godotengine/godot/payments/GenericConsumeTask.java index 0afe35510c..8b48193ae2 100644 --- a/platform/android/java/src/org/godotengine/godot/payments/GenericConsumeTask.java +++ b/platform/android/java/src/org/godotengine/godot/payments/GenericConsumeTask.java @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + package org.godotengine.godot.payments; import com.android.vending.billing.IInAppBillingService; @@ -36,15 +37,12 @@ import android.os.AsyncTask; import android.os.RemoteException; import android.util.Log; -abstract public class GenericConsumeTask extends AsyncTask<String, String, String>{ +abstract public class GenericConsumeTask extends AsyncTask<String, String, String> { private Context context; private IInAppBillingService mService; - - - - public GenericConsumeTask(Context context, IInAppBillingService mService, String sku, String receipt, String signature, String token){ + public GenericConsumeTask(Context context, IInAppBillingService mService, String sku, String receipt, String signature, String token) { this.context = context; this.mService = mService; this.sku = sku; @@ -52,19 +50,19 @@ abstract public class GenericConsumeTask extends AsyncTask<String, String, Strin this.signature = signature; this.token = token; } - + private String sku; private String receipt; private String signature; private String token; - + @Override protected String doInBackground(String... params) { try { //Log.d("godot", "Requesting to consume an item with token ." + token); int response = mService.consumePurchase(3, context.getPackageName(), token); //Log.d("godot", "consumePurchase response: " + response); - if(response == 0 || response == 8){ + if (response == 0 || response == 8) { return null; } } catch (Exception e) { @@ -72,11 +70,10 @@ abstract public class GenericConsumeTask extends AsyncTask<String, String, Strin } return null; } - - protected void onPostExecute(String sarasa){ + + protected void onPostExecute(String sarasa) { onSuccess(sku, receipt, signature, token); } - - abstract public void onSuccess(String sku, String receipt, String signature, String token); + abstract public void onSuccess(String sku, String receipt, String signature, String token); } diff --git a/platform/android/java/src/org/godotengine/godot/payments/HandlePurchaseTask.java b/platform/android/java/src/org/godotengine/godot/payments/HandlePurchaseTask.java index 7318ae2fc6..766989f953 100644 --- a/platform/android/java/src/org/godotengine/godot/payments/HandlePurchaseTask.java +++ b/platform/android/java/src/org/godotengine/godot/payments/HandlePurchaseTask.java @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + package org.godotengine.godot.payments; import org.json.JSONException; @@ -50,28 +51,26 @@ import android.util.Log; abstract public class HandlePurchaseTask { private Activity context; - - public HandlePurchaseTask(Activity context ){ + + public HandlePurchaseTask(Activity context) { this.context = context; } - - - public void handlePurchaseRequest(int resultCode, Intent data){ + + public void handlePurchaseRequest(int resultCode, Intent data) { //Log.d("XXX", "Handling purchase response"); //int responseCode = data.getIntExtra("RESPONSE_CODE", 0); PaymentsCache pc = new PaymentsCache(context); - + String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA"); //Log.d("XXX", "Purchase data:" + purchaseData); String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE"); //Log.d("XXX", "Purchase signature:" + dataSignature); - + if (resultCode == Activity.RESULT_OK) { - + try { //Log.d("SARLANGA", purchaseData); - - + JSONObject jo = new JSONObject(purchaseData); //String sku = jo.getString("productId"); //alert("You have bought the " + sku + ". Excellent choice, aventurer!"); @@ -82,8 +81,8 @@ abstract public class HandlePurchaseTask { //Integer state = jo.getInt("purchaseState"); String developerPayload = jo.getString("developerPayload"); String purchaseToken = jo.getString("purchaseToken"); - - if(! pc.getConsumableValue("validation_hash", productId).equals(developerPayload) ) { + + if (!pc.getConsumableValue("validation_hash", productId).equals(developerPayload)) { error("Untrusted callback"); return; } @@ -92,13 +91,13 @@ abstract public class HandlePurchaseTask { pc.setConsumableValue("ticket", productId, purchaseData); pc.setConsumableFlag("block", productId, true); pc.setConsumableValue("token", productId, purchaseToken); - + success(productId, dataSignature, purchaseData); return; - } catch (JSONException e) { + } catch (JSONException e) { error(e.getMessage()); } - }else if( resultCode == Activity.RESULT_CANCELED){ + } else if (resultCode == Activity.RESULT_CANCELED) { canceled(); } } @@ -106,6 +105,4 @@ abstract public class HandlePurchaseTask { abstract protected void success(String sku, String signature, String ticket); abstract protected void error(String message); abstract protected void canceled(); - - } diff --git a/platform/android/java/src/org/godotengine/godot/payments/PaymentsCache.java b/platform/android/java/src/org/godotengine/godot/payments/PaymentsCache.java index 69ac02e902..40cdeea72e 100644 --- a/platform/android/java/src/org/godotengine/godot/payments/PaymentsCache.java +++ b/platform/android/java/src/org/godotengine/godot/payments/PaymentsCache.java @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + package org.godotengine.godot.payments; import android.content.Context; @@ -34,41 +35,38 @@ import android.content.SharedPreferences; import android.util.Log; public class PaymentsCache { - + public Context context; - public PaymentsCache(Context context){ + public PaymentsCache(Context context) { this.context = context; } - - - public void setConsumableFlag(String set, String sku, Boolean flag){ - SharedPreferences sharedPref = context.getSharedPreferences("consumables_" + set, Context.MODE_PRIVATE); - SharedPreferences.Editor editor = sharedPref.edit(); - editor.putBoolean(sku, flag); - editor.commit(); -} - public boolean getConsumableFlag(String set, String sku){ - SharedPreferences sharedPref = context.getSharedPreferences( - "consumables_" + set, Context.MODE_PRIVATE); - return sharedPref.getBoolean(sku, false); + public void setConsumableFlag(String set, String sku, Boolean flag) { + SharedPreferences sharedPref = context.getSharedPreferences("consumables_" + set, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedPref.edit(); + editor.putBoolean(sku, flag); + editor.commit(); } + public boolean getConsumableFlag(String set, String sku) { + SharedPreferences sharedPref = context.getSharedPreferences( + "consumables_" + set, Context.MODE_PRIVATE); + return sharedPref.getBoolean(sku, false); + } - public void setConsumableValue(String set, String sku, String value){ - SharedPreferences sharedPref = context.getSharedPreferences("consumables_" + set, Context.MODE_PRIVATE); - SharedPreferences.Editor editor = sharedPref.edit(); - editor.putString(sku, value); + public void setConsumableValue(String set, String sku, String value) { + SharedPreferences sharedPref = context.getSharedPreferences("consumables_" + set, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedPref.edit(); + editor.putString(sku, value); //Log.d("XXX", "Setting asset: consumables_" + set + ":" + sku); - editor.commit(); + editor.commit(); } - public String getConsumableValue(String set, String sku){ - SharedPreferences sharedPref = context.getSharedPreferences( - "consumables_" + set, Context.MODE_PRIVATE); + public String getConsumableValue(String set, String sku) { + SharedPreferences sharedPref = context.getSharedPreferences( + "consumables_" + set, Context.MODE_PRIVATE); //Log.d("XXX", "Getting asset: consumables_" + set + ":" + sku); - return sharedPref.getString(sku, null); + return sharedPref.getString(sku, null); } - } diff --git a/platform/android/java/src/org/godotengine/godot/payments/PaymentsManager.java b/platform/android/java/src/org/godotengine/godot/payments/PaymentsManager.java index b327265abb..441a311358 100644 --- a/platform/android/java/src/org/godotengine/godot/payments/PaymentsManager.java +++ b/platform/android/java/src/org/godotengine/godot/payments/PaymentsManager.java @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + package org.godotengine.godot.payments; import android.app.Activity; @@ -92,11 +93,21 @@ public class PaymentsManager { @Override public void onServiceDisconnected(ComponentName name) { mService = null; + + // At this stage, godotPaymentV3 might not have been initialized yet. + if (godotPaymentV3 != null) { + godotPaymentV3.callbackDisconnected(); + } } @Override public void onServiceConnected(ComponentName name, IBinder service) { mService = IInAppBillingService.Stub.asInterface(service); + + // At this stage, godotPaymentV3 might not have been initialized yet. + if (godotPaymentV3 != null) { + godotPaymentV3.callbackConnected(); + } } }; @@ -106,7 +117,6 @@ public class PaymentsManager { @Override protected void error(String message) { godotPaymentV3.callbackFail(); - } @Override @@ -119,8 +129,12 @@ public class PaymentsManager { godotPaymentV3.callbackAlreadyOwned(sku); } - }.purchase(sku, transactionId); + } + .purchase(sku, transactionId); + } + public boolean isConnected() { + return mService != null; } public void consumeUnconsumedPurchases() { @@ -135,16 +149,15 @@ public class PaymentsManager { protected void error(String message) { Log.d("godot", "consumeUnconsumedPurchases :" + message); godotPaymentV3.callbackFailConsume(); - } @Override protected void notRequired() { Log.d("godot", "callbackSuccessNoUnconsumedPurchases :"); godotPaymentV3.callbackSuccessNoUnconsumedPurchases(); - } - }.consumeItAll(); + } + .consumeItAll(); } public void requestPurchased() { @@ -210,9 +223,9 @@ public class PaymentsManager { @Override protected void error(String message) { godotPaymentV3.callbackFail(); - } - }.consume(sku); + } + .consume(sku); } } @@ -225,7 +238,8 @@ public class PaymentsManager { protected void canceled() { godotPaymentV3.callbackCancel(); } - }.handlePurchaseRequest(resultCode, data); + } + .handlePurchaseRequest(resultCode, data); } public void validatePurchase(String purchaseToken, final String sku) { @@ -246,8 +260,8 @@ public class PaymentsManager { protected void error(String message) { godotPaymentV3.callbackFail(); } - }.consume(sku); - + } + .consume(sku); } @Override @@ -259,7 +273,8 @@ public class PaymentsManager { protected void canceled() { godotPaymentV3.callbackCancel(); } - }.validatePurchase(sku); + } + .validatePurchase(sku); } public void setAutoConsume(boolean autoConsume) { @@ -278,7 +293,8 @@ public class PaymentsManager { protected void error(String message) { godotPaymentV3.callbackFailConsume(); } - }.consume(sku); + } + .consume(sku); } // Workaround to bug where sometimes response codes come as Long instead of Integer @@ -287,8 +303,10 @@ public class PaymentsManager { if (o == null) { //logDebug("Bundle with null response code, assuming OK (known issue)"); return BILLING_RESPONSE_RESULT_OK; - } else if (o instanceof Integer) return ((Integer) o).intValue(); - else if (o instanceof Long) return (int) ((Long) o).longValue(); + } else if (o instanceof Integer) + return ((Integer)o).intValue(); + else if (o instanceof Long) + return (int)((Long)o).longValue(); else { //logError("Unexpected type for bundle response code."); //logError(o.getClass().getName()); @@ -304,25 +322,41 @@ public class PaymentsManager { * It also includes the result code numerically. */ public static String getResponseDesc(int code) { - String[] iab_msgs = ("0:OK/1:User Canceled/2:Unknown/" + - "3:Billing Unavailable/4:Item unavailable/" + - "5:Developer Error/6:Error/7:Item Already Owned/" + - "8:Item not owned").split("/"); - String[] iabhelper_msgs = ("0:OK/-1001:Remote exception during initialization/" + - "-1002:Bad response received/" + - "-1003:Purchase signature verification failed/" + - "-1004:Send intent failed/" + - "-1005:User cancelled/" + - "-1006:Unknown purchase response/" + - "-1007:Missing token/" + - "-1008:Unknown error/" + - "-1009:Subscriptions not available/" + - "-1010:Invalid consumption attempt").split("/"); + String[] iab_msgs = ("0:OK/1:User Canceled/2:Unknown/" + + + "3:Billing Unavailable/4:Item unavailable/" + + + "5:Developer Error/6:Error/7:Item Already Owned/" + + + "8:Item not owned") + .split("/"); + String[] iabhelper_msgs = ("0:OK/-1001:Remote exception during initialization/" + + + "-1002:Bad response received/" + + + "-1003:Purchase signature verification failed/" + + + "-1004:Send intent failed/" + + + "-1005:User cancelled/" + + + "-1006:Unknown purchase response/" + + + "-1007:Missing token/" + + + "-1008:Unknown error/" + + + "-1009:Subscriptions not available/" + + + "-1010:Invalid consumption attempt") + .split("/"); if (code <= -1000) { int index = -1000 - code; - if (index >= 0 && index < iabhelper_msgs.length) return iabhelper_msgs[index]; - else return String.valueOf(code) + ":Unknown IAB Helper Error"; + if (index >= 0 && index < iabhelper_msgs.length) + return iabhelper_msgs[index]; + else + return String.valueOf(code) + ":Unknown IAB Helper Error"; } else if (code < 0 || code >= iab_msgs.length) return String.valueOf(code) + ":Unknown"; else @@ -375,7 +409,7 @@ public class PaymentsManager { ArrayList<String> responseList = skuDetails.getStringArrayList("DETAILS_LIST"); for (String thisResponse : responseList) { - Log.d("godot", "response = "+thisResponse); + Log.d("godot", "response = " + thisResponse); godotPaymentV3.addSkuDetail(thisResponse); } } catch (RemoteException e) { @@ -385,7 +419,8 @@ public class PaymentsManager { } godotPaymentV3.completeSkuDetail(); } - })).start(); + })) + .start(); } private GodotPaymentV3 godotPaymentV3; @@ -393,5 +428,4 @@ public class PaymentsManager { public void setBaseSingleton(GodotPaymentV3 godotPaymentV3) { this.godotPaymentV3 = godotPaymentV3; } - } diff --git a/platform/android/java/src/org/godotengine/godot/payments/PurchaseTask.java b/platform/android/java/src/org/godotengine/godot/payments/PurchaseTask.java index 6ecea0106c..e1d9bcee65 100644 --- a/platform/android/java/src/org/godotengine/godot/payments/PurchaseTask.java +++ b/platform/android/java/src/org/godotengine/godot/payments/PurchaseTask.java @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + package org.godotengine.godot.payments; import org.json.JSONException; @@ -50,17 +51,16 @@ import android.util.Log; abstract public class PurchaseTask { private Activity context; - + private IInAppBillingService mService; - public PurchaseTask(IInAppBillingService mService, Activity context ){ + public PurchaseTask(IInAppBillingService mService, Activity context) { this.context = context; this.mService = mService; } - private boolean isLooping = false; - - public void purchase(final String sku, final String transactionId){ + + public void purchase(final String sku, final String transactionId) { Log.d("XXX", "Starting purchase for: " + sku); PaymentsCache pc = new PaymentsCache(context); Boolean isBlocked = pc.getConsumableFlag("block", sku); @@ -75,7 +75,7 @@ abstract public class PurchaseTask { Bundle buyIntentBundle; try { - buyIntentBundle = mService.getBuyIntent(3, context.getApplicationContext().getPackageName(), sku, "inapp", hash ); + buyIntentBundle = mService.getBuyIntent(3, context.getApplicationContext().getPackageName(), sku, "inapp", hash); } catch (RemoteException e) { //Log.d("XXX", "Error: " + e.getMessage()); error(e.getMessage()); @@ -83,50 +83,45 @@ abstract public class PurchaseTask { } Object rc = buyIntentBundle.get("RESPONSE_CODE"); int responseCode = 0; - if(rc == null){ + if (rc == null) { responseCode = PaymentsManager.BILLING_RESPONSE_RESULT_OK; - }else if( rc instanceof Integer){ + } else if (rc instanceof Integer) { responseCode = ((Integer)rc).intValue(); - }else if( rc instanceof Long){ + } else if (rc instanceof Long) { responseCode = (int)((Long)rc).longValue(); } //Log.d("XXX", "Buy intent response code: " + responseCode); - if(responseCode == 1 || responseCode == 3 || responseCode == 4){ + if (responseCode == 1 || responseCode == 3 || responseCode == 4) { canceled(); return; } - if(responseCode == 7){ + if (responseCode == 7) { alreadyOwned(); return; } - - + PendingIntent pendingIntent = buyIntentBundle.getParcelable("BUY_INTENT"); pc.setConsumableValue("validation_hash", sku, hash); try { - if(context == null){ + if (context == null) { //Log.d("XXX", "No context!"); } - if(pendingIntent == null){ + if (pendingIntent == null) { //Log.d("XXX", "No pending intent"); } //Log.d("XXX", "Starting activity for purchase!"); context.startIntentSenderForResult( pendingIntent.getIntentSender(), - PaymentsManager.REQUEST_CODE_FOR_PURCHASE, - new Intent(), + PaymentsManager.REQUEST_CODE_FOR_PURCHASE, + new Intent(), Integer.valueOf(0), Integer.valueOf(0), - Integer.valueOf(0)); + Integer.valueOf(0)); } catch (SendIntentException e) { error(e.getMessage()); } - - - } abstract protected void error(String message); abstract protected void canceled(); abstract protected void alreadyOwned(); - } diff --git a/platform/android/java/src/org/godotengine/godot/payments/ReleaseAllConsumablesTask.java b/platform/android/java/src/org/godotengine/godot/payments/ReleaseAllConsumablesTask.java index d831e45694..e00e37f9d1 100644 --- a/platform/android/java/src/org/godotengine/godot/payments/ReleaseAllConsumablesTask.java +++ b/platform/android/java/src/org/godotengine/godot/payments/ReleaseAllConsumablesTask.java @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + package org.godotengine.godot.payments; import java.util.ArrayList; @@ -48,69 +49,63 @@ abstract public class ReleaseAllConsumablesTask { private Context context; private IInAppBillingService mService; - - public ReleaseAllConsumablesTask(IInAppBillingService mService, Context context ){ + + public ReleaseAllConsumablesTask(IInAppBillingService mService, Context context) { this.context = context; this.mService = mService; } - - public void consumeItAll(){ - try{ + public void consumeItAll() { + try { //Log.d("godot", "consumeItall for " + context.getPackageName()); - Bundle bundle = mService.getPurchases(3, context.getPackageName(), "inapp",null); - + Bundle bundle = mService.getPurchases(3, context.getPackageName(), "inapp", null); + for (String key : bundle.keySet()) { Object value = bundle.get(key); //Log.d("godot", String.format("%s %s (%s)", key, //value.toString(), value.getClass().getName())); } - - - if (bundle.getInt("RESPONSE_CODE") == 0){ + + if (bundle.getInt("RESPONSE_CODE") == 0) { final ArrayList<String> myPurchases = bundle.getStringArrayList("INAPP_PURCHASE_DATA_LIST"); final ArrayList<String> mySignatures = bundle.getStringArrayList("INAPP_DATA_SIGNATURE_LIST"); - - if (myPurchases == null || myPurchases.size() == 0){ + if (myPurchases == null || myPurchases.size() == 0) { //Log.d("godot", "No purchases!"); notRequired(); return; } - - + //Log.d("godot", "# products to be consumed:" + myPurchases.size()); - for (int i=0;i<myPurchases.size();i++) - { - - try{ + for (int i = 0; i < myPurchases.size(); i++) { + + try { String receipt = myPurchases.get(i); JSONObject inappPurchaseData = new JSONObject(receipt); String sku = inappPurchaseData.getString("productId"); String token = inappPurchaseData.getString("purchaseToken"); String signature = mySignatures.get(i); //Log.d("godot", "A punto de consumir un item con token:" + token + "\n" + receipt); - new GenericConsumeTask(context, mService, sku, receipt,signature, token) { - + new GenericConsumeTask(context, mService, sku, receipt, signature, token) { + @Override public void onSuccess(String sku, String receipt, String signature, String token) { ReleaseAllConsumablesTask.this.success(sku, receipt, signature, token); } - }.execute(); - + } + .execute(); + } catch (JSONException e) { } } - } - }catch(Exception e){ + } catch (Exception e) { Log.d("godot", "Error releasing products:" + e.getClass().getName() + ":" + e.getMessage()); } } - + abstract protected void success(String sku, String receipt, String signature, String token); abstract protected void error(String message); abstract protected void notRequired(); - } diff --git a/platform/android/java/src/org/godotengine/godot/payments/ValidateTask.java b/platform/android/java/src/org/godotengine/godot/payments/ValidateTask.java index 6e058c140c..1eb9d001e0 100644 --- a/platform/android/java/src/org/godotengine/godot/payments/ValidateTask.java +++ b/platform/android/java/src/org/godotengine/godot/payments/ValidateTask.java @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + package org.godotengine.godot.payments; import org.json.JSONException; @@ -55,22 +56,21 @@ abstract public class ValidateTask { private Activity context; private GodotPaymentV3 godotPaymentsV3; - public ValidateTask(Activity context, GodotPaymentV3 godotPaymentsV3){ + public ValidateTask(Activity context, GodotPaymentV3 godotPaymentsV3) { this.context = context; this.godotPaymentsV3 = godotPaymentsV3; } - - public void validatePurchase(final String sku){ - new AsyncTask<String, String, String>(){ - + public void validatePurchase(final String sku) { + new AsyncTask<String, String, String>() { + private ProgressDialog dialog; @Override - protected void onPreExecute(){ + protected void onPreExecute() { dialog = ProgressDialog.show(context, null, "Please wait..."); } - + @Override protected String doInBackground(String... params) { PaymentsCache pc = new PaymentsCache(context); @@ -90,37 +90,34 @@ abstract public class ValidateTask { //Log.d("XXX", "Validation response:\n"+jsonResponse); return jsonResponse; } - + @Override - protected void onPostExecute(String response){ - if(dialog != null){ + protected void onPostExecute(String response) { + if (dialog != null) { dialog.dismiss(); } JSONObject j; try { j = new JSONObject(response); - if(j.getString("status").equals("OK")){ + if (j.getString("status").equals("OK")) { success(); return; - }else if(j.getString("status") != null){ + } else if (j.getString("status") != null) { error(j.getString("message")); - }else{ + } else { error("Connection error"); } } catch (JSONException e) { error(e.getMessage()); - }catch (Exception e){ + } catch (Exception e) { error(e.getMessage()); } - - } - - }.execute(); + + } + .execute(); } abstract protected void success(); abstract protected void error(String message); abstract protected void canceled(); - - } diff --git a/platform/android/java/src/org/godotengine/godot/utils/Crypt.java b/platform/android/java/src/org/godotengine/godot/utils/Crypt.java index 2fd66553f6..f34511137e 100644 --- a/platform/android/java/src/org/godotengine/godot/utils/Crypt.java +++ b/platform/android/java/src/org/godotengine/godot/utils/Crypt.java @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + package org.godotengine.godot.utils; import java.security.MessageDigest; @@ -34,34 +35,34 @@ import java.util.Random; public class Crypt { - public static String md5(String input){ - try { - // Create MD5 Hash - MessageDigest digest = java.security.MessageDigest.getInstance("MD5"); - digest.update(input.getBytes()); - byte messageDigest[] = digest.digest(); - - // Create Hex String - StringBuffer hexString = new StringBuffer(); - for (int i=0; i<messageDigest.length; i++) - hexString.append(Integer.toHexString(0xFF & messageDigest[i])); - return hexString.toString(); - - } catch (Exception e) { - e.printStackTrace(); - } - return ""; + public static String md5(String input) { + try { + // Create MD5 Hash + MessageDigest digest = java.security.MessageDigest.getInstance("MD5"); + digest.update(input.getBytes()); + byte messageDigest[] = digest.digest(); + + // Create Hex String + StringBuffer hexString = new StringBuffer(); + for (int i = 0; i < messageDigest.length; i++) + hexString.append(Integer.toHexString(0xFF & messageDigest[i])); + return hexString.toString(); + + } catch (Exception e) { + e.printStackTrace(); + } + return ""; } - - public static String createRandomHash(){ + + public static String createRandomHash() { return md5(Long.toString(createRandomLong())); } - - public static long createAbsRandomLong(){ + + public static long createAbsRandomLong() { return Math.abs(createRandomLong()); } - - public static long createRandomLong(){ + + public static long createRandomLong() { Random r = new Random(); return r.nextLong(); } diff --git a/platform/android/java/src/org/godotengine/godot/utils/CustomSSLSocketFactory.java b/platform/android/java/src/org/godotengine/godot/utils/CustomSSLSocketFactory.java index 3fc8c48397..7216d8b5a4 100644 --- a/platform/android/java/src/org/godotengine/godot/utils/CustomSSLSocketFactory.java +++ b/platform/android/java/src/org/godotengine/godot/utils/CustomSSLSocketFactory.java @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + package org.godotengine.godot.utils; import java.io.IOException; import java.net.Socket; @@ -42,30 +43,29 @@ import javax.net.ssl.TrustManagerFactory; import org.apache.http.conn.ssl.SSLSocketFactory; - /** * * @author Luis Linietsky <luis.linietsky@gmail.com> */ public class CustomSSLSocketFactory extends SSLSocketFactory { - SSLContext sslContext = SSLContext.getInstance("TLS"); + SSLContext sslContext = SSLContext.getInstance("TLS"); - public CustomSSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { - super(truststore); + public CustomSSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { + super(truststore); - TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509"); - tmf.init(truststore); + TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509"); + tmf.init(truststore); - sslContext.init(null, tmf.getTrustManagers(), null); - } + sslContext.init(null, tmf.getTrustManagers(), null); + } - @Override - public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException { - return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose); - } + @Override + public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException { + return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose); + } - @Override - public Socket createSocket() throws IOException { - return sslContext.getSocketFactory().createSocket(); - } + @Override + public Socket createSocket() throws IOException { + return sslContext.getSocketFactory().createSocket(); + } } diff --git a/platform/android/java/src/org/godotengine/godot/utils/HttpRequester.java b/platform/android/java/src/org/godotengine/godot/utils/HttpRequester.java index 0711f30b8b..b84f5cce2e 100644 --- a/platform/android/java/src/org/godotengine/godot/utils/HttpRequester.java +++ b/platform/android/java/src/org/godotengine/godot/utils/HttpRequester.java @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + package org.godotengine.godot.utils; import java.io.BufferedReader; @@ -63,7 +64,6 @@ import org.apache.http.params.HttpProtocolParams; import org.apache.http.protocol.HTTP; import org.apache.http.util.EntityUtils; - import android.content.Context; import android.content.SharedPreferences; import android.util.Log; @@ -73,155 +73,154 @@ import android.util.Log; * @author Luis Linietsky <luis.linietsky@gmail.com> */ public class HttpRequester { - + private Context context; - private static final int TTL = 600000; // 10 minutos - private long cttl=0; - - public HttpRequester(){ + private static final int TTL = 600000; // 10 minutos + private long cttl = 0; + + public HttpRequester() { //Log.d("XXX", "Creando http request sin contexto"); } - - public HttpRequester(Context context){ - this.context=context; + + public HttpRequester(Context context) { + this.context = context; //Log.d("XXX", "Creando http request con contexto"); } - - public String post(RequestParams params){ - HttpPost httppost = new HttpPost(params.getUrl()); - try { + + public String post(RequestParams params) { + HttpPost httppost = new HttpPost(params.getUrl()); + try { httppost.setEntity(new UrlEncodedFormEntity(params.toPairsList())); return request(httppost); } catch (UnsupportedEncodingException e) { return null; } } - - public String get(RequestParams params){ + + public String get(RequestParams params) { String response = getResponseFromCache(params.getUrl()); - if(response == null){ + if (response == null) { //Log.d("XXX", "Cache miss!"); - HttpGet httpget = new HttpGet(params.getUrl()); - long timeInit = new Date().getTime(); - response = request(httpget); - long delay = new Date().getTime() - timeInit; - Log.d("com.app11tt.android.utils.HttpRequest::get(url)", "Url: " + params.getUrl() + " downloaded in " + String.format("%.03f", delay/1000.0f) + " seconds"); - if(response == null || response.length() == 0){ - response = ""; - }else{ - saveResponseIntoCache(params.getUrl(), response); - } + HttpGet httpget = new HttpGet(params.getUrl()); + long timeInit = new Date().getTime(); + response = request(httpget); + long delay = new Date().getTime() - timeInit; + Log.d("com.app11tt.android.utils.HttpRequest::get(url)", "Url: " + params.getUrl() + " downloaded in " + String.format("%.03f", delay / 1000.0f) + " seconds"); + if (response == null || response.length() == 0) { + response = ""; + } else { + saveResponseIntoCache(params.getUrl(), response); + } } Log.d("XXX", "Req: " + params.getUrl()); Log.d("XXX", "Resp: " + response); - return response; + return response; } - - private String request(HttpUriRequest request){ + + private String request(HttpUriRequest request) { //Log.d("XXX", "Haciendo request a: " + request.getURI() ); - Log.d("PPP", "Haciendo request a: " + request.getURI() ); + Log.d("PPP", "Haciendo request a: " + request.getURI()); long init = new Date().getTime(); HttpClient httpclient = getNewHttpClient(); HttpParams httpParameters = httpclient.getParams(); HttpConnectionParams.setConnectionTimeout(httpParameters, 0); HttpConnectionParams.setSoTimeout(httpParameters, 0); HttpConnectionParams.setTcpNoDelay(httpParameters, true); - try { - HttpResponse response = httpclient.execute(request); - Log.d("PPP", "Fin de request (" + (new Date().getTime() - init) + ") a: " + request.getURI() ); + try { + HttpResponse response = httpclient.execute(request); + Log.d("PPP", "Fin de request (" + (new Date().getTime() - init) + ") a: " + request.getURI()); //Log.d("XXX1", "Status:" + response.getStatusLine().toString()); - if(response.getStatusLine().getStatusCode() == 200){ - String strResponse = EntityUtils.toString(response.getEntity()); + if (response.getStatusLine().getStatusCode() == 200) { + String strResponse = EntityUtils.toString(response.getEntity()); //Log.d("XXX2", strResponse); - return strResponse; - }else{ - Log.d("XXX3", "Response status code:" + response.getStatusLine().getStatusCode() + "\n" + EntityUtils.toString(response.getEntity())); - return null; - } - - } catch (ClientProtocolException e) { - Log.d("XXX3", e.getMessage()); - } catch (IOException e) { - Log.d("XXX4", e.getMessage()); - } + return strResponse; + } else { + Log.d("XXX3", "Response status code:" + response.getStatusLine().getStatusCode() + "\n" + EntityUtils.toString(response.getEntity())); + return null; + } + + } catch (ClientProtocolException e) { + Log.d("XXX3", e.getMessage()); + } catch (IOException e) { + Log.d("XXX4", e.getMessage()); + } return null; } - + private HttpClient getNewHttpClient() { - try { - KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); - trustStore.load(null, null); + try { + KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); + trustStore.load(null, null); - SSLSocketFactory sf = new CustomSSLSocketFactory(trustStore); - sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); + SSLSocketFactory sf = new CustomSSLSocketFactory(trustStore); + sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); - HttpParams params = new BasicHttpParams(); - HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); - HttpProtocolParams.setContentCharset(params, HTTP.UTF_8); + HttpParams params = new BasicHttpParams(); + HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); + HttpProtocolParams.setContentCharset(params, HTTP.UTF_8); - SchemeRegistry registry = new SchemeRegistry(); - registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); - registry.register(new Scheme("https", sf, 443)); + SchemeRegistry registry = new SchemeRegistry(); + registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); + registry.register(new Scheme("https", sf, 443)); - ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry); + ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry); - return new DefaultHttpClient(ccm, params); - } catch (Exception e) { - return new DefaultHttpClient(); - } + return new DefaultHttpClient(ccm, params); + } catch (Exception e) { + return new DefaultHttpClient(); + } } - + private static String convertStreamToString(InputStream is) { - BufferedReader reader = new BufferedReader(new InputStreamReader(is)); - StringBuilder sb = new StringBuilder(); - String line = null; - try { - while ((line = reader.readLine()) != null) { - sb.append((line + "\n")); - } - } catch (IOException e) { - e.printStackTrace(); - } finally { - try { - is.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - return sb.toString(); + BufferedReader reader = new BufferedReader(new InputStreamReader(is)); + StringBuilder sb = new StringBuilder(); + String line = null; + try { + while ((line = reader.readLine()) != null) { + sb.append((line + "\n")); + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + is.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return sb.toString(); } - public void saveResponseIntoCache(String request, String response){ - if(context == null){ + public void saveResponseIntoCache(String request, String response) { + if (context == null) { //Log.d("XXX", "No context, cache failed!"); return; } - SharedPreferences sharedPref = context.getSharedPreferences("http_get_cache", Context.MODE_PRIVATE); - SharedPreferences.Editor editor = sharedPref.edit(); - editor.putString("request_" + Crypt.md5(request), response); - editor.putLong("request_" + Crypt.md5(request) + "_ttl", new Date().getTime() + getTtl()); - editor.commit(); + SharedPreferences sharedPref = context.getSharedPreferences("http_get_cache", Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedPref.edit(); + editor.putString("request_" + Crypt.md5(request), response); + editor.putLong("request_" + Crypt.md5(request) + "_ttl", new Date().getTime() + getTtl()); + editor.commit(); } - - - public String getResponseFromCache(String request){ - if(context == null){ + + public String getResponseFromCache(String request) { + if (context == null) { Log.d("XXX", "No context, cache miss"); return null; } - SharedPreferences sharedPref = context.getSharedPreferences( "http_get_cache", Context.MODE_PRIVATE); - long ttl = getResponseTtl(request); - if(ttl == 0l || (new Date().getTime() - ttl) > 0l){ - Log.d("XXX", "Cache invalid ttl:" + ttl + " vs now:" + new Date().getTime()); - return null; - } - return sharedPref.getString("request_" + Crypt.md5(request), null); + SharedPreferences sharedPref = context.getSharedPreferences("http_get_cache", Context.MODE_PRIVATE); + long ttl = getResponseTtl(request); + if (ttl == 0l || (new Date().getTime() - ttl) > 0l) { + Log.d("XXX", "Cache invalid ttl:" + ttl + " vs now:" + new Date().getTime()); + return null; + } + return sharedPref.getString("request_" + Crypt.md5(request), null); } - public long getResponseTtl(String request){ - SharedPreferences sharedPref = context.getSharedPreferences( - "http_get_cache", Context.MODE_PRIVATE); - return sharedPref.getLong("request_" + Crypt.md5(request) + "_ttl", 0l); + public long getResponseTtl(String request) { + SharedPreferences sharedPref = context.getSharedPreferences( + "http_get_cache", Context.MODE_PRIVATE); + return sharedPref.getLong("request_" + Crypt.md5(request) + "_ttl", 0l); } public long getTtl() { @@ -229,7 +228,6 @@ public class HttpRequester { } public void setTtl(long ttl) { - this.cttl = (ttl*1000) + new Date().getTime(); + this.cttl = (ttl * 1000) + new Date().getTime(); } - } diff --git a/platform/android/java/src/org/godotengine/godot/utils/RequestParams.java b/platform/android/java/src/org/godotengine/godot/utils/RequestParams.java index ded11550bb..2368766afa 100644 --- a/platform/android/java/src/org/godotengine/godot/utils/RequestParams.java +++ b/platform/android/java/src/org/godotengine/godot/utils/RequestParams.java @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + package org.godotengine.godot.utils; import java.util.ArrayList; @@ -43,33 +44,33 @@ import org.apache.http.message.BasicNameValuePair; */ public class RequestParams { - private HashMap<String,String> params; + private HashMap<String, String> params; private String url; - - public RequestParams(){ - params = new HashMap<String,String>(); + + public RequestParams() { + params = new HashMap<String, String>(); } - - public void put(String key, String value){ + + public void put(String key, String value) { params.put(key, value); } - - public String get(String key){ + + public String get(String key) { return params.get(key); } - - public void remove(Object key){ + + public void remove(Object key) { params.remove(key); } - - public boolean has(String key){ + + public boolean has(String key) { return params.containsKey(key); } - - public List<NameValuePair> toPairsList(){ - List<NameValuePair> fields = new ArrayList<NameValuePair>(); - for(String key : params.keySet()){ + public List<NameValuePair> toPairsList() { + List<NameValuePair> fields = new ArrayList<NameValuePair>(); + + for (String key : params.keySet()) { fields.add(new BasicNameValuePair(key, this.get(key))); } return fields; @@ -82,6 +83,4 @@ public class RequestParams { public void setUrl(String url) { this.url = url; } - - } diff --git a/platform/android/java_class_wrapper.cpp b/platform/android/java_class_wrapper.cpp index 8606ea41a0..446a5911e5 100644 --- a/platform/android/java_class_wrapper.cpp +++ b/platform/android/java_class_wrapper.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "java_class_wrapper.h" #include "thread_jandroid.h" @@ -59,7 +60,7 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, r_error.argument = pc; continue; } - uint32_t *ptypes = E->get().param_types.ptr(); + uint32_t *ptypes = E->get().param_types.ptrw(); bool valid = true; for (int i = 0; i < pc; i++) { @@ -1116,7 +1117,7 @@ Ref<JavaClass> JavaClassWrapper::wrap(const String &p_class) { } if (!valid) { - print_line("Method Can't be bound (unsupported arguments): " + p_class + "::" + str_method); + print_line("Method can't be bound (unsupported arguments): " + p_class + "::" + str_method); env->DeleteLocalRef(obj); env->DeleteLocalRef(param_types); continue; @@ -1129,7 +1130,7 @@ Ref<JavaClass> JavaClassWrapper::wrap(const String &p_class) { String strsig; uint32_t sig = 0; if (!_get_type_sig(env, return_type, sig, strsig)) { - print_line("Method Can't be bound (unsupported return type): " + p_class + "::" + str_method); + print_line("Method can't be bound (unsupported return type): " + p_class + "::" + str_method); env->DeleteLocalRef(obj); env->DeleteLocalRef(param_types); env->DeleteLocalRef(return_type); @@ -1139,8 +1140,6 @@ Ref<JavaClass> JavaClassWrapper::wrap(const String &p_class) { signature += strsig; mi.return_type = sig; - print_line("METHOD: " + str_method + " SIG: " + signature + " static: " + itos(mi._static)); - bool discard = false; for (List<JavaClass::MethodInfo>::Element *E = java_class->methods[str_method].front(); E; E = E->next()) { @@ -1172,11 +1171,9 @@ Ref<JavaClass> JavaClassWrapper::wrap(const String &p_class) { if (new_likeliness > existing_likeliness) { java_class->methods[str_method].erase(E); - print_line("replace old"); break; } else { discard = true; - print_line("old is better"); } } diff --git a/platform/android/java_class_wrapper.h b/platform/android/java_class_wrapper.h index 1274b4bea9..648c147ca8 100644 --- a/platform/android/java_class_wrapper.h +++ b/platform/android/java_class_wrapper.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef JAVA_CLASS_WRAPPER_H #define JAVA_CLASS_WRAPPER_H diff --git a/platform/android/java_glue.cpp b/platform/android/java_glue.cpp index 0b193f5882..9baf58c3eb 100644 --- a/platform/android/java_glue.cpp +++ b/platform/android/java_glue.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef ANDROID_NATIVE_ACTIVITY #include "java_glue.h" @@ -34,6 +35,7 @@ #include "audio_driver_jandroid.h" #include "core/os/keyboard.h" #include "dir_access_jandroid.h" +#include "engine.h" #include "file_access_android.h" #include "file_access_jandroid.h" #include "java_class_wrapper.h" @@ -238,9 +240,7 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) { jclass c = env->GetObjectClass(obj); bool array; String name = _get_class_name(env, c, &array); - //print_line("name is " + name + ", array "+Variant(array)); - print_line("ARGNAME: " + name); if (name == "java.lang.String") { return String::utf8(env->GetStringUTFChars((jstring)obj, NULL)); @@ -250,7 +250,6 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) { jobjectArray arr = (jobjectArray)obj; int stringCount = env->GetArrayLength(arr); - //print_line("String array! " + String::num(stringCount)); PoolVector<String> sarr; for (int i = 0; i < stringCount; i++) { @@ -379,7 +378,6 @@ Variant _jobject_to_variant(JNIEnv *env, jobject obj) { Array vals = _jobject_to_variant(env, arr); env->DeleteLocalRef(arr); - //print_line("adding " + String::num(keys.size()) + " to Dictionary!"); for (int i = 0; i < keys.size(); i++) { ret[keys[i]] = vals[i]; @@ -410,7 +408,6 @@ class JNISingleton : public Object { public: virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error) { - //print_line("attempt to call "+String(p_method)); ERR_FAIL_COND_V(!instance, Variant()); r_error.error = Variant::CallError::CALL_OK; @@ -418,7 +415,6 @@ public: Map<StringName, MethodData>::Element *E = method_map.find(p_method); if (!E) { - print_line("no exists"); r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD; return Variant(); } @@ -426,7 +422,6 @@ public: int ac = E->get().argtypes.size(); if (ac < p_argcount) { - print_line("fewargs"); r_error.error = Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; r_error.argument = ac; return Variant(); @@ -434,7 +429,6 @@ public: if (ac > p_argcount) { - print_line("manyargs"); r_error.error = Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; r_error.argument = ac; return Variant(); @@ -463,7 +457,6 @@ public: ERR_FAIL_COND_V(res != 0, Variant()); - //print_line("argcount "+String::num(p_argcount)); List<jobject> to_erase; for (int i = 0; i < p_argcount; i++) { @@ -473,26 +466,21 @@ public: to_erase.push_back(vr.obj); } - //print_line("calling method!!"); - Variant ret; switch (E->get().ret_type) { case Variant::NIL: { - //print_line("call void"); env->CallVoidMethodA(instance, E->get().method, v); } break; case Variant::BOOL: { ret = env->CallBooleanMethodA(instance, E->get().method, v) == JNI_TRUE; - //print_line("call bool"); } break; case Variant::INT: { ret = env->CallIntMethodA(instance, E->get().method, v); - //print_line("call int"); } break; case Variant::REAL: { @@ -543,7 +531,6 @@ public: case Variant::DICTIONARY: { - //print_line("call dictionary"); jobject obj = env->CallObjectMethodA(instance, E->get().method, v); ret = _jobject_to_variant(env, obj); env->DeleteLocalRef(obj); @@ -551,7 +538,6 @@ public: } break; default: { - print_line("failure.."); env->PopLocalFrame(NULL); ERR_FAIL_V(Variant()); } break; @@ -563,7 +549,6 @@ public: } env->PopLocalFrame(NULL); - //print_line("success"); return ret; } @@ -608,6 +593,7 @@ static bool resized = false; static bool resized_reload = false; static Size2 new_size; static Vector3 accelerometer; +static Vector3 gravity; static Vector3 magnetometer; static Vector3 gyroscope; static HashMap<String, JNISingleton *> jni_singletons; @@ -645,7 +631,7 @@ static int _open_uri(const String &p_uri) { return env->CallIntMethod(godot_io, _openURI, jStr); } -static String _get_data_dir() { +static String _get_user_data_dir() { JNIEnv *env = ThreadAndroid::get_env(); jstring s = (jstring)env->CallObjectMethod(godot_io, _getDataDir); @@ -755,8 +741,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHei JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jobject obj, jobject activity, jboolean p_need_reload_hook, jobject p_asset_manager, jboolean p_use_apk_expansion) { - __android_log_print(ANDROID_LOG_INFO, "godot", "**INIT EVENT! - %p\n", env); - initialized = true; JavaVM *jvm; @@ -765,8 +749,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *en _godot_instance = env->NewGlobalRef(activity); //_godot_instance=activity; - __android_log_print(ANDROID_LOG_INFO, "godot", "***************** HELLO FROM JNI!!!!!!!!"); - { //setup IO Object @@ -774,17 +756,12 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *en if (cls) { cls = (jclass)env->NewGlobalRef(cls); - __android_log_print(ANDROID_LOG_INFO, "godot", "*******CLASS FOUND!!!"); } - __android_log_print(ANDROID_LOG_INFO, "godot", "STEP2, %p", cls); jfieldID fid = env->GetStaticFieldID(cls, "io", "Lorg/godotengine/godot/GodotIO;"); - __android_log_print(ANDROID_LOG_INFO, "godot", "STEP3 %i", fid); jobject ob = env->GetStaticObjectField(cls, fid); - __android_log_print(ANDROID_LOG_INFO, "godot", "STEP4, %p", ob); jobject gob = env->NewGlobalRef(ob); - __android_log_print(ANDROID_LOG_INFO, "godot", "STEP4.5, %p", gob); godot_io = gob; _on_video_init = env->GetMethodID(cls, "onVideoInit", "(Z)V"); @@ -823,15 +800,12 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *en AudioDriverAndroid::setup(gob); } - os_android = new OS_Android(_gfx_init_func, env, _open_uri, _get_data_dir, _get_locale, _get_model, _get_screen_dpi, _show_vk, _hide_vk, _get_vk_height, _set_screen_orient, _get_unique_id, _get_system_dir, _play_video, _is_video_playing, _pause_video, _stop_video, _set_keep_screen_on, _alert, p_use_apk_expansion); + os_android = new OS_Android(_gfx_init_func, env, _open_uri, _get_user_data_dir, _get_locale, _get_model, _get_screen_dpi, _show_vk, _hide_vk, _get_vk_height, _set_screen_orient, _get_unique_id, _get_system_dir, _play_video, _is_video_playing, _pause_video, _stop_video, _set_keep_screen_on, _alert, p_use_apk_expansion); os_android->set_need_reload_hooks(p_need_reload_hook); char wd[500]; getcwd(wd, 500); - __android_log_print(ANDROID_LOG_INFO, "godot", "test construction %i\n", tst.a); - __android_log_print(ANDROID_LOG_INFO, "godot", "running from dir %s\n", wd); - //video driver is determined here, because once initialized, it can't be changed // String vd = ProjectSettings::get_singleton()->get("display/driver"); @@ -841,7 +815,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *en static void _initialize_java_modules() { if (!ProjectSettings::get_singleton()->has_setting("android/modules")) { - print_line("ANDROID MODULES: Nothing to load, aborting"); + print_line("Android modules: Nothing to load, aborting"); return; } @@ -851,8 +825,6 @@ static void _initialize_java_modules() { return; } Vector<String> mods = modules.split(",", false); - print_line("ANDROID MODULES : " + modules); - __android_log_print(ANDROID_LOG_INFO, "godot", "mod count: %i", mods.size()); if (mods.size()) { @@ -875,7 +847,7 @@ static void _initialize_java_modules() { String m = mods[i]; //jclass singletonClass = env->FindClass(m.utf8().get_data()); - print_line("LOADING MODULE: " + m); + print_line("Loading module: " + m); jstring strClassName = env->NewStringUTF(m.utf8().get_data()); jclass singletonClass = (jclass)env->CallObjectMethod(cls, findClass, strClassName); @@ -886,7 +858,6 @@ static void _initialize_java_modules() { } //singletonClass=(jclass)env->NewGlobalRef(singletonClass); - __android_log_print(ANDROID_LOG_INFO, "godot", "****^*^*?^*^*class data %x", singletonClass); jmethodID initialize = env->GetStaticMethodID(singletonClass, "initialize", "(Landroid/app/Activity;)Lorg/godotengine/godot/Godot$SingletonBase;"); if (!initialize) { @@ -895,7 +866,6 @@ static void _initialize_java_modules() { ERR_CONTINUE(!initialize); } jobject obj = env->CallStaticObjectMethod(singletonClass, initialize, _godot_instance); - __android_log_print(ANDROID_LOG_INFO, "godot", "****^*^*?^*^*class instance %x", obj); jobject gob = env->NewGlobalRef(obj); } } @@ -904,8 +874,6 @@ static void _initialize_java_modules() { JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jobject obj, jobjectArray p_cmdline) { ThreadAndroid::setup_thread(); - __android_log_print(ANDROID_LOG_INFO, "godot", "**SETUP"); - const char **cmdline = NULL; int cmdlen = 0; bool use_apk_expansion = false; @@ -919,46 +887,31 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jo jstring string = (jstring)env->GetObjectArrayElement(p_cmdline, i); const char *rawString = env->GetStringUTFChars(string, 0); - if (!rawString) { - __android_log_print(ANDROID_LOG_INFO, "godot", "cmdline arg %i is null\n", i); - } else { - //__android_log_print(ANDROID_LOG_INFO,"godot","cmdline arg %i is: %s\n",i,rawString); - - if (strcmp(rawString, "-main_pack") == 0) - use_apk_expansion = true; + if (rawString && strcmp(rawString, "--main-pack") == 0) { + use_apk_expansion = true; } cmdline[i] = rawString; } } } - __android_log_print(ANDROID_LOG_INFO, "godot", "CMDLINE LEN %i - APK EXPANSION %i\n", cmdlen, int(use_apk_expansion)); -#if 0 - char *args[]={"-test","render",NULL}; - __android_log_print(ANDROID_LOG_INFO,"godot","pre asdasd setup..."); - Error err = Main::setup("apk",2,args,false); -#else Error err = Main::setup("apk", cmdlen, (char **)cmdline, false); -#endif if (cmdline) { free(cmdline); } if (err != OK) { - __android_log_print(ANDROID_LOG_INFO, "godot", "*****UNABLE TO SETUP"); return; //should exit instead and print the error } - __android_log_print(ANDROID_LOG_INFO, "godot", "*****SETUP OK"); java_class_wrapper = memnew(JavaClassWrapper(_godot_instance)); - ProjectSettings::get_singleton()->add_singleton(ProjectSettings::Singleton("JavaClassWrapper", java_class_wrapper)); + Engine::get_singleton()->add_singleton(Engine::Singleton("JavaClassWrapper", java_class_wrapper)); _initialize_java_modules(); } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jobject obj, jint width, jint height, jboolean reload) { - __android_log_print(ANDROID_LOG_INFO, "godot", "^_^_^_^_^ resize %lld, %i, %i\n", Thread::get_caller_id(), width, height); if (os_android) os_android->set_display_size(Size2(width, height)); @@ -972,8 +925,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, j JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jobject obj, bool p_32_bits) { - __android_log_print(ANDROID_LOG_INFO, "godot", "^_^_^_^_^ newcontext %lld\n", Thread::get_caller_id()); - if (os_android) { os_android->set_context_is_16_bits(!p_32_bits); } @@ -990,7 +941,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, job JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jobject obj) { if (step == 0) { - __android_log_print(ANDROID_LOG_INFO, "godot", "**FIRST_STEP"); // Since Godot is initialized on the UI thread, _main_thread_id was set to that thread's id, // but for Godot purposes, the main thread is the one running the game loop @@ -1008,10 +958,10 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, job ++step; } - //__android_log_print(ANDROID_LOG_INFO,"godot","**STEP EVENT! - %p-%i\n",env,Thread::get_caller_id()); - os_android->process_accelerometer(accelerometer); + os_android->process_gravity(gravity); + os_android->process_magnetometer(magnetometer); os_android->process_gyroscope(gyroscope); @@ -1021,14 +971,11 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, job jclass cls = env->FindClass("org/godotengine/godot/Godot"); jmethodID _finish = env->GetMethodID(cls, "forceQuit", "()V"); env->CallVoidMethod(_godot_instance, _finish); - __android_log_print(ANDROID_LOG_INFO, "godot", "**FINISH REQUEST!!! - %p-%i\n", env, Thread::get_caller_id()); } } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch(JNIEnv *env, jobject obj, jint ev, jint pointer, jint count, jintArray positions) { - //__android_log_print(ANDROID_LOG_INFO,"godot","**TOUCH EVENT! - %p-%i\n",env,Thread::get_caller_id()); - Vector<OS_Android::TouchPos> points; for (int i = 0; i < count; i++) { @@ -1294,7 +1241,6 @@ static unsigned int android_get_keysym(unsigned int p_code) { for (int i = 0; _ak_to_keycode[i].keysym != KEY_UNKNOWN; i++) { if (_ak_to_keycode[i].keycode == p_code) { - //print_line("outcode: " + _ak_to_keycode[i].keysym); return _ak_to_keycode[i].keysym; } @@ -1364,8 +1310,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jobj ievent->set_unicode(val); ievent->set_pressed(p_pressed); - print_line("Scancode: " + String::num(p_scancode) + ":" + String::num(ievent->get_scancode()) + " Unicode: " + String::num(val)); - if (val == '\n') { ievent->set_scancode(KEY_ENTER); } else if (val == 61448) { @@ -1386,6 +1330,10 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_accelerometer(JNIEnv accelerometer = Vector3(x, y, z); } +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gravity(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z) { + gravity = Vector3(x, y, z); +} + JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_magnetometer(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z) { magnetometer = Vector3(x, y, z); } @@ -1419,7 +1367,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_singleton(JNIEnv *env s->set_instance(env->NewGlobalRef(p_object)); jni_singletons[singname] = s; - ProjectSettings::get_singleton()->add_singleton(ProjectSettings::Singleton(singname, s)); + Engine::get_singleton()->add_singleton(Engine::Singleton(singname, s)); ProjectSettings::get_singleton()->set(singname, s); } @@ -1511,7 +1459,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_method(JNIEnv *env, j int stringCount = env->GetArrayLength(args); - print_line("Singl: " + singname + " Method: " + mname + " RetVal: " + retval); for (int i = 0; i < stringCount; i++) { jstring string = (jstring)env->GetObjectArrayElement(args, i); @@ -1523,11 +1470,10 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_method(JNIEnv *env, j cs += ")"; cs += get_jni_sig(retval); jclass cls = env->GetObjectClass(s->get_instance()); - print_line("METHOD: " + mname + " sig: " + cs); jmethodID mid = env->GetMethodID(cls, mname.ascii().get_data(), cs.ascii().get_data()); if (!mid) { - print_line("FAILED GETTING METHOID " + mname); + print_line("Failed getting method ID " + mname); } s->add_method(mname, mid, types, get_jni_type(retval)); @@ -1578,16 +1524,12 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_calldeferred(JNIEnv * int count = env->GetArrayLength(params); Variant args[VARIANT_ARG_MAX]; - //print_line("Java->GD call: "+obj->get_type()+"::"+str_method+" argc "+itos(count)); - for (int i = 0; i < MIN(count, VARIANT_ARG_MAX); i++) { jobject obj = env->GetObjectArrayElement(params, i); if (obj) args[i] = _jobject_to_variant(env, obj); env->DeleteLocalRef(obj); - - //print_line("\targ"+itos(i)+": "+Variant::get_type_name(args[i].get_type())); }; obj->call_deferred(str_method, args[0], args[1], args[2], args[3], args[4]); diff --git a/platform/android/java_glue.h b/platform/android/java_glue.h index 0aa2489813..d433b5f0d8 100644 --- a/platform/android/java_glue.h +++ b/platform/android/java_glue.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef ANDROID_NATIVE_ACTIVITY #ifndef JAVA_GLUE_H @@ -50,6 +51,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, j JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyconnectionchanged(JNIEnv *env, jobject obj, jint p_device, jboolean p_connected, jstring p_name); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_audio(JNIEnv *env, jobject obj); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_accelerometer(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gravity(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_magnetometer(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gyroscope(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusin(JNIEnv *env, jobject obj); diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index 45df312cae..5557c1de44 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "os_android.h" #include "core/io/file_access_buffered_fa.h" @@ -47,6 +48,8 @@ #include "file_access_jandroid.h" #endif +#include <dlfcn.h> + class AndroidLogger : public Logger { public: virtual void logv(const char *p_format, va_list p_list, bool p_err) { @@ -65,12 +68,6 @@ const char *OS_Android::get_video_driver_name(int p_driver) const { return "GLES2"; } - -OS::VideoMode OS_Android::get_default_video_mode() const { - - return OS::VideoMode(); -} - int OS_Android::get_audio_driver_count() const { return 1; @@ -120,30 +117,19 @@ void OS_Android::initialize_core() { #endif } -void OS_Android::initialize_logger() { - Vector<Logger *> loggers; - loggers.push_back(memnew(AndroidLogger)); - // FIXME: Reenable once we figure out how to get this properly in user:// - // instead of littering the user's working dirs (res:// + pwd) with log files (GH-12277) - //loggers.push_back(memnew(RotatedFileLogger("user://logs/log.txt"))); - _set_logger(memnew(CompositeLogger(loggers))); -} - void OS_Android::set_opengl_extensions(const char *p_gl_extensions) { ERR_FAIL_COND(!p_gl_extensions); gl_extensions = p_gl_extensions; } -void OS_Android::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { +Error OS_Android::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { use_gl2 = p_video_driver != 1; if (gfx_init_func) gfx_init_func(gfx_init_ud, use_gl2); - AudioDriverManager::add_driver(&audio_driver_android); - RasterizerGLES3::register_config(); RasterizerGLES3::make_current(); @@ -157,15 +143,12 @@ void OS_Android::initialize(const VideoMode &p_desired, int p_video_driver, int AudioDriverManager::initialize(p_audio_driver); - physics_server = memnew(PhysicsServerSW); - physics_server->init(); - physics_2d_server = Physics2DServerWrapMT::init_server<Physics2DServerSW>(); - physics_2d_server->init(); - input = memnew(InputDefault); input->set_fallback_mapping("Default Android Gamepad"); //power_manager = memnew(power_android); + + return OK; } void OS_Android::set_main_loop(MainLoop *p_main_loop) { @@ -190,6 +173,15 @@ void OS_Android::alert(const String &p_alert, const String &p_title) { alert_func(p_alert, p_title); } +Error OS_Android::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) { + p_library_handle = dlopen(p_path.utf8().get_data(), RTLD_NOW); + if (!p_library_handle) { + ERR_EXPLAIN("Can't open dynamic library: " + p_path + ". Error: " + dlerror()); + ERR_FAIL_V(ERR_CANT_OPEN); + } + return OK; +} + void OS_Android::set_mouse_show(bool p_show) { //android has no mouse... @@ -268,6 +260,9 @@ void OS_Android::set_cursor_shape(CursorShape p_shape) { //android really really really has no mouse.. how amazing.. } +void OS_Android::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { +} + void OS_Android::main_loop_begin() { if (main_loop) @@ -458,25 +453,27 @@ void OS_Android::process_touch(int p_what, int p_pointer, const Vector<TouchPos> } touch.clear(); } - } break; - case 3: { // add tuchi + case 3: { // add touch - ERR_FAIL_INDEX(p_pointer, p_points.size()); - - TouchPos tp = p_points[p_pointer]; - touch.push_back(tp); + for (int i = 0; i < p_points.size(); i++) { + if (p_points[i].id == p_pointer) { + TouchPos tp = p_points[i]; + touch.push_back(tp); - Ref<InputEventScreenTouch> ev; - ev.instance(); + Ref<InputEventScreenTouch> ev; + ev.instance(); - ev->set_index(tp.id); - ev->set_pressed(true); - ev->set_position(tp.pos); - input->parse_input_event(ev); + ev->set_index(tp.id); + ev->set_pressed(true); + ev->set_position(tp.pos); + input->parse_input_event(ev); + break; + } + } } break; - case 4: { + case 4: { // remove touch for (int i = 0; i < touch.size(); i++) { if (touch[i].id == p_pointer) { @@ -488,10 +485,10 @@ void OS_Android::process_touch(int p_what, int p_pointer, const Vector<TouchPos> ev->set_position(touch[i].pos); input->parse_input_event(ev); touch.remove(i); - i--; + + break; } } - } break; } } @@ -501,6 +498,11 @@ void OS_Android::process_accelerometer(const Vector3 &p_accelerometer) { input->set_accelerometer(p_accelerometer); } +void OS_Android::process_gravity(const Vector3 &p_gravity) { + + input->set_gravity(p_gravity); +} + void OS_Android::process_magnetometer(const Vector3 &p_magnetometer) { input->set_magnetometer(p_magnetometer); @@ -618,13 +620,13 @@ void OS_Android::set_need_reload_hooks(bool p_needs_them) { use_reload_hooks = p_needs_them; } -String OS_Android::get_data_dir() const { +String OS_Android::get_user_data_dir() const { if (data_dir_cache != String()) return data_dir_cache; - if (get_data_dir_func) { - String data_dir = get_data_dir_func(); + if (get_user_data_dir_func) { + String data_dir = get_user_data_dir_func(); //store current dir char real_current_dir_name[2048]; @@ -647,7 +649,6 @@ String OS_Android::get_data_dir() const { } return "."; - //return ProjectSettings::get_singleton()->get_singleton_object("GodotOS")->call("get_data_dir"); } void OS_Android::set_screen_orientation(ScreenOrientation p_orientation) { @@ -712,10 +713,27 @@ String OS_Android::get_joy_guid(int p_device) const { } 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 + if (p_feature == "mobile" || p_feature == "etc" || p_feature == "etc2") { + //TODO support etc2 only if GLES3 driver is selected + return true; + } +#if defined(__aarch64__) + if (p_feature == "arm64-v8a") { + return true; + } +#elif defined(__ARM_ARCH_7A__) + if (p_feature == "armeabi-v7a" || p_feature == "armeabi") { + return true; + } +#elif defined(__arm__) + if (p_feature == "armeabi") { + return true; + } +#endif + return false; } -OS_Android::OS_Android(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, OpenURIFunc p_open_uri_func, GetDataDirFunc p_get_data_dir_func, GetLocaleFunc p_get_locale_func, GetModelFunc p_get_model_func, GetScreenDPIFunc p_get_screen_dpi_func, ShowVirtualKeyboardFunc p_show_vk, HideVirtualKeyboardFunc p_hide_vk, VirtualKeyboardHeightFunc p_vk_height_func, SetScreenOrientationFunc p_screen_orient, GetUniqueIDFunc p_get_unique_id, GetSystemDirFunc p_get_sdir_func, VideoPlayFunc p_video_play_func, VideoIsPlayingFunc p_video_is_playing_func, VideoPauseFunc p_video_pause_func, VideoStopFunc p_video_stop_func, SetKeepScreenOnFunc p_set_keep_screen_on_func, AlertFunc p_alert_func, bool p_use_apk_expansion) { +OS_Android::OS_Android(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, OpenURIFunc p_open_uri_func, GetUserDataDirFunc p_get_user_data_dir_func, GetLocaleFunc p_get_locale_func, GetModelFunc p_get_model_func, GetScreenDPIFunc p_get_screen_dpi_func, ShowVirtualKeyboardFunc p_show_vk, HideVirtualKeyboardFunc p_hide_vk, VirtualKeyboardHeightFunc p_vk_height_func, SetScreenOrientationFunc p_screen_orient, GetUniqueIDFunc p_get_unique_id, GetSystemDirFunc p_get_sdir_func, VideoPlayFunc p_video_play_func, VideoIsPlayingFunc p_video_is_playing_func, VideoPauseFunc p_video_pause_func, VideoStopFunc p_video_stop_func, SetKeepScreenOnFunc p_set_keep_screen_on_func, AlertFunc p_alert_func, bool p_use_apk_expansion) { use_apk_expansion = p_use_apk_expansion; default_videomode.width = 800; @@ -731,7 +749,7 @@ OS_Android::OS_Android(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, OpenURI use_gl2 = false; open_uri_func = p_open_uri_func; - get_data_dir_func = p_get_data_dir_func; + get_user_data_dir_func = p_get_user_data_dir_func; get_locale_func = p_get_locale_func; get_model_func = p_get_model_func; get_screen_dpi_func = p_get_screen_dpi_func; @@ -752,7 +770,11 @@ OS_Android::OS_Android(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, OpenURI alert_func = p_alert_func; use_reload_hooks = false; - _set_logger(memnew(AndroidLogger)); + Vector<Logger *> loggers; + loggers.push_back(memnew(AndroidLogger)); + _set_logger(memnew(CompositeLogger(loggers))); + + AudioDriverManager::add_driver(&audio_driver_android); } OS_Android::~OS_Android() { diff --git a/platform/android/os_android.h b/platform/android/os_android.h index 0c78c198a8..12181b3688 100644 --- a/platform/android/os_android.h +++ b/platform/android/os_android.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef OS_ANDROID_H #define OS_ANDROID_H @@ -38,9 +39,6 @@ #include "os/main_loop.h" //#include "power_android.h" #include "servers/audio_server.h" -#include "servers/physics/physics_server_sw.h" -#include "servers/physics_2d/physics_2d_server_sw.h" -#include "servers/physics_2d/physics_2d_server_wrap_mt.h" #include "servers/visual/rasterizer.h" #ifdef ANDROID_NATIVE_ACTIVITY @@ -51,7 +49,7 @@ typedef void (*GFXInitFunc)(void *ud, bool gl2); typedef int (*OpenURIFunc)(const String &); -typedef String (*GetDataDirFunc)(); +typedef String (*GetUserDataDirFunc)(); typedef String (*GetLocaleFunc)(); typedef String (*GetModelFunc)(); typedef int (*GetScreenDPIFunc)(); @@ -106,8 +104,6 @@ private: bool use_16bits_fbo; VisualServer *visual_server; - PhysicsServer *physics_server; - Physics2DServer *physics_2d_server; mutable String data_dir_cache; @@ -121,7 +117,7 @@ private: MainLoop *main_loop; OpenURIFunc open_uri_func; - GetDataDirFunc get_data_dir_func; + GetUserDataDirFunc get_user_data_dir_func; GetLocaleFunc get_locale_func; GetModelFunc get_model_func; GetScreenDPIFunc get_screen_dpi_func; @@ -146,14 +142,11 @@ public: virtual int get_video_driver_count() const; virtual const char *get_video_driver_name(int p_driver) const; - virtual VideoMode get_default_video_mode() const; - virtual int get_audio_driver_count() const; virtual const char *get_audio_driver_name(int p_driver) const; - virtual void initialize_logger(); virtual void initialize_core(); - virtual void initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver); + virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver); virtual void set_main_loop(MainLoop *p_main_loop); virtual void delete_main_loop(); @@ -166,6 +159,8 @@ public: virtual void alert(const String &p_alert, const String &p_title = "ALERT!"); + virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false); + virtual void set_mouse_show(bool p_show); virtual void set_mouse_grab(bool p_grab); virtual bool is_mouse_grab_enabled() const; @@ -190,6 +185,7 @@ public: virtual bool can_draw() const; virtual void set_cursor_shape(CursorShape p_shape); + virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); void main_loop_begin(); bool main_loop_iterate(); @@ -215,7 +211,7 @@ public: virtual void set_screen_orientation(ScreenOrientation p_orientation); virtual Error shell_open(String p_uri); - virtual String get_data_dir() const; + virtual String get_user_data_dir() const; virtual String get_resource_dir() const; virtual String get_locale() const; virtual String get_model_name() const; @@ -226,6 +222,7 @@ public: virtual String get_system_dir(SystemDir p_dir) const; void process_accelerometer(const Vector3 &p_accelerometer); + void process_gravity(const Vector3 &p_gravity); void process_magnetometer(const Vector3 &p_magnetometer); void process_gyroscope(const Vector3 &p_gyroscope); void process_touch(int p_what, int p_pointer, const Vector<TouchPos> &p_points); @@ -243,7 +240,7 @@ public: void joy_connection_changed(int p_device, bool p_connected, String p_name); virtual bool _check_internal_feature_support(const String &p_feature); - OS_Android(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, OpenURIFunc p_open_uri_func, GetDataDirFunc p_get_data_dir_func, GetLocaleFunc p_get_locale_func, GetModelFunc p_get_model_func, GetScreenDPIFunc p_get_screen_dpi_func, ShowVirtualKeyboardFunc p_show_vk, HideVirtualKeyboardFunc p_hide_vk, VirtualKeyboardHeightFunc p_vk_height_func, SetScreenOrientationFunc p_screen_orient, GetUniqueIDFunc p_get_unique_id, GetSystemDirFunc p_get_sdir_func, VideoPlayFunc p_video_play_func, VideoIsPlayingFunc p_video_is_playing_func, VideoPauseFunc p_video_pause_func, VideoStopFunc p_video_stop_func, SetKeepScreenOnFunc p_set_keep_screen_on_func, AlertFunc p_alert_func, bool p_use_apk_expansion); + OS_Android(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, OpenURIFunc p_open_uri_func, GetUserDataDirFunc p_get_user_data_dir_func, GetLocaleFunc p_get_locale_func, GetModelFunc p_get_model_func, GetScreenDPIFunc p_get_screen_dpi_func, ShowVirtualKeyboardFunc p_show_vk, HideVirtualKeyboardFunc p_hide_vk, VirtualKeyboardHeightFunc p_vk_height_func, SetScreenOrientationFunc p_screen_orient, GetUniqueIDFunc p_get_unique_id, GetSystemDirFunc p_get_sdir_func, VideoPlayFunc p_video_play_func, VideoIsPlayingFunc p_video_is_playing_func, VideoPauseFunc p_video_pause_func, VideoStopFunc p_video_stop_func, SetKeepScreenOnFunc p_set_keep_screen_on_func, AlertFunc p_alert_func, bool p_use_apk_expansion); ~OS_Android(); }; diff --git a/platform/android/platform_config.h b/platform/android/platform_config.h index b1c3f027f3..299d8563dd 100644 --- a/platform/android/platform_config.h +++ b/platform/android/platform_config.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,4 +27,6 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include <alloca.h> +#include <malloc.h> diff --git a/platform/android/power_android.cpp b/platform/android/power_android.cpp index 48c9377a5a..51283183df 100644 --- a/platform/android/power_android.cpp +++ b/platform/android/power_android.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -245,8 +245,10 @@ int power_android::get_power_percent_left() { } } -power_android::power_android() - : nsecs_left(-1), percent_left(-1), power_state(OS::POWERSTATE_UNKNOWN) { +power_android::power_android() : + nsecs_left(-1), + percent_left(-1), + power_state(OS::POWERSTATE_UNKNOWN) { } power_android::~power_android() { diff --git a/platform/android/power_android.h b/platform/android/power_android.h index 4c7af1c771..f0d1bee1e2 100644 --- a/platform/android/power_android.h +++ b/platform/android/power_android.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ diff --git a/platform/android/thread_jandroid.cpp b/platform/android/thread_jandroid.cpp index 79488197ea..e85813605f 100644 --- a/platform/android/thread_jandroid.cpp +++ b/platform/android/thread_jandroid.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "thread_jandroid.h" #include "core/safe_refcount.h" diff --git a/platform/android/thread_jandroid.h b/platform/android/thread_jandroid.h index b854a83e23..2bb64f3db2 100644 --- a/platform/android/thread_jandroid.h +++ b/platform/android/thread_jandroid.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef THREAD_POSIX_H #define THREAD_POSIX_H diff --git a/platform/haiku/SCsub b/platform/haiku/SCsub index d0c244a194..592f56bbbf 100644 --- a/platform/haiku/SCsub +++ b/platform/haiku/SCsub @@ -12,7 +12,7 @@ common_haiku = [ 'audio_driver_media_kit.cpp' ] -target = env.Program( +target = env.add_program( '#bin/godot', ['godot_haiku.cpp'] + common_haiku ) diff --git a/platform/haiku/audio_driver_media_kit.cpp b/platform/haiku/audio_driver_media_kit.cpp index 4d8d580660..278a994c54 100644 --- a/platform/haiku/audio_driver_media_kit.cpp +++ b/platform/haiku/audio_driver_media_kit.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "audio_driver_media_kit.h" #ifdef MEDIA_KIT_ENABLED diff --git a/platform/haiku/audio_driver_media_kit.h b/platform/haiku/audio_driver_media_kit.h index 2f4752a973..a09403e7d6 100644 --- a/platform/haiku/audio_driver_media_kit.h +++ b/platform/haiku/audio_driver_media_kit.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "servers/audio_server.h" #ifdef MEDIA_KIT_ENABLED diff --git a/platform/haiku/context_gl_haiku.cpp b/platform/haiku/context_gl_haiku.cpp index 80d0bd78d5..41df17d2d1 100644 --- a/platform/haiku/context_gl_haiku.cpp +++ b/platform/haiku/context_gl_haiku.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "context_gl_haiku.h" #if defined(OPENGL_ENABLED) diff --git a/platform/haiku/context_gl_haiku.h b/platform/haiku/context_gl_haiku.h index a9a13a2b7f..74f09984f2 100644 --- a/platform/haiku/context_gl_haiku.h +++ b/platform/haiku/context_gl_haiku.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef CONTEXT_GL_HAIKU_H #define CONTEXT_GL_HAIKU_H diff --git a/platform/haiku/detect.py b/platform/haiku/detect.py index 50f9783dd2..7c62654ef6 100644 --- a/platform/haiku/detect.py +++ b/platform/haiku/detect.py @@ -67,7 +67,7 @@ def configure(env): ## Flags env.Append(CPPPATH=['#platform/haiku']) - env.Append(CPPFLAGS=['-DUNIX_ENABLED', '-DOPENGL_ENABLED', '-DGLES2_ENABLED', '-DGLES_OVER_GL']) + env.Append(CPPFLAGS=['-DUNIX_ENABLED', '-DOPENGL_ENABLED', '-DGLES_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 diff --git a/platform/haiku/godot_haiku.cpp b/platform/haiku/godot_haiku.cpp index ffc6e56cdd..b042d81650 100644 --- a/platform/haiku/godot_haiku.cpp +++ b/platform/haiku/godot_haiku.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "main/main.h" #include "os_haiku.h" diff --git a/platform/haiku/haiku_application.cpp b/platform/haiku/haiku_application.cpp index d23b9e27d8..4f5e3e42a2 100644 --- a/platform/haiku/haiku_application.cpp +++ b/platform/haiku/haiku_application.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,8 +27,9 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "haiku_application.h" -HaikuApplication::HaikuApplication() - : BApplication("application/x-vnd.godot") { +HaikuApplication::HaikuApplication() : + BApplication("application/x-vnd.godot") { } diff --git a/platform/haiku/haiku_application.h b/platform/haiku/haiku_application.h index 2162cae892..f92969bbb1 100644 --- a/platform/haiku/haiku_application.h +++ b/platform/haiku/haiku_application.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef HAIKU_APPLICATION_H #define HAIKU_APPLICATION_H diff --git a/platform/haiku/haiku_direct_window.cpp b/platform/haiku/haiku_direct_window.cpp index 24a8a4b17b..b234a2ff91 100644 --- a/platform/haiku/haiku_direct_window.cpp +++ b/platform/haiku/haiku_direct_window.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include <UnicodeChar.h> #include "haiku_direct_window.h" @@ -34,8 +35,8 @@ #include "main/main.h" #include "os/keyboard.h" -HaikuDirectWindow::HaikuDirectWindow(BRect p_frame) - : BDirectWindow(p_frame, "Godot", B_TITLED_WINDOW, B_QUIT_ON_WINDOW_CLOSE) { +HaikuDirectWindow::HaikuDirectWindow(BRect p_frame) : + BDirectWindow(p_frame, "Godot", B_TITLED_WINDOW, B_QUIT_ON_WINDOW_CLOSE) { last_mouse_pos_valid = false; last_buttons_state = 0; last_button_mask = 0; diff --git a/platform/haiku/haiku_direct_window.h b/platform/haiku/haiku_direct_window.h index 0f3e9de85b..55c2f5fccc 100644 --- a/platform/haiku/haiku_direct_window.h +++ b/platform/haiku/haiku_direct_window.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef HAIKU_DIRECT_WINDOW_H #define HAIKU_DIRECT_WINDOW_H diff --git a/platform/haiku/haiku_gl_view.cpp b/platform/haiku/haiku_gl_view.cpp index d898bd1a5d..a40cbe5765 100644 --- a/platform/haiku/haiku_gl_view.cpp +++ b/platform/haiku/haiku_gl_view.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,11 +27,12 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "haiku_gl_view.h" #include "main/main.h" -HaikuGLView::HaikuGLView(BRect frame, uint32 type) - : BGLView(frame, "GodotGLView", B_FOLLOW_ALL_SIDES, 0, type) { +HaikuGLView::HaikuGLView(BRect frame, uint32 type) : + BGLView(frame, "GodotGLView", B_FOLLOW_ALL_SIDES, 0, type) { } void HaikuGLView::AttachedToWindow(void) { diff --git a/platform/haiku/haiku_gl_view.h b/platform/haiku/haiku_gl_view.h index 10facfe0c3..1a694dc13b 100644 --- a/platform/haiku/haiku_gl_view.h +++ b/platform/haiku/haiku_gl_view.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef HAIKU_GL_VIEW_H #define HAIKU_GL_VIEW_H diff --git a/platform/haiku/key_mapping_haiku.cpp b/platform/haiku/key_mapping_haiku.cpp index 074f03f471..28a282e25c 100644 --- a/platform/haiku/key_mapping_haiku.cpp +++ b/platform/haiku/key_mapping_haiku.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include <InterfaceDefs.h> #include "key_mapping_haiku.h" diff --git a/platform/haiku/key_mapping_haiku.h b/platform/haiku/key_mapping_haiku.h index 943ba21e7c..917151bc4a 100644 --- a/platform/haiku/key_mapping_haiku.h +++ b/platform/haiku/key_mapping_haiku.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef KEY_MAPPING_HAIKU_H #define KEY_MAPPING_HAIKU_H diff --git a/platform/haiku/os_haiku.cpp b/platform/haiku/os_haiku.cpp index 1d52752f21..97fab5ca0d 100644 --- a/platform/haiku/os_haiku.cpp +++ b/platform/haiku/os_haiku.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "os_haiku.h" #include "drivers/gles3/rasterizer_gles3.h" @@ -76,14 +77,10 @@ int OS_Haiku::get_video_driver_count() const { } const char *OS_Haiku::get_video_driver_name(int p_driver) const { - return "GLES2"; -} - -OS::VideoMode OS_Haiku::get_default_video_mode() const { - return OS::VideoMode(800, 600, false); + return "GLES3"; } -void OS_Haiku::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { +Error OS_Haiku::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { main_loop = NULL; current_video_mode = p_desired; @@ -109,13 +106,16 @@ void OS_Haiku::initialize(const VideoMode &p_desired, int p_video_driver, int p_ context_gl = memnew(ContextGL_Haiku(window)); context_gl->initialize(); context_gl->make_current(); + context_gl->set_use_vsync(current_video_mode.use_vsync); + + /* Port to GLES 3 rasterizer */ + //rasterizer = memnew(RasterizerGLES2); - rasterizer = memnew(RasterizerGLES2); #endif visual_server = memnew(VisualServerRaster(rasterizer)); - ERR_FAIL_COND(!visual_server); + ERR_FAIL_COND_V(!visual_server, ERR_UNAVAILABLE); // TODO: enable multithreaded VS /* @@ -130,16 +130,11 @@ void OS_Haiku::initialize(const VideoMode &p_desired, int p_video_driver, int p_ window->Show(); visual_server->init(); - physics_server = memnew(PhysicsServerSW); - physics_server->init(); - physics_2d_server = memnew(Physics2DServerSW); - // TODO: enable multithreaded PS - //physics_2d_server = Physics2DServerWrapMT::init_server<Physics2DServerSW>(); - physics_2d_server->init(); - AudioDriverManager::initialize(p_audio_driver); power_manager = memnew(PowerHaiku); + + return OK; } void OS_Haiku::finalize() { @@ -153,12 +148,6 @@ void OS_Haiku::finalize() { memdelete(visual_server); memdelete(rasterizer); - physics_server->finish(); - memdelete(physics_server); - - physics_2d_server->finish(); - memdelete(physics_2d_server); - memdelete(input); #if defined(OPENGL_ENABLED) @@ -214,6 +203,10 @@ void OS_Haiku::set_cursor_shape(CursorShape p_shape) { //ERR_PRINT("set_cursor_shape() NOT IMPLEMENTED"); } +void OS_Haiku::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { + // TODO +} + int OS_Haiku::get_screen_count() const { // TODO: implement get_screen_count() return 1; @@ -331,3 +324,36 @@ bool OS_Haiku::_check_internal_feature_support(const String &p_feature) { return p_feature == "pc" || p_feature == "s3tc"; } + +String OS_Haiku::get_config_path() const { + + if (has_environment("XDG_CONFIG_HOME")) { + return get_environment("XDG_CONFIG_HOME"); + } else if (has_environment("HOME")) { + return get_environment("HOME").plus_file(".config"); + } else { + return "."; + } +} + +String OS_Haiku::get_data_path() const { + + if (has_environment("XDG_DATA_HOME")) { + return get_environment("XDG_DATA_HOME"); + } else if (has_environment("HOME")) { + return get_environment("HOME").plus_file(".local/share"); + } else { + return get_config_path(); + } +} + +String OS_Haiku::get_cache_path() const { + + if (has_environment("XDG_CACHE_HOME")) { + return get_environment("XDG_CACHE_HOME"); + } else if (has_environment("HOME")) { + return get_environment("HOME").plus_file(".cache"); + } else { + return get_config_path(); + } +} diff --git a/platform/haiku/os_haiku.h b/platform/haiku/os_haiku.h index d929f7e43b..615ae682ef 100644 --- a/platform/haiku/os_haiku.h +++ b/platform/haiku/os_haiku.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef OS_HAIKU_H #define OS_HAIKU_H @@ -38,8 +39,6 @@ #include "main/input_default.h" #include "power_haiku.h" #include "servers/audio_server.h" -#include "servers/physics_2d/physics_2d_server_sw.h" -#include "servers/physics_server.h" #include "servers/visual/rasterizer.h" #include "servers/visual_server.h" @@ -52,8 +51,6 @@ private: Rasterizer *rasterizer; VisualServer *visual_server; VideoMode current_video_mode; - PhysicsServer *physics_server; - Physics2DServer *physics_2d_server; PowerHaiku *power_manager; #ifdef MEDIA_KIT_ENABLED @@ -69,9 +66,8 @@ private: protected: virtual int get_video_driver_count() const; virtual const char *get_video_driver_name(int p_driver) const; - virtual VideoMode get_default_video_mode() const; - virtual void initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver); + virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver); virtual void finalize(); virtual void set_main_loop(MainLoop *p_main_loop); @@ -92,6 +88,7 @@ public: virtual Point2 get_mouse_position() const; virtual int get_mouse_button_state() const; virtual void set_cursor_shape(CursorShape p_shape); + virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); virtual int get_screen_count() const; virtual int get_current_screen() const; @@ -122,6 +119,10 @@ public: virtual int get_power_percent_left(); virtual bool _check_internal_feature_support(const String &p_feature); + + virtual String get_config_path() const; + virtual String get_data_path() const; + virtual String get_cache_path() const; }; #endif diff --git a/platform/haiku/platform_config.h b/platform/haiku/platform_config.h index 27731a7b74..bbd72dfeb6 100644 --- a/platform/haiku/platform_config.h +++ b/platform/haiku/platform_config.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include <alloca.h> // for ifaddrs.h needed in drivers/unix/ip_unix.cpp diff --git a/platform/haiku/power_haiku.cpp b/platform/haiku/power_haiku.cpp index 8718b317a3..2a26dd0f9c 100644 --- a/platform/haiku/power_haiku.cpp +++ b/platform/haiku/power_haiku.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -64,8 +64,10 @@ int PowerX11::get_power_percent_left() { } } -PowerHaiku::PowerHaiku() - : nsecs_left(-1), percent_left(-1), power_state(OS::POWERSTATE_UNKNOWN) { +PowerHaiku::PowerHaiku() : + nsecs_left(-1), + percent_left(-1), + power_state(OS::POWERSTATE_UNKNOWN) { } PowerHaiku::~PowerHaiku() { diff --git a/platform/haiku/power_haiku.h b/platform/haiku/power_haiku.h index 59a93cd976..2fe85cd23d 100644 --- a/platform/haiku/power_haiku.h +++ b/platform/haiku/power_haiku.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ diff --git a/platform/iphone/SCsub b/platform/iphone/SCsub index 61798c5f87..6b5f30dc41 100644 --- a/platform/iphone/SCsub +++ b/platform/iphone/SCsub @@ -3,7 +3,7 @@ Import('env') iphone_lib = [ - + 'godot_iphone.cpp', 'os_iphone.cpp', 'sem_iphone.cpp', 'gl_view.mm', @@ -17,10 +17,10 @@ iphone_lib = [ ] env_ios = env.Clone() +ios_lib = env_ios.add_library('iphone', iphone_lib) -obj = env_ios.Object('godot_iphone.cpp') +def combine_libs(target=None, source=None, env=None): + lib_path = target[0].srcnode().abspath + env.Execute('$IPHONEPATH/usr/bin/libtool -static -o "' + lib_path + '" ' + ' '.join([('"' + lib.srcnode().abspath + '"') for lib in source])) -prog = None -prog = env_ios.Program('#bin/godot', [obj] + iphone_lib) -action = "$IPHONEPATH/usr/bin/dsymutil " + File(prog)[0].path + " -o " + File(prog)[0].path + ".dSYM" -env.AddPostAction(prog, action) +combine_command = env_ios.Command('#bin/libgodot' + env_ios['LIBSUFFIX'], [ios_lib] + env_ios['LIBS'], combine_libs) diff --git a/platform/iphone/app_delegate.h b/platform/iphone/app_delegate.h index 4a1f52565a..f14864b5b7 100644 --- a/platform/iphone/app_delegate.h +++ b/platform/iphone/app_delegate.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #import "gl_view.h" #import "view_controller.h" #import <UIKit/UIKit.h> diff --git a/platform/iphone/app_delegate.mm b/platform/iphone/app_delegate.mm index 65cafbd6d4..5c3799ab09 100644 --- a/platform/iphone/app_delegate.mm +++ b/platform/iphone/app_delegate.mm @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #import "app_delegate.h" #include "core/project_settings.h" @@ -35,21 +36,6 @@ #include "main/main.h" #include "os_iphone.h" -#ifdef MODULE_FACEBOOKSCORER_IOS_ENABLED -#include "modules/FacebookScorer_ios/FacebookScorer.h" -#endif - -#ifdef MODULE_GAME_ANALYTICS_ENABLED -#import "modules/game_analytics/ios/MobileAppTracker.framework/Headers/MobileAppTracker.h" -//#import "modules/game_analytics/ios/MobileAppTracker.h" -#import <AdSupport/AdSupport.h> -#endif - -#ifdef MODULE_PARSE_ENABLED -#import "FBSDKCoreKit/FBSDKCoreKit.h" -#import <Parse/Parse.h> -#endif - #import "GameController/GameController.h" #define kFilteringFactor 0.1 @@ -418,11 +404,7 @@ static int frame_count = 0; OSIPhone::get_singleton()->set_unique_id(String::utf8([uuid UTF8String])); }; break; - /* - case 1: { - ++frame_count; - }; break; -*/ + case 1: { Main::setup2(); @@ -453,11 +435,7 @@ static int frame_count = 0; } }; break; - /* - case 3: { - ++frame_count; - }; break; -*/ + case 2: { Main::start(); @@ -558,15 +536,11 @@ static int frame_count = 0; }; - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application { - - printf("****************** did receive memory warning!\n"); OS::get_singleton()->get_main_loop()->notification( MainLoop::NOTIFICATION_OS_MEMORY_WARNING); }; -- (void)applicationDidFinishLaunching:(UIApplication *)application { - - printf("**************** app delegate init\n"); +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { CGRect rect = [[UIScreen mainScreen] bounds]; [application setStatusBarHidden:YES withAnimation:UIStatusBarAnimationNone]; @@ -603,7 +577,7 @@ static int frame_count = 0; if (err != 0) { // bail, things did not go very well for us, should probably output a message on screen with our error code... exit(0); - return; + return FALSE; }; view_controller = [[ViewController alloc] init]; @@ -638,42 +612,13 @@ static int frame_count = 0; mainViewController = view_controller; -#ifdef MODULE_GAME_ANALYTICS_ENABLED - printf("********************* didFinishLaunchingWithOptions\n"); - if (!ProjectSettings::get_singleton()->has("mobileapptracker/advertiser_id")) { - return; - } - if (!ProjectSettings::get_singleton()->has("mobileapptracker/conversion_key")) { - return; - } - - String adid = GLOBAL_DEF("mobileapptracker/advertiser_id", ""); - String convkey = GLOBAL_DEF("mobileapptracker/conversion_key", ""); - - NSString *advertiser_id = - [NSString stringWithUTF8String:adid.utf8().get_data()]; - NSString *conversion_key = - [NSString stringWithUTF8String:convkey.utf8().get_data()]; - - // Account Configuration info - must be set - [MobileAppTracker initializeWithMATAdvertiserId:advertiser_id - MATConversionKey:conversion_key]; - - // Used to pass us the IFA, enables highly accurate 1-to-1 attribution. - // Required for many advertising networks. - [MobileAppTracker - setAppleAdvertisingIdentifier:[[ASIdentifierManager sharedManager] - advertisingIdentifier] - advertisingTrackingEnabled:[[ASIdentifierManager sharedManager] - isAdvertisingTrackingEnabled]]; + // prevent to stop music in another background app + [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:nil]; -#endif + return TRUE; }; - (void)applicationWillTerminate:(UIApplication *)application { - - printf("********************* will terminate\n"); - [self deinitGameControllers]; if (motionInitialised) { @@ -688,7 +633,6 @@ static int frame_count = 0; }; - (void)applicationDidEnterBackground:(UIApplication *)application { - printf("********************* did enter background\n"); ///@TODO maybe add pause motionManager? and where would we unpause it? if (OS::get_singleton()->get_main_loop()) @@ -702,24 +646,17 @@ static int frame_count = 0; } - (void)applicationWillEnterForeground:(UIApplication *)application { - printf("********************* did enter foreground\n"); // OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN); [view_controller.view startAnimation]; } - (void)applicationWillResignActive:(UIApplication *)application { - printf("********************* will resign active\n"); // OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT); [view_controller.view stopAnimation]; // FIXME: pause seems to be recommended elsewhere } - (void)applicationDidBecomeActive:(UIApplication *)application { - printf("********************* did become active\n"); -#ifdef MODULE_GAME_ANALYTICS_ENABLED - printf("********************* mobile app tracker found\n"); - [MobileAppTracker measureSession]; -#endif if (OS::get_singleton()->get_main_loop()) OS::get_singleton()->get_main_loop()->notification( MainLoop::NOTIFICATION_WM_FOCUS_IN); @@ -735,65 +672,6 @@ static int frame_count = 0; AudioDriverCoreAudio::get_singleton()->start(); } -- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url { -#ifdef MODULE_FACEBOOKSCORER_IOS_ENABLED - return [[[FacebookScorer sharedInstance] facebook] handleOpenURL:url]; -#else - return false; -#endif -} - -// For 4.2+ support -- (BOOL)application:(UIApplication *)application - openURL:(NSURL *)url - sourceApplication:(NSString *)sourceApplication - annotation:(id)annotation { -#ifdef MODULE_PARSE_ENABLED - NSLog(@"Handling application openURL"); - return - [[FBSDKApplicationDelegate sharedInstance] application:application - openURL:url - sourceApplication:sourceApplication - annotation:annotation]; -#endif - -#ifdef MODULE_FACEBOOKSCORER_IOS_ENABLED - return [[[FacebookScorer sharedInstance] facebook] handleOpenURL:url]; -#else - return false; -#endif -} - -- (void)application:(UIApplication *)application - didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { -#ifdef MODULE_PARSE_ENABLED - // Store the deviceToken in the current installation and save it to Parse. - PFInstallation *currentInstallation = [PFInstallation currentInstallation]; - // NSString* token = [[NSString alloc] initWithData:deviceToken - // encoding:NSUTF8StringEncoding]; - NSLog(@"Device Token : %@ ", deviceToken); - [currentInstallation setDeviceTokenFromData:deviceToken]; - [currentInstallation saveInBackground]; -#endif -} - -- (void)application:(UIApplication *)application - didReceiveRemoteNotification:(NSDictionary *)userInfo { -#ifdef MODULE_PARSE_ENABLED - [PFPush handlePush:userInfo]; - NSDictionary *aps = - [userInfo objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey]; - NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; - - NSLog(@"Push Notification Payload (app active) %@", aps); - [defaults setObject:aps forKey:@"notificationInfo"]; - [defaults synchronize]; - if (application.applicationState == UIApplicationStateInactive) { - [PFAnalytics trackAppOpenedWithRemoteNotificationPayload:userInfo]; - } -#endif -} - - (void)dealloc { [window release]; [super dealloc]; diff --git a/platform/iphone/detect.py b/platform/iphone/detect.py index 00d8a59f74..25674c2b47 100644 --- a/platform/iphone/detect.py +++ b/platform/iphone/detect.py @@ -47,8 +47,8 @@ def configure(env): 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.Append(CPPFLAGS=['-O2', '-ftree-vectorize', '-fomit-frame-pointer', '-ffast-math', '-funsafe-math-optimizations']) + env.Append(LINKFLAGS=['-O2']) if env["target"] == "release_debug": env.Append(CPPFLAGS=['-DDEBUG_ENABLED']) @@ -56,11 +56,18 @@ def configure(env): elif (env["target"] == "debug"): env.Append(CPPFLAGS=['-D_DEBUG', '-DDEBUG=1', '-gdwarf-2', '-O0', '-DDEBUG_ENABLED', '-DDEBUG_MEMORY_ENABLED']) + if (env["use_lto"]): + env.Append(CPPFLAGS=['-flto']) + env.Append(LINKFLAGS=['-flto']) + ## Architecture + if env["ios_sim"] and not ("arch" in env): + env["arch"] = "x86" - if env["ios_sim"] or env["arch"] == "x86": # i386, simulator - env["arch"] = "x86" + if env["arch"] == "x86": # i386, simulator env["bits"] = "32" + elif env["arch"] == "x86_64": + env["bits"] = "64" elif (env["arch"] == "arm" or env["arch"] == "arm32" or env["arch"] == "armv7" or env["bits"] == "32"): # arm env["arch"] = "arm" env["bits"] = "32" @@ -72,18 +79,30 @@ def configure(env): 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' + compiler_path = '$IPHONEPATH/usr/bin/${ios_triple}' + s_compiler_path = '$IPHONEPATH/Developer/usr/bin/' + + ccache_path = os.environ.get("CCACHE") + if ccache_path == None: + env['CC'] = compiler_path + 'clang' + env['CXX'] = compiler_path + 'clang++' + env['S_compiler'] = s_compiler_path + 'gcc' + else: + # there aren't any ccache wrappers available for iOS, + # to enable caching we need to prepend the path to the ccache binary + env['CC'] = ccache_path + ' ' + compiler_path + 'clang' + env['CXX'] = ccache_path + ' ' + compiler_path + 'clang++' + env['S_compiler'] = ccache_path + ' ' + s_compiler_path + 'gcc' + env['AR'] = compiler_path + 'ar' + env['RANLIB'] = compiler_path + 'ranlib' ## Compile flags - if (env["arch"] == "x86"): + if (env["arch"] == "x86" or env["arch"] == "x86_64"): env['IPHONEPLATFORM'] = 'iPhoneSimulator' - env['ENV']['MACOSX_DEPLOYMENT_TARGET'] = '10.6' - env.Append(CCFLAGS='-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\\\"'.split()) + env['ENV']['MACOSX_DEPLOYMENT_TARGET'] = '10.9' + arch_flag = "i386" if env["arch"] == "x86" else env["arch"] + env.Append(CCFLAGS=('-arch ' + arch_flag + ' -fobjc-abi-version=2 -fobjc-legacy-dispatch -fmessage-length=0 -fpascal-strings -fblocks -fasm-blocks -isysroot $IPHONESDK -mios-simulator-version-min=9.0 -DCUSTOM_MATRIX_TRANSFORM_H=\\\"build/iphone/matrix4_iphone.h\\\" -DCUSTOM_VECTOR3_TRANSFORM_H=\\\"build/iphone/vector3_iphone.h\\\"').split()) elif (env["arch"] == "arm"): env.Append(CCFLAGS='-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'.split()) elif (env["arch"] == "arm64"): @@ -98,8 +117,9 @@ def configure(env): ## Link flags - if (env["arch"] == "x86"): - env.Append(LINKFLAGS=['-arch', 'i386', '-mios-simulator-version-min=4.3', + if (env["arch"] == "x86" or env["arch"] == "x86_64"): + arch_flag = "i386" if env["arch"] == "x86" else env["arch"] + env.Append(LINKFLAGS=['-arch', arch_flag, '-mios-simulator-version-min=9.0', '-isysroot', '$IPHONESDK', '-Xlinker', '-objc_abi_version', @@ -148,7 +168,7 @@ def configure(env): env['ENV']['CODESIGN_ALLOCATE'] = '/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/codesign_allocate' env.Append(CPPPATH=['#platform/iphone']) - env.Append(CPPFLAGS=['-DIPHONE_ENABLED', '-DUNIX_ENABLED', '-DGLES2_ENABLED', '-DMPC_FIXED_POINT', '-DCOREAUDIO_ENABLED']) + env.Append(CPPFLAGS=['-DIPHONE_ENABLED', '-DUNIX_ENABLED', '-DGLES_ENABLED', '-DMPC_FIXED_POINT', '-DCOREAUDIO_ENABLED']) # TODO: Move that to opus module's config if 'module_opus_enabled' in env and env['module_opus_enabled']: diff --git a/platform/iphone/export/export.cpp b/platform/iphone/export/export.cpp index 0507ef19d6..e3119814f4 100644 --- a/platform/iphone/export/export.cpp +++ b/platform/iphone/export/export.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -37,7 +37,7 @@ #include "io/zip_io.h" #include "os/file_access.h" #include "os/os.h" -#include "platform/osx/logo.gen.h" +#include "platform/iphone/logo.gen.h" #include "project_settings.h" #include "string.h" #include "version.h" @@ -56,11 +56,49 @@ class EditorExportPlatformIOS : public EditorExportPlatform { static Error _walk_dir_recursive(DirAccess *p_da, FileHandler p_handler, void *p_userdata); static Error _codesign(String p_file, void *p_userdata); - void _fix_config_file(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &pfile, const String &p_name, const String &p_binary, bool p_debug); - static Error _export_dylibs(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total); + struct IOSConfigData { + String pkg_name; + String binary_name; + String plist_content; + String architectures; + String linker_flags; + String cpp_code; + }; + + struct ExportArchitecture { + String name; + bool is_default; + + ExportArchitecture() : + name(""), + is_default(false) { + } + + ExportArchitecture(String p_name, bool p_is_default) { + name = p_name; + is_default = p_is_default; + } + }; + + struct IOSExportAsset { + String exported_path; + bool is_framework; // framework is anything linked to the binary, otherwise it's a resource + }; + + String _get_additional_plist_content(); + String _get_linker_flags(); + String _get_cpp_code(); + void _fix_config_file(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &pfile, const IOSConfigData &p_config, bool p_debug); Error _export_loading_screens(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir); Error _export_icons(const Ref<EditorExportPreset> &p_preset, const String &p_iconset_dir); + Vector<ExportArchitecture> _get_supported_architectures(); + Vector<String> _get_preset_architectures(const Ref<EditorExportPreset> &p_preset); + + void _add_assets_to_project(Vector<uint8_t> &p_project_data, const Vector<IOSExportAsset> &p_additional_assets); + Error _export_additional_assets(const String &p_out_dir, const Vector<String> &p_assets, bool p_is_framework, Vector<IOSExportAsset> &r_exported_assets); + Error _export_additional_assets(const String &p_out_dir, const Vector<SharedObject> &p_libraries, Vector<IOSExportAsset> &r_exported_assets); + protected: virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features); virtual void get_export_options(List<ExportOption> *r_options); @@ -70,7 +108,7 @@ public: virtual String get_os_name() const { return "iOS"; } virtual Ref<Texture> get_logo() const { return logo; } - virtual String get_binary_extension() const { return "ipa"; } + virtual String get_binary_extension(const Ref<EditorExportPreset> &p_preset) const { return "ipa"; } virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0); virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const; @@ -96,6 +134,17 @@ void EditorExportPlatformIOS::get_preset_features(const Ref<EditorExportPreset> if (p_preset->get("texture_format/etc2")) { r_features->push_back("etc2"); } + Vector<String> architectures = _get_preset_architectures(p_preset); + for (int i = 0; i < architectures.size(); ++i) { + r_features->push_back(architectures[i]); + } +} + +Vector<EditorExportPlatformIOS::ExportArchitecture> EditorExportPlatformIOS::_get_supported_architectures() { + Vector<ExportArchitecture> archs; + archs.push_back(ExportArchitecture("armv7", true)); + archs.push_back(ExportArchitecture("arm64", true)); + return archs; } void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options) { @@ -120,7 +169,6 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options) r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version"), "1.0")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version"), "1.0")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/bits_mode", PROPERTY_HINT_ENUM, "Fat (32 & 64 bits),64 bits,32 bits"), 1)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/iphone_120x120", PROPERTY_HINT_FILE, "png"), "")); // Home screen on iPhone/iPod Touch with retina display r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/ipad_76x76", PROPERTY_HINT_FILE, "png"), "")); // Home screen on iPad @@ -145,10 +193,13 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options) 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 */ + Vector<ExportArchitecture> architectures = _get_supported_architectures(); + for (int i = 0; i < architectures.size(); ++i) { + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "architectures/" + architectures[i].name), architectures[i].is_default)); + } } -void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &pfile, const String &p_name, const String &p_binary, bool p_debug) { +void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &pfile, const IOSConfigData &p_config, bool p_debug) { static const String export_method_string[] = { "app-store", "development", @@ -158,13 +209,12 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_ String str; String strnew; str.parse_utf8((const char *)pfile.ptr(), pfile.size()); - print_line(str); Vector<String> lines = str.split("\n"); for (int i = 0; i < lines.size(); i++) { if (lines[i].find("$binary") != -1) { - strnew += lines[i].replace("$binary", p_binary) + "\n"; + strnew += lines[i].replace("$binary", p_config.binary_name) + "\n"; } else if (lines[i].find("$name") != -1) { - strnew += lines[i].replace("$name", p_name) + "\n"; + strnew += lines[i].replace("$name", p_config.pkg_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) { @@ -186,10 +236,21 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_ strnew += lines[i].replace("$provisioning_profile_uuid_release", p_preset->get("application/provisioning_profile_uuid_release")) + "\n"; } else if (lines[i].find("$provisioning_profile_uuid_debug") != -1) { strnew += lines[i].replace("$provisioning_profile_uuid_debug", p_preset->get("application/provisioning_profile_uuid_debug")) + "\n"; + } else if (lines[i].find("$provisioning_profile_uuid") != -1) { + String uuid = p_debug ? p_preset->get("application/provisioning_profile_uuid_debug") : p_preset->get("application/provisioning_profile_uuid_release"); + strnew += lines[i].replace("$provisioning_profile_uuid", uuid) + "\n"; } else if (lines[i].find("$code_sign_identity_debug") != -1) { strnew += lines[i].replace("$code_sign_identity_debug", p_preset->get("application/code_sign_identity_debug")) + "\n"; } else if (lines[i].find("$code_sign_identity_release") != -1) { strnew += lines[i].replace("$code_sign_identity_release", p_preset->get("application/code_sign_identity_release")) + "\n"; + } else if (lines[i].find("$additional_plist_content") != -1) { + strnew += lines[i].replace("$additional_plist_content", p_config.plist_content) + "\n"; + } else if (lines[i].find("$godot_archs") != -1) { + strnew += lines[i].replace("$godot_archs", p_config.architectures) + "\n"; + } else if (lines[i].find("$linker_flags") != -1) { + strnew += lines[i].replace("$linker_flags", p_config.linker_flags) + "\n"; + } else if (lines[i].find("$cpp_code") != -1) { + strnew += lines[i].replace("$cpp_code", p_config.cpp_code) + "\n"; } else { strnew += lines[i] + "\n"; } @@ -204,27 +265,37 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_ } } -Error EditorExportPlatformIOS::_export_dylibs(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total) { - if (!p_path.ends_with(".dylib")) return OK; - const String &dest_dir = *(String *)p_userdata; - String rel_path = p_path.replace_first("res://", "dylibs/"); - DirAccess *dest_dir_access = DirAccess::open(dest_dir); - ERR_FAIL_COND_V(!dest_dir_access, ERR_CANT_OPEN); - - String base_dir = rel_path.get_base_dir(); - Error make_dir_err = OK; - if (!dest_dir_access->dir_exists(base_dir)) { - make_dir_err = dest_dir_access->make_dir_recursive(base_dir); - } - if (make_dir_err != OK) { - memdelete(dest_dir_access); - return make_dir_err; +String EditorExportPlatformIOS::_get_additional_plist_content() { + Vector<Ref<EditorExportPlugin> > export_plugins = EditorExport::get_singleton()->get_export_plugins(); + String result; + for (int i = 0; i < export_plugins.size(); ++i) { + result += export_plugins[i]->get_ios_plist_content(); } + return result; +} - Error copy_err = dest_dir_access->copy(p_path, dest_dir + rel_path); - memdelete(dest_dir_access); +String EditorExportPlatformIOS::_get_linker_flags() { + Vector<Ref<EditorExportPlugin> > export_plugins = EditorExport::get_singleton()->get_export_plugins(); + String result; + for (int i = 0; i < export_plugins.size(); ++i) { + String flags = export_plugins[i]->get_ios_linker_flags(); + if (flags.length() == 0) continue; + if (result.length() > 0) { + result += ' '; + } + result += flags; + } + // the flags will be enclosed in quotes, so need to escape them + return result.replace("\"", "\\\""); +} - return copy_err; +String EditorExportPlatformIOS::_get_cpp_code() { + Vector<Ref<EditorExportPlugin> > export_plugins = EditorExport::get_singleton()->get_export_plugins(); + String result; + for (int i = 0; i < export_plugins.size(); ++i) { + result += export_plugins[i]->get_ios_cpp_code(); + } + return result; } struct IconInfo { @@ -336,7 +407,7 @@ Error EditorExportPlatformIOS::_export_loading_screens(const Ref<EditorExportPre Error err = da->copy(loading_screen_file, p_dest_dir + info.export_name); if (err) { memdelete(da); - String err_str = String("Failed to export loading screen: ") + loading_screen_file; + String err_str = String("Failed to export loading screen (") + info.preset_key + ") from path: " + loading_screen_file; ERR_PRINT(err_str.utf8().get_data()); return err; } @@ -383,8 +454,9 @@ struct CodesignData { const Ref<EditorExportPreset> &preset; bool debug; - CodesignData(const Ref<EditorExportPreset> &p_preset, bool p_debug) - : preset(p_preset), debug(p_debug) { + CodesignData(const Ref<EditorExportPreset> &p_preset, bool p_debug) : + preset(p_preset), + debug(p_debug) { } }; @@ -402,7 +474,207 @@ Error EditorExportPlatformIOS::_codesign(String p_file, void *p_userdata) { return OK; } +struct PbxId { +private: + static char _hex_char(uint8_t four_bits) { + if (four_bits < 10) { + return ('0' + four_bits); + } + return 'A' + (four_bits - 10); + } + + static String _hex_pad(uint32_t num) { + Vector<char> ret; + ret.resize(sizeof(num) * 2); + for (int i = 0; i < sizeof(num) * 2; ++i) { + uint8_t four_bits = (num >> (sizeof(num) * 8 - (i + 1) * 4)) & 0xF; + ret[i] = _hex_char(four_bits); + } + return String::utf8(ret.ptr(), ret.size()); + } + +public: + uint32_t high_bits; + uint32_t mid_bits; + uint32_t low_bits; + + String str() const { + return _hex_pad(high_bits) + _hex_pad(mid_bits) + _hex_pad(low_bits); + } + + PbxId &operator++() { + low_bits++; + if (!low_bits) { + mid_bits++; + if (!mid_bits) { + high_bits++; + } + } + + return *this; + } +}; + +struct ExportLibsData { + Vector<String> lib_paths; + String dest_dir; +}; + +void EditorExportPlatformIOS::_add_assets_to_project(Vector<uint8_t> &p_project_data, const Vector<IOSExportAsset> &p_additional_assets) { + Vector<Ref<EditorExportPlugin> > export_plugins = EditorExport::get_singleton()->get_export_plugins(); + Vector<String> frameworks; + for (int i = 0; i < export_plugins.size(); ++i) { + Vector<String> plugin_frameworks = export_plugins[i]->get_ios_frameworks(); + for (int j = 0; j < plugin_frameworks.size(); ++j) { + frameworks.push_back(plugin_frameworks[j]); + } + } + + // that is just a random number, we just need Godot IDs not to clash with + // existing IDs in the project. + PbxId current_id = { 0x58938401, 0, 0 }; + String pbx_files; + String pbx_frameworks_build; + String pbx_frameworks_refs; + String pbx_resources_build; + String pbx_resources_refs; + + const String file_info_format = String("$build_id = {isa = PBXBuildFile; fileRef = $ref_id; };\n") + + "$ref_id = {isa = PBXFileReference; lastKnownFileType = $file_type; name = $name; path = \"$file_path\"; sourceTree = \"<group>\"; };\n"; + for (int i = 0; i < p_additional_assets.size(); ++i) { + String build_id = (++current_id).str(); + String ref_id = (++current_id).str(); + const IOSExportAsset &asset = p_additional_assets[i]; + + String type; + if (asset.exported_path.ends_with(".framework")) { + type = "wrapper.framework"; + } else if (asset.exported_path.ends_with(".dylib")) { + type = "compiled.mach-o.dylib"; + } else if (asset.exported_path.ends_with(".a")) { + type = "archive.ar"; + } else { + type = "file"; + } + + String &pbx_build = asset.is_framework ? pbx_frameworks_build : pbx_resources_build; + String &pbx_refs = asset.is_framework ? pbx_frameworks_refs : pbx_resources_refs; + + if (pbx_build.length() > 0) { + pbx_build += ",\n"; + pbx_refs += ",\n"; + } + pbx_build += build_id; + pbx_refs += ref_id; + + Dictionary format_dict; + format_dict["build_id"] = build_id; + format_dict["ref_id"] = ref_id; + format_dict["name"] = asset.exported_path.get_file(); + format_dict["file_path"] = asset.exported_path; + format_dict["file_type"] = type; + pbx_files += file_info_format.format(format_dict, "$_"); + } + + String str = String::utf8((const char *)p_project_data.ptr(), p_project_data.size()); + str = str.replace("$additional_pbx_files", pbx_files); + str = str.replace("$additional_pbx_frameworks_build", pbx_frameworks_build); + str = str.replace("$additional_pbx_frameworks_refs", pbx_frameworks_refs); + str = str.replace("$additional_pbx_resources_build", pbx_resources_build); + str = str.replace("$additional_pbx_resources_refs", pbx_resources_refs); + + CharString cs = str.utf8(); + p_project_data.resize(cs.size() - 1); + for (int i = 0; i < cs.size() - 1; i++) { + p_project_data[i] = cs[i]; + } +} + +Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir, const Vector<String> &p_assets, bool p_is_framework, Vector<IOSExportAsset> &r_exported_assets) { + DirAccess *filesystem_da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + ERR_FAIL_COND_V(!filesystem_da, ERR_CANT_CREATE); + for (int f_idx = 0; f_idx < p_assets.size(); ++f_idx) { + String asset = p_assets[f_idx]; + if (!asset.begins_with("res://")) { + // either SDK-builtin or already a part of the export template + IOSExportAsset exported_asset = { asset, p_is_framework }; + r_exported_assets.push_back(exported_asset); + } else { + DirAccess *da = DirAccess::create_for_path(asset); + if (!da) { + memdelete(filesystem_da); + ERR_FAIL_COND_V(!da, ERR_CANT_CREATE); + } + bool file_exists = da->file_exists(asset); + bool dir_exists = da->dir_exists(asset); + if (!file_exists && !dir_exists) { + memdelete(da); + memdelete(filesystem_da); + return ERR_FILE_NOT_FOUND; + } + String additional_dir = p_is_framework && asset.ends_with(".dylib") ? "/dylibs/" : "/"; + String destination_dir = p_out_dir + additional_dir + asset.get_base_dir().replace("res://", ""); + if (!filesystem_da->dir_exists(destination_dir)) { + Error make_dir_err = filesystem_da->make_dir_recursive(destination_dir); + if (make_dir_err) { + memdelete(da); + memdelete(filesystem_da); + return make_dir_err; + } + } + + String destination = destination_dir + "/" + asset.get_file(); + Error err = dir_exists ? da->copy_dir(asset, destination) : da->copy(asset, destination); + memdelete(da); + if (err) { + memdelete(filesystem_da); + return err; + } + IOSExportAsset exported_asset = { destination, p_is_framework }; + r_exported_assets.push_back(exported_asset); + } + } + memdelete(filesystem_da); + + return OK; +} + +Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir, const Vector<SharedObject> &p_libraries, Vector<IOSExportAsset> &r_exported_assets) { + Vector<Ref<EditorExportPlugin> > export_plugins = EditorExport::get_singleton()->get_export_plugins(); + for (int i = 0; i < export_plugins.size(); i++) { + Vector<String> frameworks = export_plugins[i]->get_ios_frameworks(); + Error err = _export_additional_assets(p_out_dir, frameworks, true, r_exported_assets); + ERR_FAIL_COND_V(err, err); + Vector<String> ios_bundle_files = export_plugins[i]->get_ios_bundle_files(); + err = _export_additional_assets(p_out_dir, ios_bundle_files, false, r_exported_assets); + ERR_FAIL_COND_V(err, err); + } + + Vector<String> library_paths; + for (int i = 0; i < p_libraries.size(); ++i) { + library_paths.push_back(p_libraries[i].path); + } + Error err = _export_additional_assets(p_out_dir, library_paths, true, r_exported_assets); + ERR_FAIL_COND_V(err, err); + + return OK; +} + +Vector<String> EditorExportPlatformIOS::_get_preset_architectures(const Ref<EditorExportPreset> &p_preset) { + Vector<ExportArchitecture> all_archs = _get_supported_architectures(); + Vector<String> enabled_archs; + for (int i = 0; i < all_archs.size(); ++i) { + bool is_enabled = p_preset->get("architectures/" + all_archs[i].name); + if (is_enabled) { + enabled_archs.push_back(all_archs[i].name); + } + } + return enabled_archs; +} + Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { + ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); + String src_pkg_name; String dest_dir = p_path.get_base_dir() + "/"; String binary_name = p_path.get_file().get_basename(); @@ -427,26 +699,43 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p } } - FileAccess *src_f = NULL; - zlib_filefunc_def io = zipio_create_io_from_file(&src_f); + DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + if (da) { + String current_dir = da->get_current_dir(); - ep.step("Creating app", 0); + // remove leftovers from last export so they don't interfere + // in case some files are no longer needed + if (da->change_dir(dest_dir + binary_name + ".xcodeproj") == OK) { + da->erase_contents_recursive(); + } + if (da->change_dir(dest_dir + binary_name) == OK) { + da->erase_contents_recursive(); + } - unzFile src_pkg_zip = unzOpen2(src_pkg_name.utf8().get_data(), &io); - if (!src_pkg_zip) { + da->change_dir(current_dir); - EditorNode::add_io_error("Could not find template app to export:\n" + src_pkg_name); - return ERR_FILE_NOT_FOUND; + if (!da->dir_exists(dest_dir + binary_name)) { + Error err = da->make_dir(dest_dir + binary_name); + if (err) { + memdelete(da); + return err; + } + } + memdelete(da); } - ERR_FAIL_COND_V(!src_pkg_zip, ERR_CANT_OPEN); - int ret = unzGoToFirstFile(src_pkg_zip); + ep.step("Making .pck", 0); + String pack_path = dest_dir + binary_name + ".pck"; + Vector<SharedObject> libraries; + Error err = save_pack(p_preset, pack_path, &libraries); + if (err) + return err; + + ep.step("Extracting and configuring Xcode project", 1); - 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"); + String library_to_use = "libgodot.iphone." + String(p_debug ? "debug" : "release") + ".fat.a"; - print_line("binary: " + binary_to_use); + print_line("static library: " + library_to_use); String pkg_name; if (p_preset->get("application/name") != "") pkg_name = p_preset->get("application/name"); // app_name @@ -455,22 +744,41 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p 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; + bool found_library = false; int total_size = 0; + const String project_file = "godot_ios.xcodeproj/project.pbxproj"; Set<String> files_to_parse; files_to_parse.insert("godot_ios/godot_ios-Info.plist"); - files_to_parse.insert("godot_ios.xcodeproj/project.pbxproj"); - files_to_parse.insert("export_options.plist"); + files_to_parse.insert(project_file); + files_to_parse.insert("godot_ios/export_options.plist"); + files_to_parse.insert("godot_ios/dummy.cpp"); files_to_parse.insert("godot_ios.xcodeproj/project.xcworkspace/contents.xcworkspacedata"); files_to_parse.insert("godot_ios.xcodeproj/xcshareddata/xcschemes/godot_ios.xcscheme"); - print_line("Unzipping..."); + IOSConfigData config_data = { + pkg_name, + binary_name, + _get_additional_plist_content(), + String(" ").join(_get_preset_architectures(p_preset)), + _get_linker_flags(), + _get_cpp_code() + }; + + DirAccess *tmp_app_path = DirAccess::create_for_path(dest_dir); + ERR_FAIL_COND_V(!tmp_app_path, ERR_CANT_CREATE) + print_line("Unzipping..."); + FileAccess *src_f = NULL; + zlib_filefunc_def io = zipio_create_io_from_file(&src_f); + unzFile src_pkg_zip = unzOpen2(src_pkg_name.utf8().get_data(), &io); + if (!src_pkg_zip) { + EditorNode::add_io_error("Could not open export template (not a zip file?):\n" + src_pkg_name); + return ERR_CANT_OPEN; + } + ERR_FAIL_COND_V(!src_pkg_zip, ERR_CANT_OPEN); + int ret = unzGoToFirstFile(src_pkg_zip); + Vector<uint8_t> project_file_data; while (ret == UNZ_OK) { bool is_execute = false; @@ -487,7 +795,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p //read unzOpenCurrentFile(src_pkg_zip); - unzReadCurrentFile(src_pkg_zip, data.ptr(), data.size()); + unzReadCurrentFile(src_pkg_zip, data.ptrw(), data.size()); unzCloseCurrentFile(src_pkg_zip); //write @@ -496,15 +804,18 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p if (files_to_parse.has(file)) { print_line(String("parse ") + file); - _fix_config_file(p_preset, data, pkg_name, binary_name, p_debug); - } else if (file.begins_with("godot.iphone")) { - if (file != binary_to_use) { + _fix_config_file(p_preset, data, config_data, p_debug); + } else if (file.begins_with("libgodot.iphone")) { + if (file != library_to_use) { ret = unzGoToNextFile(src_pkg_zip); continue; //ignore! } - found_binary = true; + found_library = true; is_execute = true; - file = "godot_ios.iphone"; + file = "godot_ios.a"; + } + if (file == project_file) { + project_file_data = data; } ///@TODO need to parse logo files @@ -557,16 +868,16 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p /* 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."); + if (!found_library) { + ERR_PRINTS("Requested template library '" + library_to_use + "' not found. It might be missing from your template archive."); memdelete(tmp_app_path); return ERR_FILE_NOT_FOUND; } String iconset_dir = dest_dir + binary_name + "/Images.xcassets/AppIcon.appiconset/"; - Error err = OK; + err = OK; if (!tmp_app_path->dir_exists(iconset_dir)) { - Error err = tmp_app_path->make_dir_recursive(iconset_dir); + err = tmp_app_path->make_dir_recursive(iconset_dir); } memdelete(tmp_app_path); if (err) @@ -580,20 +891,23 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p if (err) return err; - ep.step("Making .pck", 1); - - String pack_path = dest_dir + binary_name + ".pck"; - err = save_pack(p_preset, pack_path); - if (err) - return err; - - err = export_project_files(p_preset, _export_dylibs, &dest_dir); - if (err) - return err; + print_line("Exporting additional assets"); + Vector<IOSExportAsset> assets; + _export_additional_assets(dest_dir + binary_name, libraries, assets); + _add_assets_to_project(project_file_data, assets); + String project_file_name = dest_dir + binary_name + ".xcodeproj/project.pbxproj"; + FileAccess *f = FileAccess::open(project_file_name, FileAccess::WRITE); + if (!f) { + ERR_PRINTS("Can't write '" + project_file_name + "'."); + return ERR_CANT_CREATE; + }; + f->store_buffer(project_file_data.ptr(), project_file_data.size()); + f->close(); + memdelete(f); #ifdef OSX_ENABLED ep.step("Code-signing dylibs", 2); - DirAccess *dylibs_dir = DirAccess::open(dest_dir + "dylibs"); + DirAccess *dylibs_dir = DirAccess::open(dest_dir + binary_name + "/dylibs"); ERR_FAIL_COND_V(!dylibs_dir, ERR_CANT_OPEN); CodesignData codesign_data(p_preset, p_debug); err = _walk_dir_recursive(dylibs_dir, _codesign, &codesign_data); @@ -625,13 +939,14 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p export_args.push_back("-archivePath"); export_args.push_back(archive_path); export_args.push_back("-exportOptionsPlist"); - export_args.push_back(dest_dir + "export_options.plist"); + export_args.push_back(dest_dir + binary_name + "/export_options.plist"); + export_args.push_back("-allowProvisioningUpdates"); export_args.push_back("-exportPath"); export_args.push_back(dest_dir); err = OS::get_singleton()->execute("xcodebuild", export_args, true); ERR_FAIL_COND_V(err, err); #else - print_line(".ipa can only be built on macOS. Leaving XCode project without building the package."); + print_line(".ipa can only be built on macOS. Leaving Xcode project without building the package."); #endif return OK; @@ -659,14 +974,13 @@ bool EditorExportPlatformIOS::can_export(const Ref<EditorExportPreset> &p_preset if (!err.empty()) r_error = err; + r_missing_templates = !valid; 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)); + Ref<Image> img = memnew(Image(_iphone_logo)); logo.instance(); logo->create_from_image(img); } diff --git a/platform/iphone/export/export.h b/platform/iphone/export/export.h index 1b96b8338a..ea79973290 100644 --- a/platform/iphone/export/export.h +++ b/platform/iphone/export/export.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,4 +27,5 @@ /* 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/game_center.h b/platform/iphone/game_center.h index 21f40fa362..9a62cccb1a 100644 --- a/platform/iphone/game_center.h +++ b/platform/iphone/game_center.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifdef GAME_CENTER_ENABLED #ifndef GAME_CENTER_H diff --git a/platform/iphone/game_center.mm b/platform/iphone/game_center.mm index 531b80eee3..57ff79f7bc 100644 --- a/platform/iphone/game_center.mm +++ b/platform/iphone/game_center.mm @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifdef GAME_CENTER_ENABLED #include "game_center.h" @@ -109,7 +110,7 @@ void GameCenter::connect() { GameCenter::get_singleton()->authenticated = true; } else { ret["result"] = "error"; - ret["error_code"] = error.code; + ret["error_code"] = (int64_t)error.code; ret["error_description"] = [error.localizedDescription UTF8String]; GameCenter::get_singleton()->authenticated = false; }; @@ -145,7 +146,7 @@ Error GameCenter::post_score(Variant p_score) { ret["result"] = "ok"; } else { ret["result"] = "error"; - ret["error_code"] = error.code; + ret["error_code"] = (int64_t)error.code; ret["error_description"] = [error.localizedDescription UTF8String]; }; @@ -183,7 +184,7 @@ Error GameCenter::award_achievement(Variant p_params) { ret["result"] = "ok"; } else { ret["result"] = "error"; - ret["error_code"] = error.code; + ret["error_code"] = (int64_t)error.code; }; pending_events.push_back(ret); @@ -241,7 +242,7 @@ void GameCenter::request_achievement_descriptions() { } else { ret["result"] = "error"; - ret["error_code"] = error.code; + ret["error_code"] = (int64_t)error.code; }; pending_events.push_back(ret); @@ -273,7 +274,7 @@ void GameCenter::request_achievements() { } else { ret["result"] = "error"; - ret["error_code"] = error.code; + ret["error_code"] = (int64_t)error.code; }; pending_events.push_back(ret); @@ -289,7 +290,7 @@ void GameCenter::reset_achievements() { ret["result"] = "ok"; } else { ret["result"] = "error"; - ret["error_code"] = error.code; + ret["error_code"] = (int64_t)error.code; }; pending_events.push_back(ret); @@ -358,7 +359,7 @@ Error GameCenter::request_identity_verification_signature() { ret["player_id"] = [player.playerID UTF8String]; } else { ret["result"] = "error"; - ret["error_code"] = error.code; + ret["error_code"] = (int64_t)error.code; ret["error_description"] = [error.localizedDescription UTF8String]; }; diff --git a/platform/iphone/gl_view.h b/platform/iphone/gl_view.h index f7309396c6..85376ebc08 100644 --- a/platform/iphone/gl_view.h +++ b/platform/iphone/gl_view.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ diff --git a/platform/iphone/gl_view.mm b/platform/iphone/gl_view.mm index 02da706cc5..69116c64c6 100644 --- a/platform/iphone/gl_view.mm +++ b/platform/iphone/gl_view.mm @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #import "gl_view.h" #include "core/os/keyboard.h" diff --git a/platform/iphone/globals/global_defaults.cpp b/platform/iphone/globals/global_defaults.cpp index 4bdc716d6e..ccc90665c5 100644 --- a/platform/iphone/globals/global_defaults.cpp +++ b/platform/iphone/globals/global_defaults.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,15 +27,9 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "global_defaults.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"); - 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/globals/global_defaults.h b/platform/iphone/globals/global_defaults.h index ee8ae833b9..3e3c220f4a 100644 --- a/platform/iphone/globals/global_defaults.h +++ b/platform/iphone/globals/global_defaults.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,4 +27,5 @@ /* 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_global_defaults(); diff --git a/platform/iphone/godot_iphone.cpp b/platform/iphone/godot_iphone.cpp index 7d21d35e18..dacbf42087 100644 --- a/platform/iphone/godot_iphone.cpp +++ b/platform/iphone/godot_iphone.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "main/main.h" #include "os_iphone.h" #include "ustring.h" diff --git a/platform/iphone/icloud.h b/platform/iphone/icloud.h index 6944ee6749..52bb1131a0 100644 --- a/platform/iphone/icloud.h +++ b/platform/iphone/icloud.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifdef ICLOUD_ENABLED #ifndef ICLOUD_H diff --git a/platform/iphone/icloud.mm b/platform/iphone/icloud.mm index 097018f296..a9748bf562 100644 --- a/platform/iphone/icloud.mm +++ b/platform/iphone/icloud.mm @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifdef ICLOUD_ENABLED #include "icloud.h" @@ -130,6 +131,8 @@ Variant nsobject_to_variant(NSObject *object) { return Variant([num floatValue]); } else if (strcmp([num objCType], @encode(double)) == 0) { return Variant((float)[num doubleValue]); + } else { + return Variant(); } } else if ([object isKindOfClass:[NSDate class]]) { //this is a type that icloud supports...but how did you submit it in the first place? diff --git a/platform/iphone/in_app_store.h b/platform/iphone/in_app_store.h index 4c12f825c1..353438676d 100644 --- a/platform/iphone/in_app_store.h +++ b/platform/iphone/in_app_store.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifdef STOREKIT_ENABLED #ifndef IN_APP_STORE_H @@ -45,6 +46,7 @@ class InAppStore : public Object { public: Error request_product_info(Variant p_params); + Error restore_purchases(); Error purchase(Variant p_params); int get_pending_event_count(); diff --git a/platform/iphone/in_app_store.mm b/platform/iphone/in_app_store.mm index 9efd4b9891..6fa189e917 100644 --- a/platform/iphone/in_app_store.mm +++ b/platform/iphone/in_app_store.mm @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,14 +27,11 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifdef STOREKIT_ENABLED #include "in_app_store.h" -#ifdef MODULE_FUSEBOXX_ENABLED -#import "modules/fuseboxx/ios/FuseSDK.h" -#endif - extern "C" { #import <Foundation/Foundation.h> #import <StoreKit/StoreKit.h> @@ -66,6 +63,7 @@ InAppStore *InAppStore::instance = NULL; void InAppStore::_bind_methods() { ClassDB::bind_method(D_METHOD("request_product_info"), &InAppStore::request_product_info); + ClassDB::bind_method(D_METHOD("restore_purchases"), &InAppStore::restore_purchases); ClassDB::bind_method(D_METHOD("purchase"), &InAppStore::purchase); ClassDB::bind_method(D_METHOD("get_pending_event_count"), &InAppStore::get_pending_event_count); @@ -92,6 +90,7 @@ void InAppStore::_bind_methods() { PoolRealArray prices; PoolStringArray ids; PoolStringArray localized_prices; + PoolStringArray currency_codes; for (int i = 0; i < [products count]; i++) { @@ -105,12 +104,14 @@ void InAppStore::_bind_methods() { prices.push_back([product.price doubleValue]); ids.push_back(String::utf8([product.productIdentifier UTF8String])); localized_prices.push_back(String::utf8([product.localizedPrice UTF8String])); + currency_codes.push_back(String::utf8([[[product priceLocale] objectForKey:NSLocaleCurrencyCode] UTF8String])); }; ret["titles"] = titles; ret["descriptions"] = descriptions; ret["prices"] = prices; ret["ids"] = ids; ret["localized_prices"] = localized_prices; + ret["currency_codes"] = currency_codes; PoolStringArray invalid_ids; @@ -153,6 +154,14 @@ Error InAppStore::request_product_info(Variant p_params) { return OK; }; +Error InAppStore::restore_purchases() { + + printf("restoring purchases!\n"); + [[SKPaymentQueue defaultQueue] restoreCompletedTransactions]; + + return OK; +}; + @interface TransObserver : NSObject <SKPaymentTransactionObserver> { }; @end @@ -221,10 +230,6 @@ Error InAppStore::request_product_info(Variant p_params) { [pending_transactions setObject:transaction forKey:transaction.payment.productIdentifier]; } -#ifdef MODULE_FUSEBOXX_ENABLED - printf("Registering transaction on Fuseboxx!\n"); - [FuseSDK registerInAppPurchase:transaction]; -#endif }; break; case SKPaymentTransactionStateFailed: { printf("status transaction failed!\n"); @@ -240,6 +245,11 @@ Error InAppStore::request_product_info(Variant p_params) { printf("status transaction restored!\n"); String pid = String::utf8([transaction.originalTransaction.payment.productIdentifier UTF8String]); InAppStore::get_singleton()->_record_purchase(pid); + Dictionary ret; + ret["type"] = "restore"; + ret["result"] = "ok"; + ret["product_id"] = pid; + InAppStore::get_singleton()->_post_event(ret); [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; } break; default: { diff --git a/platform/iphone/ios.h b/platform/iphone/ios.h index 0c2e6b4aa6..5e77683949 100644 --- a/platform/iphone/ios.h +++ b/platform/iphone/ios.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef IOS_H #define IOS_H diff --git a/platform/iphone/ios.mm b/platform/iphone/ios.mm index add0949f94..7eb4f495f7 100644 --- a/platform/iphone/ios.mm +++ b/platform/iphone/ios.mm @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "ios.h" #import <UIKit/UIKit.h> diff --git a/platform/iphone/main.m b/platform/iphone/main.m index 0de5c3b41b..0f0810f28f 100644 --- a/platform/iphone/main.m +++ b/platform/iphone/main.m @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #import "app_delegate.h" #import <UIKit/UIKit.h> diff --git a/platform/iphone/os_iphone.cpp b/platform/iphone/os_iphone.cpp index 0efe22c1af..c4c59431f2 100644 --- a/platform/iphone/os_iphone.cpp +++ b/platform/iphone/os_iphone.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifdef IPHONE_ENABLED #include "os_iphone.h" @@ -46,6 +47,7 @@ #include "sem_iphone.h" #include "ios.h" +#include <dlfcn.h> int OSIPhone::get_video_driver_count() const { @@ -54,7 +56,7 @@ int OSIPhone::get_video_driver_count() const { const char *OSIPhone::get_video_driver_name(int p_driver) const { - return "GLES2"; + return "GLES3"; }; OSIPhone *OSIPhone::get_singleton() { @@ -62,11 +64,6 @@ OSIPhone *OSIPhone::get_singleton() { return (OSIPhone *)OS::get_singleton(); }; -OS::VideoMode OSIPhone::get_default_video_mode() const { - - return video_mode; -}; - uint8_t OSIPhone::get_orientations() const { return supported_orientations; @@ -101,16 +98,7 @@ void OSIPhone::initialize_core() { set_data_dir(data_dir); }; -void OSIPhone::initialize_logger() { - Vector<Logger *> loggers; - loggers.push_back(memnew(SyslogLogger)); - // FIXME: Reenable once we figure out how to get this properly in user:// - // instead of littering the user's working dirs (res:// + pwd) with log files (GH-12277) - //loggers.push_back(memnew(RotatedFileLogger("user://logs/log.txt"))); - _set_logger(memnew(CompositeLogger(loggers))); -} - -void OSIPhone::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { +Error OSIPhone::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { supported_orientations = 0; supported_orientations |= ((GLOBAL_DEF("video_mode/allow_horizontal", true) ? 1 : 0) << LandscapeLeft); @@ -130,48 +118,34 @@ void OSIPhone::initialize(const VideoMode &p_desired, int p_video_driver, int p_ */ visual_server->init(); - // visual_server->cursor_set_visible(false, 0); + //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; - AudioDriverManager::add_driver(&audio_driver); AudioDriverManager::initialize(p_audio_driver); - // init physics servers - physics_server = memnew(PhysicsServerSW); - physics_server->init(); - //physics_2d_server = memnew( Physics2DServerSW ); - physics_2d_server = Physics2DServerWrapMT::init_server<Physics2DServerSW>(); - physics_2d_server->init(); - input = memnew(InputDefault); -/* -#ifdef IOS_SCORELOOP_ENABLED - scoreloop = memnew(ScoreloopIOS); - ProjectSettings::get_singleton()->add_singleton(ProjectSettings::Singleton("Scoreloop", scoreloop)); - scoreloop->connect(); -#endif - */ - #ifdef GAME_CENTER_ENABLED game_center = memnew(GameCenter); - ProjectSettings::get_singleton()->add_singleton(ProjectSettings::Singleton("GameCenter", game_center)); + Engine::get_singleton()->add_singleton(Engine::Singleton("GameCenter", game_center)); game_center->connect(); #endif #ifdef STOREKIT_ENABLED store_kit = memnew(InAppStore); - ProjectSettings::get_singleton()->add_singleton(ProjectSettings::Singleton("InAppStore", store_kit)); + Engine::get_singleton()->add_singleton(Engine::Singleton("InAppStore", store_kit)); #endif #ifdef ICLOUD_ENABLED icloud = memnew(ICloud); - ProjectSettings::get_singleton()->add_singleton(ProjectSettings::Singleton("ICloud", icloud)); -//icloud->connect(); + Engine::get_singleton()->add_singleton(Engine::Singleton("ICloud", icloud)); + //icloud->connect(); #endif - ProjectSettings::get_singleton()->add_singleton(ProjectSettings::Singleton("iOS", memnew(iOS))); + Engine::get_singleton()->add_singleton(Engine::Singleton("iOS", memnew(iOS))); + + return OK; }; MainLoop *OSIPhone::get_main_loop() const { @@ -384,12 +358,6 @@ void OSIPhone::finalize() { memdelete(visual_server); // memdelete(rasterizer); - physics_server->finish(); - memdelete(physics_server); - - physics_2d_server->finish(); - memdelete(physics_2d_server); - memdelete(input); }; @@ -420,6 +388,37 @@ void OSIPhone::alert(const String &p_alert, const String &p_title) { iOS::alert(utf8_alert.get_data(), utf8_title.get_data()); } +Error OSIPhone::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) { + if (p_path.length() == 0) { + p_library_handle = RTLD_SELF; + return OK; + } + return OS_Unix::open_dynamic_library(p_path, p_library_handle, p_also_set_library_path); +} + +Error OSIPhone::close_dynamic_library(void *p_library_handle) { + if (p_library_handle == RTLD_SELF) { + return OK; + } + return OS_Unix::close_dynamic_library(p_library_handle); +} + +HashMap<String, void *> OSIPhone::dynamic_symbol_lookup_table; +void register_dynamic_symbol(char *name, void *address) { + OSIPhone::dynamic_symbol_lookup_table[String(name)] = address; +} + +Error OSIPhone::get_dynamic_library_symbol_handle(void *p_library_handle, const String p_name, void *&p_symbol_handle, bool p_optional) { + if (p_library_handle == RTLD_SELF) { + void **ptr = OSIPhone::dynamic_symbol_lookup_table.getptr(p_name); + if (ptr) { + p_symbol_handle = *ptr; + return OK; + } + } + return OS_Unix::get_dynamic_library_symbol_handle(p_library_handle, p_name, p_symbol_handle, p_optional); +} + void OSIPhone::set_video_mode(const VideoMode &p_video_mode, int p_screen) { video_mode = p_video_mode; @@ -488,11 +487,13 @@ void OSIPhone::set_cursor_shape(CursorShape p_shape){ }; -String OSIPhone::get_data_dir() const { +String OSIPhone::get_user_data_dir() const { return data_dir; }; +void OSIPhone::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot){}; + String OSIPhone::get_name() { return "iOS"; @@ -527,7 +528,7 @@ Error OSIPhone::native_video_play(String p_path, float p_volume, String p_audio_ FileAccess *f = FileAccess::open(p_path, FileAccess::READ); bool exists = f && f->is_open(); - String tempFile = get_data_dir(); + String tempFile = get_user_data_dir(); if (!exists) return FAILED; @@ -539,7 +540,7 @@ Error OSIPhone::native_video_play(String p_path, float p_volume, String p_audio_ 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()); + p_path = p_path.replace("user:/", get_user_data_dir()); memdelete(f); @@ -576,7 +577,36 @@ bool OSIPhone::_check_internal_feature_support(const String &p_feature) { return p_feature == "mobile" || p_feature == "etc" || p_feature == "pvrtc" || p_feature == "etc2"; } +// Initialization order between compilation units is not guaranteed, +// so we use this as a hack to ensure certain code is called before +// everything else, but after all units are initialized. +typedef void (*init_callback)(); +static init_callback *ios_init_callbacks = NULL; +static int ios_init_callbacks_count = 0; +static int ios_init_callbacks_capacity = 0; + +void add_ios_init_callback(init_callback cb) { + if (ios_init_callbacks_count == ios_init_callbacks_capacity) { + void *new_ptr = realloc(ios_init_callbacks, sizeof(cb) * 32); + if (new_ptr) { + ios_init_callbacks = (init_callback *)(new_ptr); + ios_init_callbacks_capacity += 32; + } + } + if (ios_init_callbacks_capacity > ios_init_callbacks_count) { + ios_init_callbacks[ios_init_callbacks_count] = cb; + ++ios_init_callbacks_count; + } +} + OSIPhone::OSIPhone(int width, int height, String p_data_dir) { + for (int i = 0; i < ios_init_callbacks_count; ++i) { + ios_init_callbacks[i](); + } + free(ios_init_callbacks); + ios_init_callbacks = NULL; + ios_init_callbacks_count = 0; + ios_init_callbacks_capacity = 0; main_loop = NULL; visual_server = NULL; @@ -594,7 +624,15 @@ OSIPhone::OSIPhone(int width, int height, String p_data_dir) { // which is initialized in initialize_core data_dir = p_data_dir; - _set_logger(memnew(SyslogLogger)); + Vector<Logger *> loggers; + loggers.push_back(memnew(SyslogLogger)); +#ifdef DEBUG_ENABLED + // it seems iOS app's stdout/stderr is only obtainable if you launch it from Xcode + loggers.push_back(memnew(StdLogger)); +#endif + _set_logger(memnew(CompositeLogger(loggers))); + + AudioDriverManager::add_driver(&audio_driver); }; OSIPhone::~OSIPhone() { diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h index e70ac9ba98..2e4458aeed 100644 --- a/platform/iphone/os_iphone.h +++ b/platform/iphone/os_iphone.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifdef IPHONE_ENABLED #ifndef OS_IPHONE_H @@ -41,9 +42,6 @@ #include "in_app_store.h" #include "main/input_default.h" #include "servers/audio_server.h" -#include "servers/physics/physics_server_sw.h" -#include "servers/physics_2d/physics_2d_server_sw.h" -#include "servers/physics_2d/physics_2d_server_wrap_mt.h" #include "servers/visual/rasterizer.h" #include "servers/visual_server.h" @@ -63,11 +61,12 @@ private: MAX_EVENTS = 64, }; + static HashMap<String, void *> dynamic_symbol_lookup_table; + friend void register_dynamic_symbol(char *name, void *address); + uint8_t supported_orientations; VisualServer *visual_server; - PhysicsServer *physics_server; - Physics2DServer *physics_2d_server; AudioDriverCoreAudio audio_driver; @@ -88,11 +87,8 @@ private: virtual int get_video_driver_count() const; virtual const char *get_video_driver_name(int p_driver) const; - virtual VideoMode get_default_video_mode() const; - - virtual void initialize_logger(); virtual void initialize_core(); - virtual void initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver); + virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver); virtual void set_main_loop(MainLoop *p_main_loop); virtual MainLoop *get_main_loop() const; @@ -160,6 +156,10 @@ public: virtual void alert(const String &p_alert, const String &p_title = "ALERT!"); + virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false); + 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, bool p_optional = false); + virtual void set_video_mode(const VideoMode &p_video_mode, int p_screen = 0); virtual VideoMode get_video_mode(int p_screen = 0) const; virtual void get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen = 0) const; @@ -174,6 +174,7 @@ public: virtual int get_virtual_keyboard_height() const; virtual void set_cursor_shape(CursorShape p_shape); + virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); virtual Size2 get_window_size() const; @@ -185,7 +186,7 @@ public: Error shell_open(String p_uri); - String get_data_dir() const; + String get_user_data_dir() const; void set_locale(String p_locale); String get_locale() const; diff --git a/platform/iphone/platform_config.h b/platform/iphone/platform_config.h index 54de66082e..d9fd61fb6e 100644 --- a/platform/iphone/platform_config.h +++ b/platform/iphone/platform_config.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,8 +27,10 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include <alloca.h> -// #define GLES2_INCLUDE_H <ES2/gl.h> + +#define GLES2_INCLUDE_H <ES2/gl.h> #define GLES3_INCLUDE_H <ES3/gl.h> #define PLATFORM_REFCOUNT diff --git a/platform/iphone/platform_refcount.h b/platform/iphone/platform_refcount.h index 8d04340793..94e4e5fa3b 100644 --- a/platform/iphone/platform_refcount.h +++ b/platform/iphone/platform_refcount.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "safe_refcount.h" #ifdef IPHONE_ENABLED diff --git a/platform/iphone/power_iphone.cpp b/platform/iphone/power_iphone.cpp index 055d31ef0a..95a9aa9705 100644 --- a/platform/iphone/power_iphone.cpp +++ b/platform/iphone/power_iphone.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -58,8 +58,10 @@ int PowerIphone::get_power_percent_left() { } } -PowerIphone::PowerIphone() - : nsecs_left(-1), percent_left(-1), power_state(OS::POWERSTATE_UNKNOWN) { +PowerIphone::PowerIphone() : + nsecs_left(-1), + percent_left(-1), + power_state(OS::POWERSTATE_UNKNOWN) { // TODO Auto-generated constructor stub } diff --git a/platform/iphone/power_iphone.h b/platform/iphone/power_iphone.h index 6270a8069c..9619064915 100644 --- a/platform/iphone/power_iphone.h +++ b/platform/iphone/power_iphone.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ diff --git a/platform/iphone/sem_iphone.cpp b/platform/iphone/sem_iphone.cpp index 623749f891..ec1337d63f 100644 --- a/platform/iphone/sem_iphone.cpp +++ b/platform/iphone/sem_iphone.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "sem_iphone.h" #include <fcntl.h> diff --git a/platform/iphone/sem_iphone.h b/platform/iphone/sem_iphone.h index 9ae201557f..ebd4e4ee43 100644 --- a/platform/iphone/sem_iphone.h +++ b/platform/iphone/sem_iphone.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef SEM_IPHONE_H #define SEM_IPHONE_H diff --git a/platform/iphone/view_controller.h b/platform/iphone/view_controller.h index ed33e5fdf3..31c4f0daf3 100644 --- a/platform/iphone/view_controller.h +++ b/platform/iphone/view_controller.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #import <GameKit/GameKit.h> #import <UIKit/UIKit.h> diff --git a/platform/iphone/view_controller.mm b/platform/iphone/view_controller.mm index 23d60cc5d5..cdaae0cb81 100644 --- a/platform/iphone/view_controller.mm +++ b/platform/iphone/view_controller.mm @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #import "view_controller.h" #include "os_iphone.h" diff --git a/platform/javascript/SCsub b/platform/javascript/SCsub index f01d9367d2..66a8a8d93c 100644 --- a/platform/javascript/SCsub +++ b/platform/javascript/SCsub @@ -6,8 +6,8 @@ javascript_files = [ "os_javascript.cpp", "audio_driver_javascript.cpp", "javascript_main.cpp", - "audio_server_javascript.cpp", "power_javascript.cpp", + "http_client_javascript.cpp", "javascript_eval.cpp", ] @@ -19,33 +19,22 @@ 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','_send_notification']\""]) +env.Append(LINKFLAGS=["-s", "EXPORTED_FUNCTIONS=\"['_main','_main_after_fs_sync','_send_notification']\""]) -# output file name without file extension -basename = "godot" + env["PROGSUFFIX"] target_dir = env.Dir("#bin") +build = env.add_program(['#bin/godot', target_dir.File('godot' + env['PROGSUFFIX'] + '.wasm')], javascript_objects, PROGSUFFIX=env['PROGSUFFIX'] + '.js'); +js, wasm = build -zip_dir = target_dir.Dir('.javascript_zip') -zip_files = env.InstallAs(zip_dir.File('godot.html'), '#misc/dist/html/default.html') - -implicit_targets = [] -if env['wasm']: - wasm = target_dir.File(basename + '.wasm') - implicit_targets.append(wasm) - zip_files.append(InstallAs(zip_dir.File('godot.wasm'), wasm)) - prejs = env.File('pre_wasm.js') -else: - asmjs_files = [target_dir.File(basename + '.asm.js'), target_dir.File(basename + '.js.mem')] - implicit_targets.extend(asmjs_files) - zip_files.append(InstallAs([zip_dir.File('godot.asm.js'), zip_dir.File('godot.mem')], asmjs_files)) - prejs = env.File('pre_asmjs.js') - -js = env.Program(['#bin/godot'] + implicit_targets, javascript_objects, PROGSUFFIX=env['PROGSUFFIX'] + '.js')[0]; -zip_files.append(InstallAs(zip_dir.File('godot.js'), js)) - -postjs = env.File('engine.js') -env.Depends(js, [prejs, postjs]) -env.Append(LINKFLAGS=['--pre-js', prejs.path]) -env.Append(LINKFLAGS=['--post-js', postjs.path]) +js_libraries = [] +js_libraries.append(env.File('http_request.js')) +for lib in js_libraries: + env.Append(LINKFLAGS=['--js-library', lib.path]) +env.Depends(build, js_libraries) + +wrapper_start = env.File('pre.js') +wrapper_end = env.File('engine.js') +js_final = env.Textfile('#bin/godot', [wrapper_start, js, wrapper_end], TEXTFILESUFFIX=env['PROGSUFFIX'] + '.wrapped.js') +zip_dir = target_dir.Dir('.javascript_zip') +zip_files = env.InstallAs([zip_dir.File('godot.js'), zip_dir.File('godot.wasm'), zip_dir.File('godot.html')], [js_final, wasm, '#misc/dist/html/default.html']) Zip('#bin/godot', zip_files, ZIPSUFFIX=env['PROGSUFFIX'] + env['ZIPSUFFIX'], ZIPROOT=zip_dir, ZIPCOMSTR="Archving $SOURCES as $TARGET") diff --git a/platform/windows/packet_peer_udp_winsock.h b/platform/javascript/api/api.cpp index 8a6951fd6e..b377ca4e52 100644 --- a/platform/windows/packet_peer_udp_winsock.h +++ b/platform/javascript/api/api.cpp @@ -1,12 +1,12 @@ /*************************************************************************/ -/* packet_peer_udp_winsock.h */ +/* api.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,58 +27,48 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef PACKET_PEER_UDP_WINSOCK_H -#define PACKET_PEER_UDP_WINSOCK_H -#include "io/packet_peer_udp.h" -#include "ring_buffer.h" +#include "api.h" +#include "engine.h" +#include "javascript_eval.h" -class PacketPeerUDPWinsock : public PacketPeerUDP { +static JavaScript *javascript_eval; - enum { - PACKET_BUFFER_SIZE = 65536 - }; +void register_javascript_api() { - mutable RingBuffer<uint8_t> rb; - uint8_t recv_buffer[PACKET_BUFFER_SIZE]; - mutable uint8_t packet_buffer[PACKET_BUFFER_SIZE]; - mutable IP_Address packet_ip; - mutable int packet_port; - mutable int queue_count; - int sockfd; - bool sock_blocking; - IP::Type sock_type; + ClassDB::register_virtual_class<JavaScript>(); + javascript_eval = memnew(JavaScript); + Engine::get_singleton()->add_singleton(Engine::Singleton("JavaScript", javascript_eval)); +} - IP_Address peer_addr; - int peer_port; +void unregister_javascript_api() { - _FORCE_INLINE_ int _get_socket(); + memdelete(javascript_eval); +} - static PacketPeerUDP *_create(); +JavaScript *JavaScript::singleton = NULL; - void _set_sock_blocking(bool p_blocking); +JavaScript *JavaScript::get_singleton() { - Error _poll(bool p_wait); + return singleton; +} -public: - virtual int get_available_packet_count() const; - virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) const; - virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size); +JavaScript::JavaScript() { - virtual int get_max_packet_size() const; + ERR_FAIL_COND(singleton != NULL); + singleton = this; +} - virtual Error listen(int p_port, const IP_Address &p_bind_address = IP_Address("*"), int p_recv_buffer_size = 65536); - virtual void close(); - virtual Error wait(); - virtual bool is_listening() const; +JavaScript::~JavaScript() {} - virtual IP_Address get_packet_address() const; - virtual int get_packet_port() const; +void JavaScript::_bind_methods() { - virtual void set_dest_address(const IP_Address &p_address, int p_port); + ClassDB::bind_method(D_METHOD("eval", "code", "use_global_execution_context"), &JavaScript::eval, DEFVAL(false)); +} - static void make_default(); - PacketPeerUDPWinsock(); - ~PacketPeerUDPWinsock(); -}; -#endif // PACKET_PEER_UDP_WINSOCK_H +#if !defined(JAVASCRIPT_ENABLED) || !defined(JAVASCRIPT_EVAL_ENABLED) +Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) { + + return Variant(); +} +#endif diff --git a/platform/javascript/api/api.h b/platform/javascript/api/api.h new file mode 100644 index 0000000000..5f22f082e9 --- /dev/null +++ b/platform/javascript/api/api.h @@ -0,0 +1,32 @@ +/*************************************************************************/ +/* api.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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_javascript_api(); +void unregister_javascript_api(); diff --git a/platform/javascript/javascript_eval.h b/platform/javascript/api/javascript_eval.h index ed7cf383da..05f7c9f38a 100644 --- a/platform/javascript/javascript_eval.h +++ b/platform/javascript/api/javascript_eval.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,7 +27,6 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef JAVASCRIPT_EVAL_ENABLED #ifndef JAVASCRIPT_EVAL_H #define JAVASCRIPT_EVAL_H @@ -52,4 +51,3 @@ public: }; #endif // JAVASCRIPT_EVAL_H -#endif // JAVASCRIPT_EVAL_ENABLED diff --git a/platform/javascript/audio_driver_javascript.cpp b/platform/javascript/audio_driver_javascript.cpp index cd3974669f..5bf345e6cd 100644 --- a/platform/javascript/audio_driver_javascript.cpp +++ b/platform/javascript/audio_driver_javascript.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,32 +27,23 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "audio_driver_javascript.h" #include <emscripten.h> -#include <string.h> - -#define MAX_NUMBER_INTERFACES 3 -#define MAX_NUMBER_OUTPUT_DEVICES 6 - -/* Structure for passing information to callback function */ - -//AudioDriverJavaScript* AudioDriverJavaScript::s_ad=NULL; AudioDriverJavaScript *AudioDriverJavaScript::singleton_js = NULL; + const char *AudioDriverJavaScript::get_name() const { return "JavaScript"; } -extern "C" { - -void js_audio_driver_mix_function(int p_frames) { +extern "C" EMSCRIPTEN_KEEPALIVE void js_audio_driver_mix_function(int p_frames) { //print_line("MIXI! "+itos(p_frames)); AudioDriverJavaScript::singleton_js->mix_to_js(p_frames); } -} void AudioDriverJavaScript::mix_to_js(int p_frames) { @@ -65,11 +56,11 @@ void AudioDriverJavaScript::mix_to_js(int p_frames) { audio_server_process(p_frames, stream_buffer); for (int i = 0; i < tomix * internal_buffer_channels; i++) { - internal_buffer[i] = float(stream_buffer[i] >> 16) * 32768.0; + internal_buffer[i] = float(stream_buffer[i] >> 16) / 32768.0; } /* clang-format off */ - EM_ASM_({ + EM_ASM_ARGS({ var data = HEAPF32.subarray($0 / 4, $0 / 4 + $2 * 2); for (var channel = 0; channel < _as_output_buffer.numberOfChannels; channel++) { @@ -95,29 +86,24 @@ Error AudioDriverJavaScript::init() { void AudioDriverJavaScript::start() { - internal_buffer_channels = 2; internal_buffer = memnew_arr(float, INTERNAL_BUFFER_SIZE *internal_buffer_channels); stream_buffer = memnew_arr(int32_t, INTERNAL_BUFFER_SIZE * 4); //max 4 channels /* clang-format off */ - EM_ASM( - _as_audioctx = new (window.AudioContext || window.webkitAudioContext)(); - - audio_server_mix_function = Module.cwrap('js_audio_driver_mix_function', 'void', ['number']); - ); - - int buffer_latency = 16384; - EM_ASM_( { - _as_script_node = _as_audioctx.createScriptProcessor($0, 0, 2); + mix_rate = EM_ASM_INT({ + _as_audioctx = new (window.AudioContext || window.webkitAudioContext); + _as_script_node = _as_audioctx.createScriptProcessor($0, 0, $1); _as_script_node.connect(_as_audioctx.destination); console.log(_as_script_node.bufferSize); + var jsAudioDriverMixFunction = cwrap('js_audio_driver_mix_function', null, ['number']); _as_script_node.onaudioprocess = function(audioProcessingEvent) { - // The output buffer contains the samples that will be modified and played + // The output buffer contains the samples that will be modified and played _as_output_buffer = audioProcessingEvent.outputBuffer; - audio_server_mix_function(_as_output_buffer.getChannelData(0).length); - } - }, buffer_latency); + jsAudioDriverMixFunction([_as_output_buffer.getChannelData(0).length]); + }; + return _as_audioctx.sampleRate; + }, INTERNAL_BUFFER_SIZE, internal_buffer_channels); /* clang-format on */ } @@ -152,6 +138,7 @@ void AudioDriverJavaScript::finish() { AudioDriverJavaScript::AudioDriverJavaScript() { - mix_rate = 44100; + internal_buffer_channels = 2; + mix_rate = DEFAULT_MIX_RATE; singleton_js = this; } diff --git a/platform/javascript/audio_driver_javascript.h b/platform/javascript/audio_driver_javascript.h index c3adeca07b..d78ab8eea4 100644 --- a/platform/javascript/audio_driver_javascript.h +++ b/platform/javascript/audio_driver_javascript.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,25 +27,21 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef AUDIO_DRIVER_JAVASCRIPT_H #define AUDIO_DRIVER_JAVASCRIPT_H #include "servers/audio_server.h" -#include "os/mutex.h" - class AudioDriverJavaScript : public AudioDriver { enum { INTERNAL_BUFFER_SIZE = 4096, - STREAM_SCALE_BITS = 12 - }; int mix_rate; float *internal_buffer; int internal_buffer_channels; - int internal_buffer_size; int32_t *stream_buffer; public: diff --git a/platform/javascript/audio_server_javascript.cpp b/platform/javascript/audio_server_javascript.cpp deleted file mode 100644 index ab9f66ce5b..0000000000 --- a/platform/javascript/audio_server_javascript.cpp +++ /dev/null @@ -1,853 +0,0 @@ -/*************************************************************************/ -/* audio_server_javascript.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ -#include "audio_server_javascript.h" - -// FIXME: Needs to be ported to the new AudioServer API in 3.0 -#if 0 -#include "emscripten.h" - -AudioMixer *AudioServerJavascript::get_mixer() { - - return NULL; -} - -void AudioServerJavascript::audio_mixer_chunk_callback(int p_frames){ - - -} - - -RID AudioServerJavascript::sample_create(SampleFormat p_format, bool p_stereo, int p_length) { - - Sample *sample = memnew( Sample ); - sample->format=p_format; - sample->stereo=p_stereo; - sample->length=p_length; - sample->loop_begin=0; - sample->loop_end=p_length; - sample->loop_format=SAMPLE_LOOP_NONE; - sample->mix_rate=44100; - sample->index=-1; - - return sample_owner.make_rid(sample); - -} - -void AudioServerJavascript::sample_set_description(RID p_sample, const String& p_description){ - - -} -String AudioServerJavascript::sample_get_description(RID p_sample) const{ - - return String(); -} - -AudioServerJavascript::SampleFormat AudioServerJavascript::sample_get_format(RID p_sample) const{ - - return SAMPLE_FORMAT_PCM8; -} -bool AudioServerJavascript::sample_is_stereo(RID p_sample) const{ - - const Sample *sample = sample_owner.get(p_sample); - ERR_FAIL_COND_V(!sample,false); - return sample->stereo; - -} -int AudioServerJavascript::sample_get_length(RID p_sample) const{ - const Sample *sample = sample_owner.get(p_sample); - ERR_FAIL_COND_V(!sample,0); - return sample->length; -} -const void* AudioServerJavascript::sample_get_data_ptr(RID p_sample) const{ - - return NULL; -} - -void AudioServerJavascript::sample_set_data(RID p_sample, const PoolVector<uint8_t>& p_buffer){ - - Sample *sample = sample_owner.get(p_sample); - ERR_FAIL_COND(!sample); - int chans = sample->stereo?2:1; - - Vector<float> buffer; - buffer.resize(sample->length*chans); - PoolVector<uint8_t>::Read r=p_buffer.read(); - if (sample->format==SAMPLE_FORMAT_PCM8) { - const int8_t*ptr = (const int8_t*)r.ptr(); - for(int i=0;i<sample->length*chans;i++) { - buffer[i]=ptr[i]/128.0; - } - } else if (sample->format==SAMPLE_FORMAT_PCM16){ - const int16_t*ptr = (const int16_t*)r.ptr(); - for(int i=0;i<sample->length*chans;i++) { - buffer[i]=ptr[i]/32768.0; - } - } else { - ERR_EXPLAIN("Unsupported for now"); - ERR_FAIL(); - } - - sample->tmp_data=buffer; - - - -} -PoolVector<uint8_t> AudioServerJavascript::sample_get_data(RID p_sample) const{ - - - return PoolVector<uint8_t>(); -} - -void AudioServerJavascript::sample_set_mix_rate(RID p_sample,int p_rate){ - Sample *sample = sample_owner.get(p_sample); - ERR_FAIL_COND(!sample); - sample->mix_rate=p_rate; - -} - -int AudioServerJavascript::sample_get_mix_rate(RID p_sample) const{ - const Sample *sample = sample_owner.get(p_sample); - ERR_FAIL_COND_V(!sample,0); - return sample->mix_rate; -} - - -void AudioServerJavascript::sample_set_loop_format(RID p_sample,SampleLoopFormat p_format){ - - Sample *sample = sample_owner.get(p_sample); - ERR_FAIL_COND(!sample); - sample->loop_format=p_format; - -} - -AudioServerJavascript::SampleLoopFormat AudioServerJavascript::sample_get_loop_format(RID p_sample) const { - - const Sample *sample = sample_owner.get(p_sample); - ERR_FAIL_COND_V(!sample,SAMPLE_LOOP_NONE); - return sample->loop_format; -} - -void AudioServerJavascript::sample_set_loop_begin(RID p_sample,int p_pos){ - - Sample *sample = sample_owner.get(p_sample); - ERR_FAIL_COND(!sample); - sample->loop_begin=p_pos; - -} -int AudioServerJavascript::sample_get_loop_begin(RID p_sample) const{ - - const Sample *sample = sample_owner.get(p_sample); - ERR_FAIL_COND_V(!sample,0); - return sample->loop_begin; -} - -void AudioServerJavascript::sample_set_loop_end(RID p_sample,int p_pos){ - - Sample *sample = sample_owner.get(p_sample); - ERR_FAIL_COND(!sample); - sample->loop_end=p_pos; - -} -int AudioServerJavascript::sample_get_loop_end(RID p_sample) const{ - - const Sample *sample = sample_owner.get(p_sample); - ERR_FAIL_COND_V(!sample,0); - return sample->loop_end; -} - - -/* VOICE API */ - -RID AudioServerJavascript::voice_create(){ - - Voice *voice = memnew( Voice ); - - voice->index=voice_base; - voice->volume=1.0; - voice->pan=0.0; - voice->pan_depth=.0; - voice->pan_height=0.0; - voice->chorus=0; - voice->reverb_type=REVERB_SMALL; - voice->reverb=0; - voice->mix_rate=-1; - voice->positional=false; - voice->active=false; - - /* clang-format off */ - EM_ASM_( { - _as_voices[$0] = null; - _as_voice_gain[$0] = _as_audioctx.createGain(); - _as_voice_pan[$0] = _as_audioctx.createStereoPanner(); - _as_voice_gain[$0].connect(_as_voice_pan[$0]); - _as_voice_pan[$0].connect(_as_audioctx.destination); - }, voice_base); - /* clang-format on */ - - voice_base++; - - return voice_owner.make_rid( voice ); -} - -void AudioServerJavascript::voice_play(RID p_voice, RID p_sample){ - - Voice* voice=voice_owner.get(p_voice); - ERR_FAIL_COND(!voice); - Sample *sample=sample_owner.get(p_sample); - ERR_FAIL_COND(!sample); - - // due to how webaudio works, sample cration is deferred until used - // sorry! WebAudio absolutely sucks - - - if (sample->index==-1) { - //create sample if not created - ERR_FAIL_COND(sample->tmp_data.size()==0); - sample->index=sample_base; - /* clang-format off */ - EM_ASM_({ - _as_samples[$0] = _as_audioctx.createBuffer($1, $2, $3); - }, sample_base, sample->stereo ? 2 : 1, sample->length, sample->mix_rate); - /* clang-format on */ - - sample_base++; - int chans = sample->stereo?2:1; - - - for(int i=0;i<chans;i++) { - /* clang-format off */ - EM_ASM_({ - _as_edited_buffer = _as_samples[$0].getChannelData($1); - }, sample->index, i); - /* clang-format on */ - - for(int j=0;j<sample->length;j++) { - /* clang-format off */ - EM_ASM_({ - _as_edited_buffer[$0] = $1; - }, j, sample->tmp_data[j * chans + i]); - /* clang-format on */ - } - } - - sample->tmp_data.clear(); - } - - - voice->sample_mix_rate=sample->mix_rate; - if (voice->mix_rate==-1) { - voice->mix_rate=voice->sample_mix_rate; - } - - float freq_diff = Math::log(float(voice->mix_rate)/float(voice->sample_mix_rate))/Math::log(2.0); - int detune = int(freq_diff*1200.0); - - /* clang-format off */ - EM_ASM_({ - if (_as_voices[$0] !== null) { - _as_voices[$0].stop(); //stop and byebye - } - _as_voices[$0] = _as_audioctx.createBufferSource(); - _as_voices[$0].connect(_as_voice_gain[$0]); - _as_voices[$0].buffer = _as_samples[$1]; - _as_voices[$0].loopStart.value = $1; - _as_voices[$0].loopEnd.value = $2; - _as_voices[$0].loop.value = $3; - _as_voices[$0].detune.value = $6; - _as_voice_pan[$0].pan.value = $4; - _as_voice_gain[$0].gain.value = $5; - _as_voices[$0].start(); - _as_voices[$0].onended = function() { - _as_voices[$0].disconnect(_as_voice_gain[$0]); - _as_voices[$0] = null; - } - }, voice->index, sample->index, sample->mix_rate * sample->loop_begin, sample->mix_rate * sample->loop_end, sample->loop_format != SAMPLE_LOOP_NONE, voice->pan, voice->volume * fx_volume_scale, detune); - /* clang-format on */ - - voice->active=true; -} - -void AudioServerJavascript::voice_set_volume(RID p_voice, float p_volume){ - - Voice* voice=voice_owner.get(p_voice); - ERR_FAIL_COND(!voice); - - voice->volume=p_volume; - - if (voice->active) { - /* clang-format off */ - EM_ASM_({ - _as_voice_gain[$0].gain.value = $1; - }, voice->index, voice->volume * fx_volume_scale); - /* clang-format on */ - } - -} -void AudioServerJavascript::voice_set_pan(RID p_voice, float p_pan, float p_depth,float height){ - - Voice* voice=voice_owner.get(p_voice); - ERR_FAIL_COND(!voice); - - voice->pan=p_pan; - voice->pan_depth=p_depth; - voice->pan_height=height; - - if (voice->active) { - /* clang-format off */ - EM_ASM_({ - _as_voice_pan[$0].pan.value = $1; - }, voice->index, voice->pan); - /* clang-format on */ - } -} -void AudioServerJavascript::voice_set_filter(RID p_voice, FilterType p_type, float p_cutoff, float p_resonance, float p_gain){ - -} -void AudioServerJavascript::voice_set_chorus(RID p_voice, float p_chorus ){ - -} -void AudioServerJavascript::voice_set_reverb(RID p_voice, ReverbRoomType p_room_type, float p_reverb){ - -} -void AudioServerJavascript::voice_set_mix_rate(RID p_voice, int p_mix_rate){ - - Voice* voice=voice_owner.get(p_voice); - ERR_FAIL_COND(!voice); - - voice->mix_rate=p_mix_rate; - - if (voice->active) { - - float freq_diff = Math::log(float(voice->mix_rate)/float(voice->sample_mix_rate))/Math::log(2.0); - int detune = int(freq_diff*1200.0); - /* clang-format off */ - EM_ASM_({ - _as_voices[$0].detune.value = $1; - }, voice->index, detune); - /* clang-format on */ - } -} -void AudioServerJavascript::voice_set_positional(RID p_voice, bool p_positional){ - -} - -float AudioServerJavascript::voice_get_volume(RID p_voice) const{ - - Voice* voice=voice_owner.get(p_voice); - ERR_FAIL_COND_V(!voice,0); - - return voice->volume; -} -float AudioServerJavascript::voice_get_pan(RID p_voice) const{ - - Voice* voice=voice_owner.get(p_voice); - ERR_FAIL_COND_V(!voice,0); - - return voice->pan; -} -float AudioServerJavascript::voice_get_pan_depth(RID p_voice) const{ - Voice* voice=voice_owner.get(p_voice); - ERR_FAIL_COND_V(!voice,0); - - return voice->pan_depth; -} -float AudioServerJavascript::voice_get_pan_height(RID p_voice) const{ - - Voice* voice=voice_owner.get(p_voice); - ERR_FAIL_COND_V(!voice,0); - - return voice->pan_height; -} -AudioServerJavascript::FilterType AudioServerJavascript::voice_get_filter_type(RID p_voice) const{ - - return FILTER_NONE; -} -float AudioServerJavascript::voice_get_filter_cutoff(RID p_voice) const{ - - return 0; -} -float AudioServerJavascript::voice_get_filter_resonance(RID p_voice) const{ - - return 0; -} -float AudioServerJavascript::voice_get_chorus(RID p_voice) const{ - - return 0; -} -AudioServerJavascript::ReverbRoomType AudioServerJavascript::voice_get_reverb_type(RID p_voice) const{ - - return REVERB_SMALL; -} -float AudioServerJavascript::voice_get_reverb(RID p_voice) const{ - - return 0; -} - -int AudioServerJavascript::voice_get_mix_rate(RID p_voice) const{ - - return 44100; -} - -bool AudioServerJavascript::voice_is_positional(RID p_voice) const{ - - return false; -} - -void AudioServerJavascript::voice_stop(RID p_voice){ - - Voice* voice=voice_owner.get(p_voice); - ERR_FAIL_COND(!voice); - - if (voice->active) { - /* clang-format off */ - EM_ASM_({ - if (_as_voices[$0] !== null) { - _as_voices[$0].stop(); - _as_voices[$0].disconnect(_as_voice_gain[$0]); - _as_voices[$0] = null; - } - }, voice->index); - /* clang-format on */ - - voice->active=false; - } - - -} -bool AudioServerJavascript::voice_is_active(RID p_voice) const{ - Voice* voice=voice_owner.get(p_voice); - ERR_FAIL_COND_V(!voice,false); - - return voice->active; -} - -/* STREAM API */ - -RID AudioServerJavascript::audio_stream_create(AudioStream *p_stream) { - - - Stream *s = memnew(Stream); - s->audio_stream=p_stream; - s->event_stream=NULL; - s->active=false; - s->E=NULL; - s->volume_scale=1.0; - p_stream->set_mix_rate(webaudio_mix_rate); - - return stream_owner.make_rid(s); -} - -RID AudioServerJavascript::event_stream_create(EventStream *p_stream) { - - - Stream *s = memnew(Stream); - s->audio_stream=NULL; - s->event_stream=p_stream; - s->active=false; - s->E=NULL; - s->volume_scale=1.0; - //p_stream->set_mix_rate(AudioDriverJavascript::get_singleton()->get_mix_rate()); - - return stream_owner.make_rid(s); - - -} - - -void AudioServerJavascript::stream_set_active(RID p_stream, bool p_active) { - - - Stream *s = stream_owner.get(p_stream); - ERR_FAIL_COND(!s); - - if (s->active==p_active) - return; - - s->active=p_active; - if (p_active) - s->E=active_audio_streams.push_back(s); - else { - active_audio_streams.erase(s->E); - s->E=NULL; - } -} - -bool AudioServerJavascript::stream_is_active(RID p_stream) const { - - Stream *s = stream_owner.get(p_stream); - ERR_FAIL_COND_V(!s,false); - return s->active; -} - -void AudioServerJavascript::stream_set_volume_scale(RID p_stream, float p_scale) { - - Stream *s = stream_owner.get(p_stream); - ERR_FAIL_COND(!s); - s->volume_scale=p_scale; - -} - -float AudioServerJavascript::stream_set_volume_scale(RID p_stream) const { - - Stream *s = stream_owner.get(p_stream); - ERR_FAIL_COND_V(!s,0); - return s->volume_scale; - -} - - -/* Audio Physics API */ - -void AudioServerJavascript::free(RID p_id){ - - if (voice_owner.owns(p_id)) { - Voice* voice=voice_owner.get(p_id); - ERR_FAIL_COND(!voice); - - if (voice->active) { - /* clang-format off */ - EM_ASM_({ - if (_as_voices[$0] !== null) { - _as_voices[$0].stop(); - _as_voices[$0].disconnect(_as_voice_gain[$0]); - } - }, voice->index); - /* clang-format on */ - } - - /* clang-format off */ - EM_ASM_({ - delete _as_voices[$0]; - _as_voice_gain[$0].disconnect(_as_voice_pan[$0]); - delete _as_voice_gain[$0]; - _as_voice_pan[$0].disconnect(_as_audioctx.destination); - delete _as_voice_pan[$0]; - }, voice->index); - /* clang-format on */ - - voice_owner.free(p_id); - memdelete(voice); - - } else if (sample_owner.owns(p_id)) { - - Sample *sample = sample_owner.get(p_id); - ERR_FAIL_COND(!sample); - - /* clang-format off */ - EM_ASM_({ - delete _as_samples[$0]; - }, sample->index); - /* clang-format on */ - - sample_owner.free(p_id); - memdelete(sample); - - } else if (stream_owner.owns(p_id)) { - - - Stream *s=stream_owner.get(p_id); - - if (s->active) { - stream_set_active(p_id,false); - } - - memdelete(s); - stream_owner.free(p_id); - } -} - -extern "C" { - - -void audio_server_mix_function(int p_frames) { - - //print_line("MIXI! "+itos(p_frames)); - static_cast<AudioServerJavascript*>(AudioServerJavascript::get_singleton())->mix_to_js(p_frames); -} - -} - -void AudioServerJavascript::mix_to_js(int p_frames) { - - - //process in chunks to make sure to never process more than INTERNAL_BUFFER_SIZE - int todo=p_frames; - int offset=0; - - while(todo) { - - int tomix=MIN(todo,INTERNAL_BUFFER_SIZE); - driver_process_chunk(tomix); - - /* clang-format off */ - EM_ASM_({ - var data = HEAPF32.subarray($0 / 4, $0 / 4 + $2 * 2); - - for (var channel = 0; channel < _as_output_buffer.numberOfChannels; channel++) { - var outputData = _as_output_buffer.getChannelData(channel); - // Loop through samples - for (var sample = 0; sample < $2; sample++) { - // make output equal to the same as the input - outputData[sample + $1] = data[sample * 2 + channel]; - } - } - }, internal_buffer, offset, tomix); - /* clang-format on */ - - todo-=tomix; - offset+=tomix; - } -} - -void AudioServerJavascript::init(){ - - /* - // clang-format off - EM_ASM( - console.log('server is ' + audio_server); - ); - // clang-format on - */ - - - //int latency = GLOBAL_DEF("javascript/audio_latency",16384); - - internal_buffer_channels=2; - internal_buffer = memnew_arr(float,INTERNAL_BUFFER_SIZE*internal_buffer_channels); - stream_buffer = memnew_arr(int32_t,INTERNAL_BUFFER_SIZE*4); //max 4 channels - - stream_volume=0.3; - - int buffer_latency=16384; - - /* clang-format off */ - EM_ASM_( { - _as_script_node = _as_audioctx.createScriptProcessor($0, 0, 2); - _as_script_node.connect(_as_audioctx.destination); - console.log(_as_script_node.bufferSize); - - _as_script_node.onaudioprocess = function(audioProcessingEvent) { - // The output buffer contains the samples that will be modified and played - _as_output_buffer = audioProcessingEvent.outputBuffer; - audio_server_mix_function(_as_output_buffer.getChannelData(0).length); - } - }, buffer_latency); - /* clang-format on */ - - -} - -void AudioServerJavascript::finish(){ - -} -void AudioServerJavascript::update(){ - - for(List<Stream*>::Element *E=active_audio_streams.front();E;) { //stream might be removed durnig this callback - - List<Stream*>::Element *N=E->next(); - - if (E->get()->audio_stream) - E->get()->audio_stream->update(); - - E=N; - } -} - -/* MISC config */ - -void AudioServerJavascript::lock(){ - -} -void AudioServerJavascript::unlock(){ - -} -int AudioServerJavascript::get_default_channel_count() const{ - - return 1; -} -int AudioServerJavascript::get_default_mix_rate() const{ - - return 44100; -} - -void AudioServerJavascript::set_stream_global_volume_scale(float p_volume){ - - stream_volume_scale=p_volume; -} -void AudioServerJavascript::set_fx_global_volume_scale(float p_volume){ - - fx_volume_scale=p_volume; -} -void AudioServerJavascript::set_event_voice_global_volume_scale(float p_volume){ - -} - -float AudioServerJavascript::get_stream_global_volume_scale() const{ - return 1; -} -float AudioServerJavascript::get_fx_global_volume_scale() const{ - - return 1; -} -float AudioServerJavascript::get_event_voice_global_volume_scale() const{ - - return 1; -} - -uint32_t AudioServerJavascript::read_output_peak() const{ - - return 0; -} - -AudioServerJavascript *AudioServerJavascript::singleton=NULL; - -AudioServer *AudioServerJavascript::get_singleton() { - return singleton; -} - -double AudioServerJavascript::get_mix_time() const{ - - return 0; -} -double AudioServerJavascript::get_output_delay() const { - - return 0; -} - - -void AudioServerJavascript::driver_process_chunk(int p_frames) { - - - - int samples=p_frames*internal_buffer_channels; - - for(int i=0;i<samples;i++) { - internal_buffer[i]=0; - } - - - for(List<Stream*>::Element *E=active_audio_streams.front();E;E=E->next()) { - - ERR_CONTINUE(!E->get()->active); // bug? - - - AudioStream *as=E->get()->audio_stream; - if (!as) - continue; - - int channels=as->get_channel_count(); - if (channels==0) - continue; // does not want mix - if (!as->mix(stream_buffer,p_frames)) - continue; //nothing was mixed!! - - int32_t stream_vol_scale=(stream_volume*stream_volume_scale*E->get()->volume_scale)*(1<<STREAM_SCALE_BITS); - -#define STRSCALE(m_val) ((((m_val >> STREAM_SCALE_BITS) * stream_vol_scale) >> 8) / 8388608.0) - switch(channels) { - case 1: { - - for(int i=0;i<p_frames;i++) { - - internal_buffer[(i<<1)+0]+=STRSCALE(stream_buffer[i]); - internal_buffer[(i<<1)+1]+=STRSCALE(stream_buffer[i]); - } - } break; - case 2: { - - for(int i=0;i<p_frames*2;i++) { - - internal_buffer[i]+=STRSCALE(stream_buffer[i]); - } - } break; - case 4: { - - for(int i=0;i<p_frames;i++) { - - internal_buffer[(i<<2)+0]+=STRSCALE((stream_buffer[(i<<2)+0]+stream_buffer[(i<<2)+2])>>1); - internal_buffer[(i<<2)+1]+=STRSCALE((stream_buffer[(i<<2)+1]+stream_buffer[(i<<2)+3])>>1); - } - } break; - - - } - -#undef STRSCALE - } -} - - -/*void AudioServerSW::driver_process(int p_frames,int32_t *p_buffer) { - - - _output_delay=p_frames/double(AudioDriverSW::get_singleton()->get_mix_rate()); - //process in chunks to make sure to never process more than INTERNAL_BUFFER_SIZE - int todo=p_frames; - while(todo) { - - int tomix=MIN(todo,INTERNAL_BUFFER_SIZE); - driver_process_chunk(tomix,p_buffer); - p_buffer+=tomix; - todo-=tomix; - } - - -}*/ - -AudioServerJavascript::AudioServerJavascript() { - - singleton=this; - sample_base=1; - voice_base=1; - /* clang-format off */ - EM_ASM( - _as_samples = {}; - _as_voices = {}; - _as_voice_pan = {}; - _as_voice_gain = {}; - - _as_audioctx = new (window.AudioContext || window.webkitAudioContext)(); - - audio_server_mix_function = Module.cwrap('audio_server_mix_function', 'void', ['number']); - ); - /* clang-format on */ - - /* clang-format off */ - webaudio_mix_rate = EM_ASM_INT_V( - return _as_audioctx.sampleRate; - ); - /* clang-format on */ - print_line("WEBAUDIO MIX RATE: "+itos(webaudio_mix_rate)); - event_voice_scale=1.0; - fx_volume_scale=1.0; - stream_volume_scale=1.0; - -} -#endif diff --git a/platform/javascript/audio_server_javascript.h b/platform/javascript/audio_server_javascript.h deleted file mode 100644 index 0773459f56..0000000000 --- a/platform/javascript/audio_server_javascript.h +++ /dev/null @@ -1,229 +0,0 @@ -/*************************************************************************/ -/* audio_server_javascript.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ -#ifndef AUDIO_SERVER_JAVASCRIPT_H -#define AUDIO_SERVER_JAVASCRIPT_H - -// FIXME: Needs to be ported to the new AudioServer API in 3.0 -#if 0 -#include "servers/audio_server.h" - -class AudioServerJavascript : public AudioServer { - - GDCLASS(AudioServerJavascript,AudioServer); - - enum { - INTERNAL_BUFFER_SIZE=4096, - STREAM_SCALE_BITS=12 - - }; - - AudioMixer *get_mixer(); - void audio_mixer_chunk_callback(int p_frames); - - struct Sample { - SampleFormat format; - SampleLoopFormat loop_format; - int loop_begin; - int loop_end; - int length; - int index; - int mix_rate; - bool stereo; - - Vector<float> tmp_data; - }; - - mutable RID_Owner<Sample> sample_owner; - int sample_base; - - struct Voice { - int index; - float volume; - float pan; - float pan_depth; - float pan_height; - - float chorus; - ReverbRoomType reverb_type; - float reverb; - - int mix_rate; - int sample_mix_rate; - bool positional; - - bool active; - - }; - - mutable RID_Owner<Voice> voice_owner; - - int voice_base; - - struct Stream { - bool active; - List<Stream*>::Element *E; - AudioStream *audio_stream; - EventStream *event_stream; - float volume_scale; - }; - - List<Stream*> active_audio_streams; - - //List<Stream*> event_streams; - - float * internal_buffer; - int internal_buffer_channels; - int32_t * stream_buffer; - - mutable RID_Owner<Stream> stream_owner; - - float stream_volume; - float stream_volume_scale; - - float event_voice_scale; - float fx_volume_scale; - - - void driver_process_chunk(int p_frames); - - int webaudio_mix_rate; - - - static AudioServerJavascript *singleton; -public: - - void mix_to_js(int p_frames); - /* SAMPLE API */ - - virtual RID sample_create(SampleFormat p_format, bool p_stereo, int p_length); - - virtual void sample_set_description(RID p_sample, const String& p_description); - virtual String sample_get_description(RID p_sample) const; - - virtual SampleFormat sample_get_format(RID p_sample) const; - virtual bool sample_is_stereo(RID p_sample) const; - virtual int sample_get_length(RID p_sample) const; - virtual const void* sample_get_data_ptr(RID p_sample) const; - - - virtual void sample_set_data(RID p_sample, const PoolVector<uint8_t>& p_buffer); - virtual PoolVector<uint8_t> sample_get_data(RID p_sample) const; - - virtual void sample_set_mix_rate(RID p_sample,int p_rate); - virtual int sample_get_mix_rate(RID p_sample) const; - - virtual void sample_set_loop_format(RID p_sample,SampleLoopFormat p_format); - virtual SampleLoopFormat sample_get_loop_format(RID p_sample) const; - - virtual void sample_set_loop_begin(RID p_sample,int p_pos); - virtual int sample_get_loop_begin(RID p_sample) const; - - virtual void sample_set_loop_end(RID p_sample,int p_pos); - virtual int sample_get_loop_end(RID p_sample) const; - - - /* VOICE API */ - - virtual RID voice_create(); - - virtual void voice_play(RID p_voice, RID p_sample); - - virtual void voice_set_volume(RID p_voice, float p_volume); - virtual void voice_set_pan(RID p_voice, float p_pan, float p_depth=0,float height=0); //pan and depth go from -1 to 1 - virtual void voice_set_filter(RID p_voice, FilterType p_type, float p_cutoff, float p_resonance, float p_gain=0); - virtual void voice_set_chorus(RID p_voice, float p_chorus ); - virtual void voice_set_reverb(RID p_voice, ReverbRoomType p_room_type, float p_reverb); - virtual void voice_set_mix_rate(RID p_voice, int p_mix_rate); - virtual void voice_set_positional(RID p_voice, bool p_positional); - - virtual float voice_get_volume(RID p_voice) const; - virtual float voice_get_pan(RID p_voice) const; //pan and depth go from -1 to 1 - virtual float voice_get_pan_depth(RID p_voice) const; //pan and depth go from -1 to 1 - virtual float voice_get_pan_height(RID p_voice) const; //pan and depth go from -1 to 1 - virtual FilterType voice_get_filter_type(RID p_voice) const; - virtual float voice_get_filter_cutoff(RID p_voice) const; - virtual float voice_get_filter_resonance(RID p_voice) const; - virtual float voice_get_chorus(RID p_voice) const; - virtual ReverbRoomType voice_get_reverb_type(RID p_voice) const; - virtual float voice_get_reverb(RID p_voice) const; - - virtual int voice_get_mix_rate(RID p_voice) const; - virtual bool voice_is_positional(RID p_voice) const; - - virtual void voice_stop(RID p_voice); - virtual bool voice_is_active(RID p_voice) const; - - /* STREAM API */ - - virtual RID audio_stream_create(AudioStream *p_stream); - virtual RID event_stream_create(EventStream *p_stream); - - virtual void stream_set_active(RID p_stream, bool p_active); - virtual bool stream_is_active(RID p_stream) const; - - virtual void stream_set_volume_scale(RID p_stream, float p_scale); - virtual float stream_set_volume_scale(RID p_stream) const; - - /* Audio Physics API */ - - virtual void free(RID p_id); - - virtual void init(); - virtual void finish(); - virtual void update(); - - /* MISC config */ - - virtual void lock(); - virtual void unlock(); - virtual int get_default_channel_count() const; - virtual int get_default_mix_rate() const; - - virtual void set_stream_global_volume_scale(float p_volume); - virtual void set_fx_global_volume_scale(float p_volume); - virtual void set_event_voice_global_volume_scale(float p_volume); - - virtual float get_stream_global_volume_scale() const; - virtual float get_fx_global_volume_scale() const; - virtual float get_event_voice_global_volume_scale() const; - - virtual uint32_t read_output_peak() const; - - static AudioServer *get_singleton(); - - virtual double get_mix_time() const; //useful for video -> audio sync - virtual double get_output_delay() const; - - - AudioServerJavascript(); -}; - -#endif // AUDIO_SERVER_JAVASCRIPT_H -#endif diff --git a/platform/javascript/detect.py b/platform/javascript/detect.py index a2988d9c60..7e6a1518ed 100644 --- a/platform/javascript/detect.py +++ b/platform/javascript/detect.py @@ -19,7 +19,6 @@ def can_build(): def get_opts(): from SCons.Variables import BoolVariable return [ - BoolVariable('wasm', 'Compile to WebAssembly', False), BoolVariable('javascript_eval', 'Enable JavaScript eval interface', True), ] @@ -29,6 +28,11 @@ def get_flags(): return [ ('tools', False), ('module_theora_enabled', False), + # Disabling the mbedtls module reduces file size. + # The module has little use due to the limited networking functionality + # in this platform. For the available networking methods, the browser + # manages TLS. + ('module_mbedtls_enabled', False), ] @@ -50,8 +54,14 @@ def configure(env): ## Build type if (env["target"] == "release"): - env.Append(CCFLAGS=['-O3']) - env.Append(LINKFLAGS=['-O3']) + # Use -Os to prioritize optimizing for reduced file size. This is + # particularly valuable for the web platform because it directly + # decreases download time. + # -Os reduces file size by around 5 MiB over -O3. -Oz only saves about + # 100 KiB over -Os, which does not justify the negative impact on + # run-time performance. + env.Append(CCFLAGS=['-Os']) + env.Append(LINKFLAGS=['-Os']) elif (env["target"] == "release_debug"): env.Append(CCFLAGS=['-O2', '-DDEBUG_ENABLED']) @@ -103,20 +113,13 @@ def configure(env): ## Link flags - env.Append(LINKFLAGS=['-s', 'EXTRA_EXPORTED_RUNTIME_METHODS="[\'FS\']"']) + env.Append(LINKFLAGS=['-s', 'BINARYEN=1']) + env.Append(LINKFLAGS=['-s', 'ALLOW_MEMORY_GROWTH=1']) env.Append(LINKFLAGS=['-s', 'USE_WEBGL2=1']) + env.Append(LINKFLAGS=['-s', 'EXTRA_EXPORTED_RUNTIME_METHODS="[\'FS\']"']) - if env['wasm']: - env.Append(LINKFLAGS=['-s', 'BINARYEN=1']) - # In contrast to asm.js, enabling memory growth on WebAssembly has no - # major performance impact, and causes only a negligible increase in - # memory size. - env.Append(LINKFLAGS=['-s', 'ALLOW_MEMORY_GROWTH=1']) - env.extra_suffix = '.webassembly' + env.extra_suffix - else: - env.Append(LINKFLAGS=['-s', 'ASM_JS=1']) - env.Append(LINKFLAGS=['--separate-asm']) - env.Append(LINKFLAGS=['--memory-init-file', '1']) + env.Append(LINKFLAGS=['-s', 'INVOKE_RUN=0']) + env.Append(LINKFLAGS=['-s', 'NO_EXIT_RUNTIME=1']) # TODO: Move that to opus module's config if 'module_opus_enabled' in env and env['module_opus_enabled']: diff --git a/platform/javascript/dom_keys.h b/platform/javascript/dom_keys.h index d63f165382..4edca63c6d 100644 --- a/platform/javascript/dom_keys.h +++ b/platform/javascript/dom_keys.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef DOM_KEYS_H #define DOM_KEYS_H @@ -262,7 +263,7 @@ int dom2godot_scancode(int dom_keycode) { case DOM_VK_CAPS_LOCK: return KEY_CAPSLOCK; - /* + /* case DOM_VK_KANA: return KEY_UNKNOWN; case DOM_VK_HANGUL: return KEY_UNKNOWN; case DOM_VK_EISU: return KEY_UNKNOWN; @@ -274,7 +275,7 @@ int dom2godot_scancode(int dom_keycode) { case DOM_VK_ESCAPE: return KEY_ESCAPE; - /* + /* case DOM_VK_CONVERT: return KEY_UNKNOWN; case DOM_VK_NONCONVERT: return KEY_UNKNOWN; case DOM_VK_ACCEPT: return KEY_UNKNOWN; @@ -292,7 +293,7 @@ int dom2godot_scancode(int dom_keycode) { case DOM_VK_DOWN: return KEY_DOWN; - //case DOM_VK_SELECT: return KEY_UNKNOWN; + //case DOM_VK_SELECT: return KEY_UNKNOWN; case DOM_VK_PRINTSCREEN: // this is the usual printScreen key case DOM_VK_PRINT: // maybe for alt+printScreen or physical printers? @@ -320,7 +321,7 @@ int dom2godot_scancode(int dom_keycode) { case DOM_VK_DIVIDE: return KEY_KP_DIVIDE; - /* + /* case DOM_VK_F17: return KEY_UNKNOWN; case DOM_VK_F18: return KEY_UNKNOWN; case DOM_VK_F19: return KEY_UNKNOWN; @@ -335,7 +336,7 @@ int dom2godot_scancode(int dom_keycode) { case DOM_VK_SCROLL_LOCK: return KEY_SCROLLLOCK; - /* + /* case DOM_VK_WIN_OEM_FJ_JISHO: return KEY_UNKNOWN; case DOM_VK_WIN_OEM_FJ_MASSHOU: return KEY_UNKNOWN; case DOM_VK_WIN_OEM_FJ_TOUROKU: return KEY_UNKNOWN; @@ -375,7 +376,7 @@ int dom2godot_scancode(int dom_keycode) { case DOM_VK_QUOTE: return KEY_APOSTROPHE; - // rest is OEM/unusual + // rest is OEM/unusual default: return KEY_UNKNOWN; }; diff --git a/platform/javascript/engine.js b/platform/javascript/engine.js index 99d1c20bbd..bca1851f40 100644 --- a/platform/javascript/engine.js +++ b/platform/javascript/engine.js @@ -5,7 +5,6 @@ (function() { var engine = Engine; - var USING_WASM = engine.USING_WASM; var DOWNLOAD_ATTEMPTS_MAX = 4; var basePath = null; @@ -32,87 +31,101 @@ this.rtenv = null; - var gameInitPromise = null; + var initPromise = null; var unloadAfterInit = true; - var memorySize = 268435456; + var preloadedFiles = []; + + var resizeCanvasOnStart = true; var progressFunc = null; - var pckProgressTracker = {}; + var preloadProgressTracker = {}; var lastProgress = { loaded: 0, total: 0 }; var canvas = null; + var executableName = null; + var locale = null; var stdout = null; var stderr = null; - this.initGame = function(mainPack) { - - if (!gameInitPromise) { + this.init = function(newBasePath) { - if (mainPack === undefined) { - if (basePath !== null) { - mainPack = basePath + '.pck'; - } else { - return Promise.reject(new Error("No main pack to load specified")); - } - } - if (basePath === null) - basePath = getBasePath(mainPack); - - gameInitPromise = Engine.initEngine().then( + if (!initPromise) { + initPromise = Engine.load(newBasePath).then( instantiate.bind(this) ); - var gameLoadPromise = loadPromise(mainPack, pckProgressTracker).then(function(xhr) { return xhr.response; }); - gameInitPromise = Promise.all([gameLoadPromise, gameInitPromise]).then(function(values) { - // resolve with pck - return new Uint8Array(values[0]); - }); - if (unloadAfterInit) - gameInitPromise.then(Engine.unloadEngine); requestAnimationFrame(animateProgress); + if (unloadAfterInit) + initPromise.then(Engine.unloadEngine); } - return gameInitPromise; + return initPromise; }; - function instantiate(initializer) { + function instantiate(wasmBuf) { - var rtenvOpts = { - noInitialRun: true, - thisProgram: getBaseName(basePath), + var rtenvProps = { engine: this, + ENV: {}, }; if (typeof stdout === 'function') - rtenvOpts.print = stdout; + rtenvProps.print = stdout; if (typeof stderr === 'function') - rtenvOpts.printErr = stderr; - if (typeof WebAssembly === 'object' && initializer instanceof ArrayBuffer) { - rtenvOpts.instantiateWasm = function(imports, onSuccess) { - WebAssembly.instantiate(initializer, imports).then(function(result) { - onSuccess(result.instance); - }); - return {}; - }; - } else if (initializer.asm && initializer.mem) { - rtenvOpts.asm = initializer.asm; - rtenvOpts.memoryInitializerRequest = initializer.mem; - rtenvOpts.TOTAL_MEMORY = memorySize; - } else { - throw new Error("Invalid initializer"); - } + rtenvProps.printErr = stderr; + rtenvProps.instantiateWasm = function(imports, onSuccess) { + WebAssembly.instantiate(wasmBuf, imports).then(function(result) { + onSuccess(result.instance); + }); + return {}; + }; return new Promise(function(resolve, reject) { - rtenvOpts.onRuntimeInitialized = resolve; - rtenvOpts.onAbort = reject; - rtenvOpts.engine.rtenv = Engine.RuntimeEnvironment(rtenvOpts); + rtenvProps.onRuntimeInitialized = resolve; + rtenvProps.onAbort = reject; + rtenvProps.engine.rtenv = Engine.RuntimeEnvironment(rtenvProps); }); } - this.start = function(mainPack) { + this.preloadFile = function(pathOrBuffer, bufferFilename) { + + if (pathOrBuffer instanceof ArrayBuffer) { + pathOrBuffer = new Uint8Array(pathOrBuffer); + } else if (ArrayBuffer.isView(pathOrBuffer)) { + pathOrBuffer = new Uint8Array(pathOrBuffer.buffer); + } + if (pathOrBuffer instanceof Uint8Array) { + preloadedFiles.push({ + name: bufferFilename, + buffer: pathOrBuffer + }); + return Promise.resolve(); + } else if (typeof pathOrBuffer === 'string') { + return loadPromise(pathOrBuffer, preloadProgressTracker).then(function(xhr) { + preloadedFiles.push({ + name: pathOrBuffer, + buffer: xhr.response + }); + }); + } else { + throw Promise.reject("Invalid object for preloading"); + } + }; + + this.start = function() { + + return this.init().then( + Function.prototype.apply.bind(synchronousStart, this, arguments) + ); + }; + + this.startGame = function(mainPack) { - return this.initGame(mainPack).then(synchronousStart.bind(this)); + executableName = getBaseName(mainPack); + return Promise.all([this.init(getBasePath(mainPack)), this.preloadFile(mainPack)]).then( + Function.prototype.apply.bind(synchronousStart, this, []) + ); }; - function synchronousStart(pckView) { - // TODO don't expect canvas when runninng as cli tool + function synchronousStart() { + if (canvas instanceof HTMLCanvasElement) { this.rtenv.canvas = canvas; } else { @@ -125,13 +138,17 @@ } var actualCanvas = this.rtenv.canvas; - var context = false; + var testContext = false; + var testCanvas; try { - context = actualCanvas.getContext('webgl2') || actualCanvas.getContext('experimental-webgl2'); + testCanvas = document.createElement('canvas'); + testContext = testCanvas.getContext('webgl2') || testCanvas.getContext('experimental-webgl2'); } catch (e) {} - if (!context) { + if (!testContext) { throw new Error("WebGL 2 not available"); } + testCanvas = null; + testContext = null; // canvas can grab focus on click if (actualCanvas.tabIndex < 0) { @@ -147,15 +164,33 @@ ev.preventDefault(); }, false); - this.rtenv.FS.createDataFile('/', this.rtenv.thisProgram + '.pck', pckView, true, true, true); - gameInitPromise = null; - this.rtenv.callMain(); + if (locale) { + this.rtenv.locale = locale; + } else { + this.rtenv.locale = navigator.languages ? navigator.languages[0] : navigator.language; + } + this.rtenv.locale = this.rtenv.locale.split('.')[0]; + this.rtenv.resizeCanvasOnStart = resizeCanvasOnStart; + + this.rtenv.thisProgram = executableName || getBaseName(basePath); + + preloadedFiles.forEach(function(file) { + this.rtenv.FS.createDataFile('/', file.name, new Uint8Array(file.buffer), true, true, true); + }, this); + + preloadedFiles = null; + initPromise = null; + this.rtenv.callMain(arguments); } this.setProgressFunc = function(func) { progressFunc = func; }; + this.setResizeCanvasOnStart = function(enabled) { + resizeCanvasOnStart = enabled; + }; + function animateProgress() { var loaded = 0; @@ -163,7 +198,7 @@ var totalIsValid = true; var progressIsFinal = true; - [loadingFiles, pckProgressTracker].forEach(function(tracker) { + [loadingFiles, preloadProgressTracker].forEach(function(tracker) { Object.keys(tracker).forEach(function(file) { if (!tracker[file].final) progressIsFinal = false; @@ -190,14 +225,20 @@ canvas = elem; }; - this.setAsmjsMemorySize = function(size) { - memorySize = size; + this.setExecutableName = function(newName) { + + executableName = newName; + }; + + this.setLocale = function(newLocale) { + + locale = newLocale; }; this.setUnloadAfterInit = function(enabled) { - if (enabled && !unloadAfterInit && gameInitPromise) { - gameInitPromise.then(Engine.unloadEngine); + if (enabled && !unloadAfterInit && initPromise) { + initPromise.then(Engine.unloadEngine); } unloadAfterInit = enabled; }; @@ -232,26 +273,16 @@ Engine.RuntimeEnvironment = engine.RuntimeEnvironment; - Engine.initEngine = function(newBasePath) { + Engine.load = function(newBasePath) { if (newBasePath !== undefined) basePath = getBasePath(newBasePath); if (engineLoadPromise === null) { - if (USING_WASM) { - if (typeof WebAssembly !== 'object') - return Promise.reject(new Error("Browser doesn't support WebAssembly")); - // TODO cache/retrieve module to/from idb - engineLoadPromise = loadPromise(basePath + '.wasm').then(function(xhr) { - return xhr.response; - }); - } else { - var asmjsPromise = loadPromise(basePath + '.asm.js').then(function(xhr) { - return asmjsModulePromise(xhr.response); - }); - var memPromise = loadPromise(basePath + '.mem'); - engineLoadPromise = Promise.all([asmjsPromise, memPromise]).then(function(values) { - return { asm: values[0], mem: values[1] }; - }); - } + if (typeof WebAssembly !== 'object') + return Promise.reject(new Error("Browser doesn't support WebAssembly")); + // TODO cache/retrieve module to/from idb + engineLoadPromise = loadPromise(basePath + '.wasm').then(function(xhr) { + return xhr.response; + }); engineLoadPromise = engineLoadPromise.catch(function(err) { engineLoadPromise = null; throw err; @@ -260,34 +291,7 @@ return engineLoadPromise; }; - function asmjsModulePromise(module) { - var elem = document.createElement('script'); - var script = new Blob([ - 'Engine.asm = (function() { var Module = {};', - module, - 'return Module.asm; })();' - ]); - var url = URL.createObjectURL(script); - elem.src = url; - return new Promise(function(resolve, reject) { - elem.addEventListener('load', function() { - URL.revokeObjectURL(url); - var asm = Engine.asm; - Engine.asm = undefined; - setTimeout(function() { - // delay to reclaim compilation memory - resolve(asm); - }, 1); - }); - elem.addEventListener('error', function() { - URL.revokeObjectURL(url); - reject("asm.js faiilure"); - }); - document.body.appendChild(elem); - }); - } - - Engine.unloadEngine = function() { + Engine.unload = function() { engineLoadPromise = null; }; @@ -306,7 +310,7 @@ if (!file.endsWith('.js')) { xhr.responseType = 'arraybuffer'; } - ['loadstart', 'progress', 'load', 'error', 'timeout', 'abort'].forEach(function(ev) { + ['loadstart', 'progress', 'load', 'error', 'abort'].forEach(function(ev) { xhr.addEventListener(ev, onXHREvent.bind(xhr, resolve, reject, file, tracker)); }); xhr.send(); @@ -321,7 +325,7 @@ this.abort(); return; } else { - loadXHR(resolve, reject, file); + setTimeout(loadXHR.bind(null, resolve, reject, file, tracker), 1000); } } @@ -348,12 +352,11 @@ break; case 'error': - case 'timeout': if (++tracker[file].attempts >= DOWNLOAD_ATTEMPTS_MAX) { tracker[file].final = true; reject(new Error("Failed loading file '" + file + "'")); } else { - loadXHR(resolve, reject, file); + setTimeout(loadXHR.bind(null, resolve, reject, file, tracker), 1000); } break; diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp index 4a97bf4c32..d81aa25c32 100644 --- a/platform/javascript/export/export.cpp +++ b/platform/javascript/export/export.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,16 +27,16 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "editor/editor_node.h" #include "editor_export.h" #include "io/zip_io.h" +#include "main/splash.gen.h" #include "platform/javascript/logo.gen.h" #include "platform/javascript/run_icon.gen.h" #define EXPORT_TEMPLATE_WEBASSEMBLY_RELEASE "webassembly_release.zip" #define EXPORT_TEMPLATE_WEBASSEMBLY_DEBUG "webassembly_debug.zip" -#define EXPORT_TEMPLATE_ASMJS_RELEASE "javascript_release.zip" -#define EXPORT_TEMPLATE_ASMJS_DEBUG "javascript_debug.zip" class EditorExportPlatformJavaScript : public EditorExportPlatform { @@ -47,25 +47,18 @@ class EditorExportPlatformJavaScript : public EditorExportPlatform { bool runnable_when_last_polled; void _fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug); - void _fix_fsloader_js(Vector<uint8_t> &p_js, const String &p_pack_name, uint64_t p_pack_size); public: - enum Target { - TARGET_WEBASSEMBLY, - TARGET_ASMJS - }; - virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features); virtual void get_export_options(List<ExportOption> *r_options); - 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; - virtual String get_binary_extension() const; + virtual String get_binary_extension(const Ref<EditorExportPreset> &p_preset) const; virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0); virtual bool poll_devices(); @@ -78,7 +71,7 @@ public: virtual void get_platform_features(List<String> *r_features) { r_features->push_back("web"); - r_features->push_back("JavaScript"); + r_features->push_back(get_os_name()); } EditorExportPlatformJavaScript(); @@ -90,17 +83,9 @@ void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Re String str_export; Vector<String> lines = str_template.split("\n"); - int memory_mb; - if (p_preset->get("options/target").operator int() != TARGET_ASMJS) - // WebAssembly allows memory growth, so start with a reasonable default - memory_mb = 1 << 4; - else - memory_mb = 1 << (p_preset->get("options/memory_size").operator int() + 5); - for (int i = 0; i < lines.size(); i++) { String current_line = lines[i]; - current_line = current_line.replace("$GODOT_TOTAL_MEMORY", itos(memory_mb * 1024 * 1024)); current_line = current_line.replace("$GODOT_BASENAME", p_name); current_line = current_line.replace("$GODOT_HEAD_INCLUDE", p_preset->get("html/head_include")); current_line = current_line.replace("$GODOT_DEBUG_ENABLED", p_debug ? "true" : "false"); @@ -129,24 +114,15 @@ void EditorExportPlatformJavaScript::get_preset_features(const Ref<EditorExportP void EditorExportPlatformJavaScript::get_export_options(List<ExportOption> *r_options) { - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "options/target", PROPERTY_HINT_ENUM, "WebAssembly,asm.js"), TARGET_WEBASSEMBLY)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "options/memory_size", PROPERTY_HINT_ENUM, "32 MB,64 MB,128 MB,256 MB,512 MB,1 GB"), 3)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc2"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc2"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "html/custom_html_shell", PROPERTY_HINT_GLOBAL_FILE, "html"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "html/head_include", PROPERTY_HINT_MULTILINE_TEXT), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "zip"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "zip"), "")); } -bool EditorExportPlatformJavaScript::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const { - - if (p_option == "options/memory_size") { - return p_options["options/target"].operator int() == TARGET_ASMJS; - } - return true; -} - String EditorExportPlatformJavaScript::get_name() const { return "HTML5"; @@ -154,7 +130,7 @@ String EditorExportPlatformJavaScript::get_name() const { String EditorExportPlatformJavaScript::get_os_name() const { - return "JavaScript"; + return "HTML5"; } Ref<Texture> EditorExportPlatformJavaScript::get_logo() const { @@ -166,30 +142,25 @@ bool EditorExportPlatformJavaScript::can_export(const Ref<EditorExportPreset> &p r_missing_templates = false; - if (p_preset->get("options/target").operator int() == TARGET_WEBASSEMBLY) { - if (find_export_template(EXPORT_TEMPLATE_WEBASSEMBLY_RELEASE) == String()) - r_missing_templates = true; - else if (find_export_template(EXPORT_TEMPLATE_WEBASSEMBLY_DEBUG) == String()) - r_missing_templates = true; - } else { - if (find_export_template(EXPORT_TEMPLATE_ASMJS_RELEASE) == String()) - r_missing_templates = true; - else if (find_export_template(EXPORT_TEMPLATE_ASMJS_DEBUG) == String()) - r_missing_templates = true; - } + if (find_export_template(EXPORT_TEMPLATE_WEBASSEMBLY_RELEASE) == String()) + r_missing_templates = true; + else if (find_export_template(EXPORT_TEMPLATE_WEBASSEMBLY_DEBUG) == String()) + r_missing_templates = true; return !r_missing_templates; } -String EditorExportPlatformJavaScript::get_binary_extension() const { +String EditorExportPlatformJavaScript::get_binary_extension(const Ref<EditorExportPreset> &p_preset) const { return "html"; } Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { + ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); String custom_debug = p_preset->get("custom_template/debug"); String custom_release = p_preset->get("custom_template/release"); + String custom_html = p_preset->get("html/custom_html_shell"); String template_path = p_debug ? custom_debug : custom_release; @@ -197,56 +168,45 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese if (template_path == String()) { - if (p_preset->get("options/target").operator int() == TARGET_WEBASSEMBLY) { - if (p_debug) - template_path = find_export_template(EXPORT_TEMPLATE_WEBASSEMBLY_DEBUG); - else - template_path = find_export_template(EXPORT_TEMPLATE_WEBASSEMBLY_RELEASE); - } else { - if (p_debug) - template_path = find_export_template(EXPORT_TEMPLATE_ASMJS_DEBUG); - else - template_path = find_export_template(EXPORT_TEMPLATE_ASMJS_RELEASE); - } + if (p_debug) + template_path = find_export_template(EXPORT_TEMPLATE_WEBASSEMBLY_DEBUG); + else + template_path = find_export_template(EXPORT_TEMPLATE_WEBASSEMBLY_RELEASE); } if (template_path != String() && !FileAccess::exists(template_path)) { - EditorNode::get_singleton()->show_warning(TTR("Template file not found:\n") + template_path); + EditorNode::get_singleton()->show_warning(TTR("Template file not found:") + "\n" + template_path); return ERR_FILE_NOT_FOUND; } String pck_path = p_path.get_basename() + ".pck"; Error error = save_pack(p_preset, pck_path); if (error != OK) { - EditorNode::get_singleton()->show_warning(TTR("Could not write file:\n") + pck_path); + EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + pck_path); return error; } - FileAccess *f = FileAccess::open(pck_path, FileAccess::READ); - if (!f) { - EditorNode::get_singleton()->show_warning(TTR("Could not read file:\n") + pck_path); - return ERR_FILE_CANT_READ; - } - size_t pack_size = f->get_len(); - memdelete(f); - FileAccess *src_f = NULL; zlib_filefunc_def io = zipio_create_io_from_file(&src_f); unzFile pkg = unzOpen2(template_path.utf8().get_data(), &io); if (!pkg) { - EditorNode::get_singleton()->show_warning(TTR("Could not open template for export:\n") + template_path); + EditorNode::get_singleton()->show_warning(TTR("Could not open template for export:") + "\n" + template_path); return ERR_FILE_NOT_FOUND; } - int ret = unzGoToFirstFile(pkg); - while (ret == UNZ_OK) { + if (unzGoToFirstFile(pkg) != UNZ_OK) { + EditorNode::get_singleton()->show_warning(TTR("Invalid export template:") + "\n" + template_path); + unzClose(pkg); + return ERR_FILE_CORRUPT; + } + do { //get filename unz_file_info info; char fname[16384]; - ret = unzGetCurrentFileInfo(pkg, &info, fname, 16384, NULL, 0, NULL, 0); + unzGetCurrentFileInfo(pkg, &info, fname, 16384, NULL, 0, NULL, 0); String file = fname; @@ -255,42 +215,81 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese //read unzOpenCurrentFile(pkg); - unzReadCurrentFile(pkg, data.ptr(), data.size()); + unzReadCurrentFile(pkg, data.ptrw(), data.size()); unzCloseCurrentFile(pkg); //write if (file == "godot.html") { + if (!custom_html.empty()) { + continue; + } _fix_html(data, p_preset, p_path.get_file().get_basename(), p_debug); file = p_path.get_file(); + } else if (file == "godot.js") { file = p_path.get_file().get_basename() + ".js"; } else if (file == "godot.wasm") { file = p_path.get_file().get_basename() + ".wasm"; - } else if (file == "godot.asm.js") { - - file = p_path.get_file().get_basename() + ".asm.js"; - } else if (file == "godot.mem") { - - file = p_path.get_file().get_basename() + ".mem"; } String dst = p_path.get_base_dir().plus_file(file); FileAccess *f = FileAccess::open(dst, FileAccess::WRITE); if (!f) { - EditorNode::get_singleton()->show_warning(TTR("Could not write file:\n") + dst); + EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + dst); unzClose(pkg); return ERR_FILE_CANT_WRITE; } f->store_buffer(data.ptr(), data.size()); memdelete(f); - ret = unzGoToNextFile(pkg); + } while (unzGoToNextFile(pkg) == UNZ_OK); + unzClose(pkg); + + if (!custom_html.empty()) { + + FileAccess *f = FileAccess::open(custom_html, FileAccess::READ); + if (!f) { + EditorNode::get_singleton()->show_warning(TTR("Could not read custom HTML shell:") + "\n" + custom_html); + return ERR_FILE_CANT_READ; + } + Vector<uint8_t> buf; + buf.resize(f->get_len()); + f->get_buffer(buf.ptrw(), buf.size()); + memdelete(f); + _fix_html(buf, p_preset, p_path.get_file().get_basename(), p_debug); + + f = FileAccess::open(p_path, FileAccess::WRITE); + if (!f) { + EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + p_path); + return ERR_FILE_CANT_WRITE; + } + f->store_buffer(buf.ptr(), buf.size()); + memdelete(f); } + Ref<Image> splash; + String splash_path = GLOBAL_GET("application/boot_splash/image"); + splash_path = splash_path.strip_edges(); + if (!splash_path.empty()) { + splash.instance(); + Error err = splash->load(splash_path); + if (err) { + EditorNode::get_singleton()->show_warning(TTR("Could not read boot splash image file:") + "\n" + splash_path + "\n" + TTR("Using default boot splash image.")); + splash.unref(); + } + } + if (splash.is_null()) { + splash = Ref<Image>(memnew(Image(boot_splash_png))); + } + String png_path = p_path.get_base_dir().plus_file(p_path.get_file().get_basename() + ".png"); + if (splash->save_png(png_path) != OK) { + EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + png_path); + return ERR_FILE_CANT_WRITE; + } return OK; } @@ -319,7 +318,7 @@ int EditorExportPlatformJavaScript::get_device_count() const { Error EditorExportPlatformJavaScript::run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) { - String path = EditorSettings::get_singleton()->get_settings_path() + "/tmp/tmp_export.html"; + String path = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmp_export.html"); Error err = export_project(p_preset, true, path, p_debug_flags); if (err) { return err; diff --git a/platform/javascript/export/export.h b/platform/javascript/export/export.h index 0cea0a213b..2835d0723f 100644 --- a/platform/javascript/export/export.h +++ b/platform/javascript/export/export.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,4 +27,5 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + void register_javascript_exporter(); diff --git a/platform/javascript/http_client.h.inc b/platform/javascript/http_client.h.inc new file mode 100644 index 0000000000..d75d33a33a --- /dev/null +++ b/platform/javascript/http_client.h.inc @@ -0,0 +1,53 @@ +/*************************************************************************/ +/* http_client.h.inc */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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. */ +/*************************************************************************/ + +// HTTPClient's additional private members in the javascript platform + +Error prepare_request(Method p_method, const String &p_url, const Vector<String> &p_headers); + +int xhr_id; +int read_limit; +int response_read_offset; +Status status; + +String host; +int port; +bool use_tls; +String username; +String password; + +int polled_response_code; +String polled_response_header; +PoolByteArray polled_response; + +#ifdef DEBUG_ENABLED +bool has_polled; +uint64_t last_polling_frame; +#endif diff --git a/platform/javascript/http_client_javascript.cpp b/platform/javascript/http_client_javascript.cpp new file mode 100644 index 0000000000..118a77835e --- /dev/null +++ b/platform/javascript/http_client_javascript.cpp @@ -0,0 +1,305 @@ +/*************************************************************************/ +/* http_client_javascript.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 "http_request.h" +#include "io/http_client.h" + +Error HTTPClient::connect_to_host(const String &p_host, int p_port, bool p_ssl, bool p_verify_host) { + + close(); + if (p_ssl && !p_verify_host) { + WARN_PRINT("Disabling HTTPClient's host verification is not supported for the HTML5 platform, host will be verified"); + } + + port = p_port; + use_tls = p_ssl; + + host = p_host; + + String host_lower = host.to_lower(); + if (host_lower.begins_with("http://")) { + host = host.substr(7, host.length() - 7); + } else if (host_lower.begins_with("https://")) { + use_tls = true; + host = host.substr(8, host.length() - 8); + } + + ERR_FAIL_COND_V(host.length() < HOST_MIN_LEN, ERR_INVALID_PARAMETER); + + if (port < 0) { + if (use_tls) { + port = PORT_HTTPS; + } else { + port = PORT_HTTP; + } + } + + status = host.is_valid_ip_address() ? STATUS_CONNECTING : STATUS_RESOLVING; + + return OK; +} + +void HTTPClient::set_connection(const Ref<StreamPeer> &p_connection) { + + ERR_EXPLAIN("Accessing an HTTPClient's StreamPeer is not supported for the HTML5 platform"); + ERR_FAIL(); +} + +Ref<StreamPeer> HTTPClient::get_connection() const { + + ERR_EXPLAIN("Accessing an HTTPClient's StreamPeer is not supported for the HTML5 platform"); + ERR_FAIL_V(REF()); +} + +Error HTTPClient::prepare_request(Method p_method, const String &p_url, const Vector<String> &p_headers) { + + ERR_FAIL_INDEX_V(p_method, METHOD_MAX, ERR_INVALID_PARAMETER); + ERR_EXPLAIN("HTTP methods TRACE and CONNECT are not supported for the HTML5 platform"); + ERR_FAIL_COND_V(p_method == METHOD_TRACE || p_method == METHOD_CONNECT, ERR_UNAVAILABLE); + ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(host.empty(), ERR_UNCONFIGURED); + ERR_FAIL_COND_V(port < 0, ERR_UNCONFIGURED); + ERR_FAIL_COND_V(!p_url.begins_with("/"), ERR_INVALID_PARAMETER); + + String url = (use_tls ? "https://" : "http://") + host + ":" + itos(port) + "/" + p_url; + godot_xhr_reset(xhr_id); + godot_xhr_open(xhr_id, _methods[p_method], url.utf8().get_data(), + username.empty() ? NULL : username.utf8().get_data(), + password.empty() ? NULL : password.utf8().get_data()); + + for (int i = 0; i < p_headers.size(); i++) { + int header_separator = p_headers[i].find(": "); + ERR_FAIL_COND_V(header_separator < 0, ERR_INVALID_PARAMETER); + godot_xhr_set_request_header(xhr_id, + p_headers[i].left(header_separator).utf8().get_data(), + p_headers[i].right(header_separator + 2).utf8().get_data()); + } + response_read_offset = 0; + status = STATUS_REQUESTING; + return OK; +} + +Error HTTPClient::request_raw(Method p_method, const String &p_url, const Vector<String> &p_headers, const PoolVector<uint8_t> &p_body) { + + Error err = prepare_request(p_method, p_url, p_headers); + if (err != OK) + return err; + PoolByteArray::Read read = p_body.read(); + godot_xhr_send_data(xhr_id, read.ptr(), p_body.size()); + return OK; +} + +Error HTTPClient::request(Method p_method, const String &p_url, const Vector<String> &p_headers, const String &p_body) { + + Error err = prepare_request(p_method, p_url, p_headers); + if (err != OK) + return err; + godot_xhr_send_string(xhr_id, p_body.utf8().get_data()); + return OK; +} + +void HTTPClient::close() { + + host = ""; + port = -1; + use_tls = false; + status = STATUS_DISCONNECTED; + polled_response.resize(0); + polled_response_code = 0; + polled_response_header = String(); + godot_xhr_reset(xhr_id); +} + +HTTPClient::Status HTTPClient::get_status() const { + + return status; +} + +bool HTTPClient::has_response() const { + + return !polled_response_header.empty(); +} + +bool HTTPClient::is_response_chunked() const { + + // TODO evaluate using moz-chunked-arraybuffer, fetch & ReadableStream + return false; +} + +int HTTPClient::get_response_code() const { + + return polled_response_code; +} + +Error HTTPClient::get_response_headers(List<String> *r_response) { + + if (polled_response_header.empty()) + return ERR_INVALID_PARAMETER; + + Vector<String> header_lines = polled_response_header.split("\r\n", false); + for (int i = 0; i < header_lines.size(); ++i) { + r_response->push_back(header_lines[i]); + } + polled_response_header = String(); + return OK; +} + +int HTTPClient::get_response_body_length() const { + + return polled_response.size(); +} + +PoolByteArray HTTPClient::read_response_body_chunk() { + + ERR_FAIL_COND_V(status != STATUS_BODY, PoolByteArray()); + + int to_read = MIN(read_limit, polled_response.size() - response_read_offset); + PoolByteArray chunk; + chunk.resize(to_read); + PoolByteArray::Write write = chunk.write(); + PoolByteArray::Read read = polled_response.read(); + memcpy(write.ptr(), read.ptr() + response_read_offset, to_read); + write = PoolByteArray::Write(); + read = PoolByteArray::Read(); + response_read_offset += to_read; + + if (response_read_offset == polled_response.size()) { + status = STATUS_CONNECTED; + polled_response.resize(0); + godot_xhr_reset(xhr_id); + } + + return chunk; +} + +void HTTPClient::set_blocking_mode(bool p_enable) { + + ERR_EXPLAIN("HTTPClient blocking mode is not supported for the HTML5 platform"); + ERR_FAIL_COND(p_enable); +} + +bool HTTPClient::is_blocking_mode_enabled() const { + + return false; +} + +void HTTPClient::set_read_chunk_size(int p_size) { + + read_limit = p_size; +} + +Error HTTPClient::poll() { + + switch (status) { + + case STATUS_DISCONNECTED: + return ERR_UNCONFIGURED; + + case STATUS_RESOLVING: + status = STATUS_CONNECTING; + return OK; + + case STATUS_CONNECTING: + status = STATUS_CONNECTED; + return OK; + + case STATUS_CONNECTED: + case STATUS_BODY: + return OK; + + case STATUS_CONNECTION_ERROR: + return ERR_CONNECTION_ERROR; + + case STATUS_REQUESTING: + +#ifdef DEBUG_ENABLED + if (!has_polled) { + has_polled = true; + } else { + // forcing synchronous requests is not possible on the web + if (last_polling_frame == Engine::get_singleton()->get_idle_frames()) { + WARN_PRINT("HTTPClient polled multiple times in one frame, " + "but request cannot progress more than once per " + "frame on the HTML5 platform."); + } + } + last_polling_frame = Engine::get_singleton()->get_idle_frames(); +#endif + + polled_response_code = godot_xhr_get_status(xhr_id); + if (godot_xhr_get_ready_state(xhr_id) != XHR_READY_STATE_DONE) { + return OK; + } else if (!polled_response_code) { + status = STATUS_CONNECTION_ERROR; + return ERR_CONNECTION_ERROR; + } + + status = STATUS_BODY; + + PoolByteArray bytes; + int len = godot_xhr_get_response_headers_length(xhr_id); + bytes.resize(len + 1); + + PoolByteArray::Write write = bytes.write(); + godot_xhr_get_response_headers(xhr_id, reinterpret_cast<char *>(write.ptr()), len); + write[len] = 0; + write = PoolByteArray::Write(); + + PoolByteArray::Read read = bytes.read(); + polled_response_header = String::utf8(reinterpret_cast<const char *>(read.ptr())); + read = PoolByteArray::Read(); + + polled_response.resize(godot_xhr_get_response_length(xhr_id)); + write = polled_response.write(); + godot_xhr_get_response(xhr_id, write.ptr(), polled_response.size()); + write = PoolByteArray::Write(); + break; + } + return OK; +} + +HTTPClient::HTTPClient() { + + xhr_id = godot_xhr_new(); + read_limit = 4096; + status = STATUS_DISCONNECTED; + port = -1; + use_tls = false; + polled_response_code = 0; +#ifdef DEBUG_ENABLED + has_polled = false; + last_polling_frame = 0; +#endif +} + +HTTPClient::~HTTPClient() { + + godot_xhr_free(xhr_id); +} diff --git a/platform/windows/stream_peer_winsock.h b/platform/javascript/http_request.h index 26e2a3e4c9..b5ff46d7fe 100644 --- a/platform/windows/stream_peer_winsock.h +++ b/platform/javascript/http_request.h @@ -1,12 +1,12 @@ /*************************************************************************/ -/* stream_peer_winsock.h */ +/* http_request.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,65 +27,49 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef WINDOWS_ENABLED -#ifndef STREAM_PEER_WINSOCK_H -#define STREAM_PEER_WINSOCK_H +#ifndef HTTP_REQUEST_H +#define HTTP_REQUEST_H -#include "error_list.h" - -#include "core/io/ip_address.h" -#include "core/io/stream_peer_tcp.h" - -class StreamPeerWinsock : public StreamPeerTCP { - -protected: - mutable Status status; - IP::Type sock_type; - - int sockfd; - - Error _block(int p_sockfd, bool p_read, bool p_write) const; - - Error _poll_connection() const; - - IP_Address peer_host; - int peer_port; - - Error write(const uint8_t *p_data, int p_bytes, int &r_sent, bool p_block); - Error read(uint8_t *p_buffer, int p_bytes, int &r_received, bool p_block); - - static StreamPeerTCP *_create(); - -public: - virtual Error connect_to_host(const IP_Address &p_host, uint16_t p_port); - - virtual Error put_data(const uint8_t *p_data, int p_bytes); - virtual Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent); +#ifdef __cplusplus +extern "C" { +#endif - virtual Error get_data(uint8_t *p_buffer, int p_bytes); - virtual Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received); +#include "stddef.h" - virtual int get_available_bytes() const; +typedef enum { + XHR_READY_STATE_UNSENT = 0, + XHR_READY_STATE_OPENED = 1, + XHR_READY_STATE_HEADERS_RECEIVED = 2, + XHR_READY_STATE_LOADING = 3, + XHR_READY_STATE_DONE = 4, +} godot_xhr_ready_state_t; - void set_socket(int p_sockfd, IP_Address p_host, int p_port, IP::Type p_sock_type); +extern int godot_xhr_new(); +extern void godot_xhr_reset(int p_xhr_id); +extern bool godot_xhr_free(int p_xhr_id); - virtual IP_Address get_connected_host() const; - virtual uint16_t get_connected_port() const; +extern int godot_xhr_open(int p_xhr_id, const char *p_method, const char *p_url, const char *p_user = NULL, const char *p_password = NULL); - virtual bool is_connected_to_host() const; - virtual Status get_status() const; - virtual void disconnect_from_host(); +extern void godot_xhr_set_request_header(int p_xhr_id, const char *p_header, const char *p_value); - static void make_default(); - static void cleanup(); +extern void godot_xhr_send_null(int p_xhr_id); +extern void godot_xhr_send_string(int p_xhr_id, const char *p_data); +extern void godot_xhr_send_data(int p_xhr_id, const void *p_data, int p_len); +extern void godot_xhr_abort(int p_xhr_id); - virtual void set_nodelay(bool p_enabled); +/* this is an HTTPClient::ResponseCode, not ::Status */ +extern int godot_xhr_get_status(int p_xhr_id); +extern godot_xhr_ready_state_t godot_xhr_get_ready_state(int p_xhr_id); - StreamPeerWinsock(); - ~StreamPeerWinsock(); -}; +extern int godot_xhr_get_response_headers_length(int p_xhr_id); +extern void godot_xhr_get_response_headers(int p_xhr_id, char *r_dst, int p_len); -#endif // TCP_SOCKET_POSIX_H +extern int godot_xhr_get_response_length(int p_xhr_id); +extern void godot_xhr_get_response(int p_xhr_id, void *r_dst, int p_len); +#ifdef __cplusplus +} #endif + +#endif /* HTTP_REQUEST_H */ diff --git a/platform/javascript/http_request.js b/platform/javascript/http_request.js new file mode 100644 index 0000000000..c420052e54 --- /dev/null +++ b/platform/javascript/http_request.js @@ -0,0 +1,145 @@ +/*************************************************************************/ +/* http_request.js */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +var GodotHTTPRequest = { + + $GodotHTTPRequest: { + + requests: [], + + getUnusedRequestId: function() { + var idMax = GodotHTTPRequest.requests.length; + for (var potentialId = 0; potentialId < idMax; ++potentialId) { + if (GodotHTTPRequest.requests[potentialId] instanceof XMLHttpRequest) { + continue; + } + return potentialId; + } + GodotHTTPRequest.requests.push(null) + return idMax; + }, + + setupRequest: function(xhr) { + xhr.responseType = 'arraybuffer'; + }, + }, + + godot_xhr_new: function() { + var newId = GodotHTTPRequest.getUnusedRequestId(); + GodotHTTPRequest.requests[newId] = new XMLHttpRequest; + GodotHTTPRequest.setupRequest(GodotHTTPRequest.requests[newId]); + return newId; + }, + + godot_xhr_reset: function(xhrId) { + GodotHTTPRequest.requests[xhrId] = new XMLHttpRequest; + GodotHTTPRequest.setupRequest(GodotHTTPRequest.requests[xhrId]); + }, + + godot_xhr_free: function(xhrId) { + GodotHTTPRequest.requests[xhrId].abort(); + GodotHTTPRequest.requests[xhrId] = null; + }, + + godot_xhr_open: function(xhrId, method, url, user, password) { + user = user > 0 ? UTF8ToString(user) : null; + password = password > 0 ? UTF8ToString(password) : null; + GodotHTTPRequest.requests[xhrId].open(UTF8ToString(method), UTF8ToString(url), true, user, password); + }, + + godot_xhr_set_request_header: function(xhrId, header, value) { + GodotHTTPRequest.requests[xhrId].setRequestHeader(UTF8ToString(header), UTF8ToString(value)); + }, + + godot_xhr_send_null: function(xhrId) { + GodotHTTPRequest.requests[xhrId].send(); + }, + + godot_xhr_send_string: function(xhrId, strPtr) { + if (!strPtr) { + Module.printErr("Failed to send string per XHR: null pointer"); + return; + } + GodotHTTPRequest.requests[xhrId].send(UTF8ToString(strPtr)); + }, + + godot_xhr_send_data: function(xhrId, ptr, len) { + if (!ptr) { + Module.printErr("Failed to send data per XHR: null pointer"); + return; + } + if (len < 0) { + Module.printErr("Failed to send data per XHR: buffer length less than 0"); + return; + } + GodotHTTPRequest.requests[xhrId].send(HEAPU8.subarray(ptr, ptr + len)); + }, + + godot_xhr_abort: function(xhrId) { + GodotHTTPRequest.requests[xhrId].abort(); + }, + + godot_xhr_get_status: function(xhrId) { + return GodotHTTPRequest.requests[xhrId].status; + }, + + godot_xhr_get_ready_state: function(xhrId) { + return GodotHTTPRequest.requests[xhrId].readyState; + }, + + godot_xhr_get_response_headers_length: function(xhrId) { + var headers = GodotHTTPRequest.requests[xhrId].getAllResponseHeaders(); + return headers === null ? 0 : lengthBytesUTF8(headers); + }, + + godot_xhr_get_response_headers: function(xhrId, dst, len) { + var str = GodotHTTPRequest.requests[xhrId].getAllResponseHeaders(); + if (str === null) + return; + var buf = new Uint8Array(len + 1); + stringToUTF8Array(str, buf, 0, buf.length); + buf = buf.subarray(0, -1); + HEAPU8.set(buf, dst); + }, + + godot_xhr_get_response_length: function(xhrId) { + var body = GodotHTTPRequest.requests[xhrId].response; + return body === null ? 0 : body.byteLength; + }, + + godot_xhr_get_response: function(xhrId, dst, len) { + var buf = GodotHTTPRequest.requests[xhrId].response; + if (buf === null) + return; + buf = new Uint8Array(buf).subarray(0, len); + HEAPU8.set(buf, dst); + }, +}; + +autoAddDeps(GodotHTTPRequest, "$GodotHTTPRequest"); +mergeInto(LibraryManager.library, GodotHTTPRequest); diff --git a/platform/javascript/javascript_eval.cpp b/platform/javascript/javascript_eval.cpp index 1d737879f6..2ef88345f6 100644 --- a/platform/javascript/javascript_eval.cpp +++ b/platform/javascript/javascript_eval.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,18 +27,12 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifdef JAVASCRIPT_EVAL_ENABLED -#include "javascript_eval.h" +#include "api/javascript_eval.h" #include "emscripten.h" -JavaScript *JavaScript::singleton = NULL; - -JavaScript *JavaScript::get_singleton() { - - return singleton; -} - extern "C" EMSCRIPTEN_KEEPALIVE uint8_t *resize_poolbytearray_and_open_write(PoolByteArray *p_arr, PoolByteArray::Write *r_write, int p_len) { p_arr->resize(p_len); @@ -52,7 +46,7 @@ Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) { bool b; double d; char *s; - } js_data[4]; + } js_data; PoolByteArray arr; PoolByteArray::Write arr_write; @@ -63,9 +57,8 @@ Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) { const CODE = $0; const USE_GLOBAL_EXEC_CONTEXT = $1; const PTR = $2; - const ELEM_LEN = $3; - const BYTEARRAY_PTR = $4; - const BYTEARRAY_WRITE_PTR = $5; + const BYTEARRAY_PTR = $3; + const BYTEARRAY_WRITE_PTR = $4; var eval_ret; try { if (USE_GLOBAL_EXEC_CONTEXT) { @@ -125,56 +118,25 @@ Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) { HEAPU8.set(eval_ret, bytes_ptr); return 20; // POOL_BYTE_ARRAY } - - if (typeof eval_ret.x==='number' && typeof eval_ret.y==='number') { - setValue(PTR, eval_ret.x, 'double'); - setValue(PTR + ELEM_LEN, eval_ret.y, 'double'); - if (typeof eval_ret.z==='number') { - setValue(PTR + ELEM_LEN*2, eval_ret.z, 'double'); - return 7; // VECTOR3 - } - else if (typeof eval_ret.width==='number' && typeof eval_ret.height==='number') { - setValue(PTR + ELEM_LEN*2, eval_ret.width, 'double'); - setValue(PTR + ELEM_LEN*3, eval_ret.height, 'double'); - return 6; // RECT2 - } - return 5; // VECTOR2 - } - - if (typeof eval_ret.r === 'number' && typeof eval_ret.g === 'number' && typeof eval_ret.b === 'number') { - setValue(PTR, eval_ret.r, 'double'); - setValue(PTR + ELEM_LEN, eval_ret.g, 'double'); - setValue(PTR + ELEM_LEN*2, eval_ret.b, 'double'); - setValue(PTR + ELEM_LEN*3, typeof eval_ret.a === 'number' ? eval_ret.a : 1, 'double'); - return 14; // COLOR - } break; } return 0; // NIL - }, p_code.utf8().get_data(), p_use_global_exec_context, js_data, sizeof *js_data, &arr, &arr_write)); + }, p_code.utf8().get_data(), p_use_global_exec_context, &js_data, &arr, &arr_write)); /* clang-format on */ switch (return_type) { case Variant::BOOL: - return js_data->b; + return js_data.b; case Variant::REAL: - return js_data->d; + return js_data.d; case Variant::STRING: { - String str = String::utf8(js_data->s); + String str = String::utf8(js_data.s); /* clang-format off */ - EM_ASM_({ _free($0); }, js_data->s); + EM_ASM_({ _free($0); }, js_data.s); /* clang-format on */ return str; } - case Variant::VECTOR2: - return Vector2(js_data[0].d, js_data[1].d); - case Variant::VECTOR3: - return Vector3(js_data[0].d, js_data[1].d, js_data[2].d); - case Variant::RECT2: - return Rect2(js_data[0].d, js_data[1].d, js_data[2].d, js_data[3].d); - case Variant::COLOR: - return Color(js_data[0].d, js_data[1].d, js_data[2].d, js_data[3].d); case Variant::POOL_BYTE_ARRAY: arr_write = PoolByteArray::Write(); return arr; @@ -182,18 +144,4 @@ Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) { return Variant(); } -void JavaScript::_bind_methods() { - - ClassDB::bind_method(D_METHOD("eval", "code", "use_global_execution_context"), &JavaScript::eval, false); -} - -JavaScript::JavaScript() { - - ERR_FAIL_COND(singleton != NULL); - singleton = this; -} - -JavaScript::~JavaScript() { -} - #endif // JAVASCRIPT_EVAL_ENABLED diff --git a/platform/javascript/javascript_main.cpp b/platform/javascript/javascript_main.cpp index ed4f416cfd..e85fe0800f 100644 --- a/platform/javascript/javascript_main.cpp +++ b/platform/javascript/javascript_main.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "emscripten.h" #include "io/resource_loader.h" #include "main/main.h" @@ -61,11 +62,10 @@ int main(int argc, char *argv[]) { // run the 'main_after_fs_sync' function /* clang-format off */ EM_ASM( - Module.noExitRuntime = true; FS.mkdir('/userfs'); FS.mount(IDBFS, {}, '/userfs'); FS.syncfs(true, function(err) { - Module['ccall']('main_after_fs_sync', null, ['string'], [err ? err.message : ""]) + ccall('main_after_fs_sync', null, ['string'], [err ? err.message : ""]) }); ); /* clang-format on */ diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp index f6446e77da..ace0bdad60 100644 --- a/platform/javascript/os_javascript.cpp +++ b/platform/javascript/os_javascript.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,11 +27,13 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "os_javascript.h" +#include "core/engine.h" #include "core/io/file_access_buffered_fa.h" -#include "core/project_settings.h" #include "dom_keys.h" +#include "drivers/gles2/rasterizer_gles2.h" #include "drivers/gles3/rasterizer_gles3.h" #include "drivers/unix/dir_access_unix.h" #include "drivers/unix/file_access_unix.h" @@ -56,17 +58,19 @@ static void dom2godot_mod(T emscripten_event_ptr, Ref<InputEventWithModifiers> g int OS_JavaScript::get_video_driver_count() const { - return 1; + return VIDEO_DRIVER_MAX; } const char *OS_JavaScript::get_video_driver_name(int p_driver) const { - return "GLES3"; -} - -OS::VideoMode OS_JavaScript::get_default_video_mode() const { - - return OS::VideoMode(); + switch (p_driver) { + case VIDEO_DRIVER_GLES3: + return "GLES3"; + case VIDEO_DRIVER_GLES2: + return "GLES2"; + } + ERR_EXPLAIN("Invalid video driver index " + itos(p_driver)); + ERR_FAIL_V(NULL); } int OS_JavaScript::get_audio_driver_count() const { @@ -85,16 +89,6 @@ void OS_JavaScript::initialize_core() { FileAccess::make_default<FileAccessBufferedFA<FileAccessUnix> >(FileAccess::ACCESS_RESOURCES); } -void OS_JavaScript::initialize_logger() { - _set_logger(memnew(StdLogger)); -} - -void OS_JavaScript::set_opengl_extensions(const char *p_gl_extensions) { - - ERR_FAIL_COND(!p_gl_extensions); - gl_extensions = p_gl_extensions; -} - static EM_BOOL _browser_resize_callback(int event_type, const EmscriptenUiEvent *ui_event, void *user_data) { ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_RESIZE, false); @@ -171,14 +165,15 @@ static EM_BOOL _mousebutton_callback(int event_type, const EmscriptenMouseEvent } int mask = _input->get_mouse_button_mask(); + int button_flag = 1 << (ev->get_button_index() - 1); if (ev->is_pressed()) { // since the event is consumed, focus manually if (!is_canvas_focused()) { focus_canvas(); } - mask |= ev->get_button_index(); - } else if (mask & ev->get_button_index()) { - mask &= ~ev->get_button_index(); + mask |= button_flag; + } else if (mask & button_flag) { + mask &= ~button_flag; } else { // release event, but press was outside the canvas, so ignore return false; @@ -209,7 +204,7 @@ static EM_BOOL _mousemove_callback(int event_type, const EmscriptenMouseEvent *m ev->set_position(pos); ev->set_global_position(ev->get_position()); - ev->set_relative(_input->get_mouse_position() - ev->get_position()); + ev->set_relative(ev->get_position() - _input->get_mouse_position()); _input->set_mouse_position(ev->get_position()); ev->set_speed(_input->get_last_mouse_speed()); @@ -344,7 +339,7 @@ static EM_BOOL _touchmove_callback(int event_type, const EmscriptenTouchEvent *t ev_mouse->set_position(Point2(first_touch.canvasX, first_touch.canvasY)); ev_mouse->set_global_position(ev_mouse->get_position()); - ev_mouse->set_relative(_input->get_mouse_position() - ev_mouse->get_position()); + ev_mouse->set_relative(ev_mouse->get_position() - _input->get_mouse_position()); _input->set_mouse_position(ev_mouse->get_position()); ev_mouse->set_speed(_input->get_last_mouse_speed()); @@ -427,7 +422,7 @@ void send_notification(int notif) { } } -void OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { +Error OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { print_line("Init OS"); @@ -435,44 +430,45 @@ void OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, i emscripten_webgl_init_context_attributes(&attributes); attributes.alpha = false; attributes.antialias = false; - attributes.majorVersion = 2; + ERR_FAIL_INDEX_V(p_video_driver, VIDEO_DRIVER_MAX, ERR_INVALID_PARAMETER); + switch (p_video_driver) { + case VIDEO_DRIVER_GLES3: + attributes.majorVersion = 2; + RasterizerGLES3::register_config(); + RasterizerGLES3::make_current(); + break; + case VIDEO_DRIVER_GLES2: + attributes.majorVersion = 1; + RasterizerGLES2::register_config(); + RasterizerGLES2::make_current(); + break; + } EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(NULL, &attributes); - ERR_FAIL_COND(emscripten_webgl_make_context_current(ctx) != EMSCRIPTEN_RESULT_SUCCESS); + ERR_FAIL_COND_V(emscripten_webgl_make_context_current(ctx) != EMSCRIPTEN_RESULT_SUCCESS, ERR_UNAVAILABLE); video_mode = p_desired; // can't fulfil fullscreen request due to browser security video_mode.fullscreen = false; - set_window_size(Size2(p_desired.width, p_desired.height)); + /* clang-format off */ + if (EM_ASM_INT_V({ return Module.resizeCanvasOnStart })) { + /* clang-format on */ + set_window_size(Size2(video_mode.width, video_mode.height)); + } else { + set_window_size(get_window_size()); + } - // find locale, emscripten only sets "C" char locale_ptr[16]; /* clang-format off */ - EM_ASM_({ - var locale = ""; - if (Module.locale) { - // best case: server-side script reads Accept-Language early and - // defines the locale to be read here - locale = Module.locale; - } else { - // no luck, use what the JS engine can tell us - // if this turns out not compatible enough, add tests for - // browserLanguage, systemLanguage and userLanguage - locale = navigator.languages ? navigator.languages[0] : navigator.language; - } - locale = locale.split('.')[0]; - stringToUTF8(locale, $0, 16); + EM_ASM_ARGS({ + stringToUTF8(Module.locale, $0, 16); }, locale_ptr); /* clang-format on */ setenv("LANG", locale_ptr, true); print_line("Init Audio"); - AudioDriverManager::add_driver(&audio_driver_javascript); AudioDriverManager::initialize(p_audio_driver); - RasterizerGLES3::register_config(); - RasterizerGLES3::make_current(); - print_line("Init VS"); visual_server = memnew(VisualServerRaster()); @@ -480,11 +476,6 @@ void OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, i print_line("Init Physicsserver"); - physics_server = memnew(PhysicsServerSW); - physics_server->init(); - physics_2d_server = memnew(Physics2DServerSW); - physics_2d_server->init(); - input = memnew(InputDefault); _input = input; @@ -521,12 +512,9 @@ void OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, i #undef SET_EM_CALLBACK #undef EM_CHECK -#ifdef JAVASCRIPT_EVAL_ENABLED - javascript_eval = memnew(JavaScript); - ProjectSettings::get_singleton()->add_singleton(ProjectSettings::Singleton("JavaScript", javascript_eval)); -#endif - visual_server->init(); + + return OK; } void OS_JavaScript::set_main_loop(MainLoop *p_main_loop) { @@ -583,7 +571,7 @@ void OS_JavaScript::set_css_cursor(const char *p_cursor) { /* clang-format off */ EM_ASM_({ - Module.canvas.style.cursor = Module.UTF8ToString($0); + Module.canvas.style.cursor = UTF8ToString($0); }, p_cursor); /* clang-format on */ } @@ -593,7 +581,7 @@ const char *OS_JavaScript::get_css_cursor() const { char cursor[16]; /* clang-format off */ EM_ASM_INT({ - Module.stringToUTF8(Module.canvas.style.cursor ? Module.canvas.style.cursor : 'auto', $0, 16); + stringToUTF8(Module.canvas.style.cursor ? Module.canvas.style.cursor : 'auto', $0, 16); }, cursor); /* clang-format on */ return cursor; @@ -799,6 +787,9 @@ void OS_JavaScript::set_cursor_shape(CursorShape p_shape) { set_css_cursor(godot2dom_cursor(cursor_shape)); } +void OS_JavaScript::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { +} + void OS_JavaScript::main_loop_begin() { if (main_loop) @@ -806,7 +797,7 @@ void OS_JavaScript::main_loop_begin() { /* clang-format off */ EM_ASM_ARGS({ - const send_notification = Module.cwrap('send_notification', null, ['number']); + const send_notification = cwrap('send_notification', null, ['number']); const notifs = arguments; (['mouseover', 'mouseleave', 'focus', 'blur']).forEach(function(event, i) { Module.canvas.addEventListener(event, send_notification.bind(null, notifs[i])); @@ -898,14 +889,13 @@ String OS_JavaScript::get_resource_dir() const { return "/"; //javascript has it's own filesystem for resources inside the APK } -String OS_JavaScript::get_data_dir() const { +String OS_JavaScript::get_user_data_dir() const { /* - if (get_data_dir_func) - return get_data_dir_func(); + if (get_user_data_dir_func) + return get_user_data_dir_func(); */ return "/userfs"; - //return ProjectSettings::get_singleton()->get_singleton_object("GodotOS")->call("get_data_dir"); }; String OS_JavaScript::get_executable_path() const { @@ -990,7 +980,25 @@ int OS_JavaScript::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 + if (p_feature == "HTML5" || p_feature == "web") + return true; + +#ifdef JAVASCRIPT_EVAL_ENABLED + if (p_feature == "JavaScript") + return true; +#endif + + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_get_current_context(); + // all extensions are already automatically enabled, this function allows + // checking WebGL extension support without inline JavaScript + if (p_feature == "s3tc" && emscripten_webgl_enable_extension(ctx, "WEBGL_compressed_texture_s3tc_srgb")) + return true; + if (p_feature == "etc" && emscripten_webgl_enable_extension(ctx, "WEBGL_compressed_texture_etc1")) + return true; + if (p_feature == "etc2" && emscripten_webgl_enable_extension(ctx, "WEBGL_compressed_texture_etc")) + return true; + + return false; } void OS_JavaScript::set_idbfs_available(bool p_idbfs_available) { @@ -1003,19 +1011,25 @@ bool OS_JavaScript::is_userfs_persistent() const { return idbfs_available; } -OS_JavaScript::OS_JavaScript(const char *p_execpath, GetDataDirFunc p_get_data_dir_func) { +OS_JavaScript::OS_JavaScript(const char *p_execpath, GetUserDataDirFunc p_get_user_data_dir_func) { + set_cmdline(p_execpath, get_cmdline_args()); main_loop = NULL; - gl_extensions = NULL; window_maximized = false; soft_fs_enabled = false; canvas_size_adjustment_requested = false; - get_data_dir_func = p_get_data_dir_func; + get_user_data_dir_func = p_get_user_data_dir_func; FileAccessUnix::close_notification_func = _close_notification_funcs; idbfs_available = false; time_to_save_sync = -1; + + Vector<Logger *> loggers; + loggers.push_back(memnew(StdLogger)); + _set_logger(memnew(CompositeLogger(loggers))); + + AudioDriverManager::add_driver(&audio_driver_javascript); } OS_JavaScript::~OS_JavaScript() { diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h index 1c939d3fd5..f0ba9422e8 100644 --- a/platform/javascript/os_javascript.h +++ b/platform/javascript/os_javascript.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,25 +27,22 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef OS_JAVASCRIPT_H #define OS_JAVASCRIPT_H #include "audio_driver_javascript.h" -#include "audio_server_javascript.h" #include "drivers/unix/os_unix.h" -#include "javascript_eval.h" #include "main/input_default.h" #include "os/input.h" #include "os/main_loop.h" #include "power_javascript.h" #include "servers/audio_server.h" -#include "servers/physics/physics_server_sw.h" -#include "servers/physics_2d/physics_2d_server_sw.h" #include "servers/visual/rasterizer.h" #include <emscripten/html5.h> -typedef String (*GetDataDirFunc)(); +typedef String (*GetUserDataDirFunc)(); class OS_JavaScript : public OS_Unix { @@ -54,10 +51,7 @@ class OS_JavaScript : public OS_Unix { int64_t last_sync_time; VisualServer *visual_server; - PhysicsServer *physics_server; - Physics2DServer *physics_2d_server; AudioDriverJavaScript audio_driver_javascript; - const char *gl_extensions; InputDefault *input; Vector2 windowed_size; @@ -68,14 +62,10 @@ class OS_JavaScript : public OS_Unix { CursorShape cursor_shape; MainLoop *main_loop; - GetDataDirFunc get_data_dir_func; + GetUserDataDirFunc get_user_data_dir_func; PowerJavascript *power_manager; -#ifdef JAVASCRIPT_EVAL_ENABLED - JavaScript *javascript_eval; -#endif - static void _close_notification_funcs(const String &p_file, int p_flags); void process_joypads(); @@ -88,14 +78,11 @@ public: virtual int get_video_driver_count() const; virtual const char *get_video_driver_name(int p_driver) const; - virtual VideoMode get_default_video_mode() const; - virtual int get_audio_driver_count() const; virtual const char *get_audio_driver_name(int p_driver) const; - virtual void initialize_logger(); virtual void initialize_core(); - virtual void initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver); + virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver); virtual void set_main_loop(MainLoop *p_main_loop); virtual void delete_main_loop(); @@ -140,6 +127,7 @@ public: virtual bool is_userfs_persistent() const; virtual void set_cursor_shape(CursorShape p_shape); + virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); void main_loop_begin(); bool main_loop_iterate(); @@ -150,10 +138,8 @@ public: virtual bool has_touchscreen_ui_hint() const; - void set_opengl_extensions(const char *p_gl_extensions); - virtual Error shell_open(String p_uri); - virtual String get_data_dir() const; + virtual String get_user_data_dir() const; String get_executable_path() const; virtual String get_resource_dir() const; @@ -172,7 +158,7 @@ public: void set_idbfs_available(bool p_idbfs_available); - OS_JavaScript(const char *p_execpath, GetDataDirFunc p_get_data_dir_func); + OS_JavaScript(const char *p_execpath, GetUserDataDirFunc p_get_user_data_dir_func); ~OS_JavaScript(); }; diff --git a/platform/javascript/platform_config.h b/platform/javascript/platform_config.h index b1c3f027f3..af4cf07393 100644 --- a/platform/javascript/platform_config.h +++ b/platform/javascript/platform_config.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,4 +27,5 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include <alloca.h> diff --git a/platform/javascript/power_javascript.cpp b/platform/javascript/power_javascript.cpp index 10964502d4..5241644dbc 100644 --- a/platform/javascript/power_javascript.cpp +++ b/platform/javascript/power_javascript.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -63,8 +63,10 @@ int PowerJavascript::get_power_percent_left() { } } -PowerJavascript::PowerJavascript() - : nsecs_left(-1), percent_left(-1), power_state(OS::POWERSTATE_UNKNOWN) { +PowerJavascript::PowerJavascript() : + nsecs_left(-1), + percent_left(-1), + power_state(OS::POWERSTATE_UNKNOWN) { } PowerJavascript::~PowerJavascript() { diff --git a/platform/javascript/power_javascript.h b/platform/javascript/power_javascript.h index 8454c5d728..c0c564aa60 100644 --- a/platform/javascript/power_javascript.h +++ b/platform/javascript/power_javascript.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ diff --git a/platform/javascript/pre_wasm.js b/platform/javascript/pre.js index be4383c8c9..311aa44fda 100644 --- a/platform/javascript/pre_wasm.js +++ b/platform/javascript/pre.js @@ -1,3 +1,2 @@ var Engine = { - USING_WASM: true, RuntimeEnvironment: function(Module) { diff --git a/platform/javascript/pre_asmjs.js b/platform/javascript/pre_asmjs.js deleted file mode 100644 index 3c497721b6..0000000000 --- a/platform/javascript/pre_asmjs.js +++ /dev/null @@ -1,3 +0,0 @@ -var Engine = { - USING_WASM: false, - RuntimeEnvironment: function(Module) { diff --git a/platform/osx/SCsub b/platform/osx/SCsub index be3950bc6d..4dfa46528a 100644 --- a/platform/osx/SCsub +++ b/platform/osx/SCsub @@ -4,7 +4,13 @@ import os Import('env') def make_debug(target, source, env): - os.system('dsymutil %s -o %s.dSYM' % (target[0], target[0])) + if (env["macports_clang"] != 'no'): + mpprefix = os.environ.get("MACPORTS_PREFIX", "/opt/local") + mpclangver = env["macports_clang"] + os.system(mpprefix + '/libexec/llvm-' + mpclangver + '/bin/llvm-dsymutil {0} -o {0}.dSYM'.format(target[0])) + else: + os.system('dsymutil {0} -o {0}.dSYM'.format(target[0])) + os.system('strip -u -r {0}'.format(target[0])) files = [ 'crash_handler_osx.mm', @@ -16,7 +22,8 @@ files = [ 'power_osx.cpp', ] -binary = env.Program('#bin/godot', files) -if env["debug_symbols"] == "full" or env["debug_symbols"] == "yes": - env.AddPostAction(binary, make_debug) +prog = env.add_program('#bin/godot', files) + +if (env["debug_symbols"] == "full" or env["debug_symbols"] == "yes") and env["separate_debug_symbols"]: + env.AddPostAction(prog, make_debug) diff --git a/platform/osx/crash_handler_osx.h b/platform/osx/crash_handler_osx.h index ff037e6b7a..5d93afb22d 100644 --- a/platform/osx/crash_handler_osx.h +++ b/platform/osx/crash_handler_osx.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef CRASH_HANDLER_OSX_H #define CRASH_HANDLER_OSX_H diff --git a/platform/osx/crash_handler_osx.mm b/platform/osx/crash_handler_osx.mm index 2ed88db309..99ce25adfb 100644 --- a/platform/osx/crash_handler_osx.mm +++ b/platform/osx/crash_handler_osx.mm @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,14 +27,15 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "main/main.h" #include "os_osx.h" +#include "project_settings.h" #include <string.h> #include <unistd.h> -// Note: Dump backtrace in 32bit mode is getting a bus error on the fgets by the ->execute, so enable only on 64bit -#if defined(DEBUG_ENABLED) && defined(__x86_64__) +#if defined(DEBUG_ENABLED) #define CRASH_HANDLER_ENABLED 1 #endif @@ -48,13 +49,8 @@ #include <mach-o/dyld.h> #include <mach-o/getsect.h> -#ifdef __x86_64__ static uint64_t load_address() { const struct segment_command_64 *cmd = getsegbyname("__TEXT"); -#else -static uint32_t load_address() { - const struct segment_command *cmd = getsegbyname("__TEXT"); -#endif char full_path[1024]; uint32_t size = sizeof(full_path); @@ -118,11 +114,7 @@ static void handle_crash(int sig) { args.push_back("-o"); args.push_back(_execpath); args.push_back("-arch"); -#ifdef __x86_64__ args.push_back("x86_64"); -#else - args.push_back("i386"); -#endif args.push_back("-l"); snprintf(str, 1024, "%p", load_addr); args.push_back(str); diff --git a/platform/osx/detect.py b/platform/osx/detect.py index 31032659b6..e51a8082f7 100644 --- a/platform/osx/detect.py +++ b/platform/osx/detect.py @@ -19,11 +19,12 @@ def can_build(): def get_opts(): - from SCons.Variables import EnumVariable + from SCons.Variables import BoolVariable, EnumVariable return [ ('osxcross_sdk', 'OSXCross SDK version', 'darwin14'), EnumVariable('debug_symbols', 'Add debug symbols to release version', 'yes', ('yes', 'no', 'full')), + BoolVariable('separate_debug_symbols', 'Create a separate file with the debug symbols', False), ] @@ -56,36 +57,39 @@ def configure(env): ## Architecture - is64 = sys.maxsize > 2**32 - if (env["bits"] == "default"): - env["bits"] = "64" if is64 else "32" + # Mac OS X no longer runs on 32-bit since 10.7 which is unsupported since 2014 + # As such, we only support 64-bit + env["bits"] = "64" ## Compiler configuration if "OSXCROSS_ROOT" not in os.environ: # regular native build - if (env["bits"] == "fat"): - env.Append(CCFLAGS=['-arch', 'i386', '-arch', 'x86_64']) - env.Append(LINKFLAGS=['-arch', 'i386', '-arch', 'x86_64']) - elif (env["bits"] == "32"): - env.Append(CCFLAGS=['-arch', 'i386']) - env.Append(LINKFLAGS=['-arch', 'i386']) - else: # 64-bit, default - env.Append(CCFLAGS=['-arch', 'x86_64']) - env.Append(LINKFLAGS=['-arch', 'x86_64']) + env.Append(CCFLAGS=['-arch', 'x86_64']) + env.Append(LINKFLAGS=['-arch', 'x86_64']) + if (env["macports_clang"] != 'no'): + mpprefix = os.environ.get("MACPORTS_PREFIX", "/opt/local") + mpclangver = env["macports_clang"] + env["CC"] = mpprefix + "/libexec/llvm-" + mpclangver + "/bin/clang" + env["LINK"] = mpprefix + "/libexec/llvm-" + mpclangver + "/bin/clang++" + env["CXX"] = mpprefix + "/libexec/llvm-" + mpclangver + "/bin/clang++" + env['AR'] = mpprefix + "/libexec/llvm-" + mpclangver + "/bin/llvm-ar" + env['RANLIB'] = mpprefix + "/libexec/llvm-" + mpclangver + "/bin/llvm-ranlib" + env['AS'] = mpprefix + "/libexec/llvm-" + mpclangver + "/bin/llvm-as" + env.Append(CCFLAGS=['-D__MACPORTS__']) #hack to fix libvpx MM256_BROADCASTSI128_SI256 define else: # osxcross build root = os.environ.get("OSXCROSS_ROOT", 0) - if env["bits"] == "fat": - basecmd = root + "/target/bin/x86_64-apple-" + env["osxcross_sdk"] + "-" - env.Append(CCFLAGS=['-arch', 'i386', '-arch', 'x86_64']) - env.Append(LINKFLAGS=['-arch', 'i386', '-arch', 'x86_64']) - elif env["bits"] == "32": - basecmd = root + "/target/bin/i386-apple-" + env["osxcross_sdk"] + "-" - else: # 64-bit, default - basecmd = root + "/target/bin/x86_64-apple-" + env["osxcross_sdk"] + "-" - - env['CC'] = basecmd + "cc" - env['CXX'] = basecmd + "c++" + basecmd = root + "/target/bin/x86_64-apple-" + env["osxcross_sdk"] + "-" + + ccache_path = os.environ.get("CCACHE") + if ccache_path == None: + env['CC'] = basecmd + "cc" + env['CXX'] = basecmd + "c++" + else: + # there aren't any ccache wrappers available for OS X cross-compile, + # to enable caching we need to prepend the path to the ccache binary + env['CC'] = ccache_path + ' ' + basecmd + "cc" + env['CXX'] = ccache_path + ' ' + basecmd + "c++" env['AR'] = basecmd + "ar" env['RANLIB'] = basecmd + "ranlib" env['AS'] = basecmd + "as" @@ -93,7 +97,7 @@ def configure(env): if (env["CXX"] == "clang++"): env.Append(CPPFLAGS=['-DTYPED_METHOD_BIND']) env["CC"] = "clang" - env["LD"] = "clang++" + env["LINK"] = "clang++" ## Dependencies @@ -103,7 +107,7 @@ def configure(env): ## Flags env.Append(CPPPATH=['#platform/osx']) - env.Append(CPPFLAGS=['-DOSX_ENABLED', '-DUNIX_ENABLED', '-DGLES2_ENABLED', '-DAPPLE_STYLE_KEYS', '-DCOREAUDIO_ENABLED']) + env.Append(CPPFLAGS=['-DOSX_ENABLED', '-DUNIX_ENABLED', '-DGLES_ENABLED', '-DAPPLE_STYLE_KEYS', '-DCOREAUDIO_ENABLED']) env.Append(LINKFLAGS=['-framework', 'Cocoa', '-framework', 'Carbon', '-framework', 'OpenGL', '-framework', 'AGL', '-framework', 'AudioUnit', '-framework', 'CoreAudio', '-lz', '-framework', 'IOKit', '-framework', 'ForceFeedback']) env.Append(LIBS=['pthread']) diff --git a/platform/osx/dir_access_osx.h b/platform/osx/dir_access_osx.h index 9a7773f5ee..e01ff2fe4d 100644 --- a/platform/osx/dir_access_osx.h +++ b/platform/osx/dir_access_osx.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef DIR_ACCESS_OSX_H #define DIR_ACCESS_OSX_H diff --git a/platform/osx/dir_access_osx.mm b/platform/osx/dir_access_osx.mm index 6121e6ccfb..cf66cab060 100644 --- a/platform/osx/dir_access_osx.mm +++ b/platform/osx/dir_access_osx.mm @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "dir_access_osx.h" #if defined(UNIX_ENABLED) || defined(LIBC_FILEIO_ENABLED) diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp index 8a6f1dc04c..db265812fa 100644 --- a/platform/osx/export/export.cpp +++ b/platform/osx/export/export.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -74,7 +74,7 @@ public: virtual String get_os_name() const { return "OSX"; } virtual Ref<Texture> get_logo() const { return logo; } - virtual String get_binary_extension() const { return use_dmg() ? "dmg" : "zip"; } + virtual String get_binary_extension(const Ref<EditorExportPreset> &p_preset) const { return use_dmg() ? "dmg" : "zip"; } virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0); virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const; @@ -101,15 +101,7 @@ void EditorExportPlatformOSX::get_preset_features(const Ref<EditorExportPreset> r_features->push_back("etc2"); } - int bits = p_preset->get("application/bits_mode"); - - if (bits == 0 || bits == 1) { - r_features->push_back("64"); - } - - if (bits == 0 || bits == 2) { - r_features->push_back("32"); - } + r_features->push_back("64"); } void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options) { @@ -125,7 +117,6 @@ void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options) 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"), 0)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "display/high_res"), false)); #ifdef OSX_ENABLED @@ -160,7 +151,7 @@ void EditorExportPlatformOSX::_make_icon(const Ref<Image> &p_icon, Vector<uint8_ copy->convert(Image::FORMAT_RGBA8); copy->resize(size, size); it->create_from_image(copy); - String path = EditorSettings::get_singleton()->get_settings_path() + "/tmp/icon.png"; + String path = EditorSettings::get_singleton()->get_cache_dir().plus_file("icon.png"); ResourceSaver::save(path, it); FileAccess *f = FileAccess::open(path, FileAccess::READ); @@ -288,6 +279,7 @@ Error EditorExportPlatformOSX::_create_dmg(const String &p_dmg_path, const Strin } Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { + ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); String src_pkg_name; @@ -322,11 +314,8 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p 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"); + String binary_to_use = "godot_osx_" + String(p_debug ? "debug" : "release") + ".64"; - print_line("binary: " + binary_to_use); String pkg_name; if (p_preset->get("application/name") != "") pkg_name = p_preset->get("application/name"); // app_name @@ -344,7 +333,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p if (use_dmg()) { // We're on OSX so we can export to DMG, but first we create our application bundle - tmp_app_path_name = EditorSettings::get_singleton()->get_settings_path() + "/tmp/" + pkg_name + ".app"; + tmp_app_path_name = EditorSettings::get_singleton()->get_cache_dir().plus_file(pkg_name + ".app"); print_line("Exporting to " + tmp_app_path_name); DirAccess *tmp_app_path = DirAccess::create_for_path(tmp_app_path_name); if (!tmp_app_path) { @@ -358,6 +347,11 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p } if (err == OK) { + print_line("Creating " + tmp_app_path_name + "/Contents/Frameworks"); + err = tmp_app_path->make_dir_recursive(tmp_app_path_name + "/Contents/Frameworks"); + } + + if (err == OK) { print_line("Creating " + tmp_app_path_name + "/Contents/Resources"); err = tmp_app_path->make_dir_recursive(tmp_app_path_name + "/Contents/Resources"); } @@ -389,7 +383,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p //read unzOpenCurrentFile(src_pkg_zip); - unzReadCurrentFile(src_pkg_zip, data.ptr(), data.size()); + unzReadCurrentFile(src_pkg_zip, data.ptrw(), data.size()); unzCloseCurrentFile(src_pkg_zip); //write @@ -501,10 +495,23 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p if (use_dmg()) { String pack_path = tmp_app_path_name + "/Contents/Resources/" + pkg_name + ".pck"; - err = save_pack(p_preset, pack_path); + Vector<SharedObject> shared_objects; + err = save_pack(p_preset, pack_path, &shared_objects); // see if we can code sign our new package String identity = p_preset->get("codesign/identity"); + + if (err == OK) { + DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + for (int i = 0; i < shared_objects.size(); i++) { + err = da->copy(shared_objects[i].path, tmp_app_path_name + "/Contents/Frameworks/" + shared_objects[i].path.get_file()); + if (err == OK && identity != "") { + err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Frameworks/" + shared_objects[i].path.get_file()); + } + } + memdelete(da); + } + if (err == OK && identity != "") { ep.step("Code signing bundle", 2); @@ -539,8 +546,10 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p OS::get_singleton()->move_to_trash(tmp_app_path_name); } else { - String pack_path = EditorSettings::get_singleton()->get_settings_path() + "/tmp/" + pkg_name + ".pck"; - Error err = save_pack(p_preset, pack_path); + String pack_path = EditorSettings::get_singleton()->get_cache_dir().plus_file(pkg_name + ".pck"); + + Vector<SharedObject> shared_objects; + err = save_pack(p_preset, pack_path, &shared_objects); if (err == OK) { zipOpenNewFileInZip(dst_pkg_zip, @@ -566,12 +575,35 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p break; zipWriteInFileInZip(dst_pkg_zip, buf, r); } + zipCloseFileInZip(dst_pkg_zip); memdelete(pf); } else { err = ERR_CANT_OPEN; } } + + if (err == OK) { + //add shared objects + for (int i = 0; i < shared_objects.size(); i++) { + Vector<uint8_t> file = FileAccess::get_file_as_array(shared_objects[i].path); + ERR_CONTINUE(file.empty()); + + zipOpenNewFileInZip(dst_pkg_zip, + (pkg_name + ".app/Contents/Frameworks/").plus_file(shared_objects[i].path.get_file()).utf8().get_data(), + NULL, + NULL, + 0, + NULL, + 0, + NULL, + Z_DEFLATED, + Z_DEFAULT_COMPRESSION); + + zipWriteInFileInZip(dst_pkg_zip, file.ptr(), file.size()); + zipCloseFileInZip(dst_pkg_zip); + } + } } } @@ -579,31 +611,38 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p zipClose(dst_pkg_zip, NULL); } - return OK; + return err; } bool EditorExportPlatformOSX::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { - bool valid = true; + bool valid = false; String err; - if (!exists_export_template("osx.zip", &err)) { - valid = false; + if (exists_export_template("osx.zip", &err)) { + valid = true; } - 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/debug") != "") { + if (FileAccess::exists(p_preset->get("custom_package/debug"))) { + valid = true; + } else { + 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 (p_preset->get("custom_package/release") != "") { + if (FileAccess::exists(p_preset->get("custom_package/release"))) { + valid = true; + } else { + err += "Custom release package not found.\n"; + } } if (!err.empty()) r_error = err; + r_missing_templates = !valid; return valid; } diff --git a/platform/osx/export/export.h b/platform/osx/export/export.h index bb87c8a6b7..08294fc33c 100644 --- a/platform/osx/export/export.h +++ b/platform/osx/export/export.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,4 +27,5 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + void register_osx_exporter(); diff --git a/platform/osx/godot_main_osx.mm b/platform/osx/godot_main_osx.mm index e2740fc666..6ccbaf896b 100644 --- a/platform/osx/godot_main_osx.mm +++ b/platform/osx/godot_main_osx.mm @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "main/main.h" #include "os_osx.h" @@ -81,8 +82,17 @@ int main(int argc, char **argv) { #endif OS_OSX os; + Error err; + + if (os.open_with_filename != "") { + char *argv_c = (char *)malloc(os.open_with_filename.utf8().size()); + memcpy(argv_c, os.open_with_filename.utf8().get_data(), os.open_with_filename.utf8().size()); + err = Main::setup(argv[0], 1, &argv_c); + free(argv_c); + } else { + err = Main::setup(argv[0], argc - first_arg, &argv[first_arg]); + } - Error err = Main::setup(argv[0], argc - first_arg, &argv[first_arg]); if (err != OK) return 255; diff --git a/platform/osx/joypad_osx.cpp b/platform/osx/joypad_osx.cpp index ab323ad410..20ceadca9d 100644 --- a/platform/osx/joypad_osx.cpp +++ b/platform/osx/joypad_osx.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "joypad_osx.h" #include <machine/endian.h> diff --git a/platform/osx/joypad_osx.h b/platform/osx/joypad_osx.h index 4bdef8eb83..addbefc5ad 100644 --- a/platform/osx/joypad_osx.h +++ b/platform/osx/joypad_osx.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef JOYPADOSX_H #define JOYPADOSX_H diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h index 420bb50af9..486d7af1c1 100644 --- a/platform/osx/os_osx.h +++ b/platform/osx/os_osx.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef OS_OSX_H #define OS_OSX_H @@ -38,15 +39,12 @@ #include "os/input.h" #include "power_osx.h" #include "servers/audio_server.h" -#include "servers/physics_2d/physics_2d_server_sw.h" -#include "servers/physics_2d/physics_2d_server_wrap_mt.h" -#include "servers/physics_server.h" #include "servers/visual/rasterizer.h" #include "servers/visual/visual_server_wrap_mt.h" #include "servers/visual_server.h" +#include <AppKit/NSCursor.h> #include <ApplicationServices/ApplicationServices.h> -//bitch #undef CursorShape /** @author Juan Linietsky <reduzio@gmail.com> @@ -54,6 +52,17 @@ class OS_OSX : public OS_Unix { public: + struct KeyEvent { + unsigned int osx_state; + bool pressed; + bool echo; + uint32_t scancode; + uint32_t unicode; + }; + + Vector<KeyEvent> key_event_buffer; + int key_event_pos; + bool force_quit; // rasterizer seems to no longer be given to visual server, its using GLES3 directly? //Rasterizer *rasterizer; @@ -62,9 +71,6 @@ public: List<String> args; MainLoop *main_loop; - PhysicsServer *physics_server; - Physics2DServer *physics_2d_server; - IP_Unix *ip_unix; AudioDriverCoreAudio audio_driver; @@ -77,6 +83,7 @@ public: CGEventSourceRef eventSource; void process_events(); + void process_key_events(); void *framework; // pthread_key_t current; @@ -93,6 +100,7 @@ public: id context; CursorShape cursor_shape; + NSCursor *cursors[CURSOR_MAX] = { NULL }; MouseMode mouse_mode; String title; @@ -103,6 +111,8 @@ public: Size2 window_size; Rect2 restore_rect; + String open_with_filename; + Point2 im_position; ImeCallback im_callback; void *im_target; @@ -124,13 +134,8 @@ public: void _update_window(); protected: - virtual int get_video_driver_count() const; - virtual const char *get_video_driver_name(int p_driver) const; - virtual VideoMode get_default_video_mode() const; - - virtual void initialize_logger(); virtual void initialize_core(); - virtual void initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver); + virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver); virtual void finalize(); virtual void set_main_loop(MainLoop *p_main_loop); @@ -145,7 +150,10 @@ public: virtual void alert(const String &p_alert, const String &p_title = "ALERT!"); + virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false); + virtual void set_cursor_shape(CursorShape p_shape); + virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); virtual void set_mouse_show(bool p_show); virtual void set_mouse_grab(bool p_grab); @@ -156,11 +164,17 @@ public: virtual void set_window_title(const String &p_title); virtual Size2 get_window_size() const; + virtual Size2 get_real_window_size() const; virtual void set_icon(const Ref<Image> &p_icon); virtual MainLoop *get_main_loop() const; + virtual String get_config_path() const; + virtual String get_data_path() const; + virtual String get_cache_path() const; + virtual String get_godot_dir_name() const; + virtual String get_system_dir(SystemDir p_dir) const; virtual bool can_draw() const; @@ -205,22 +219,26 @@ public: virtual bool is_window_minimized() const; virtual void set_window_maximized(bool p_enabled); virtual bool is_window_maximized() const; + virtual void set_window_always_on_top(bool p_enabled); + virtual bool is_window_always_on_top() const; virtual void request_attention(); virtual String get_joy_guid(int p_device) const; - virtual void set_borderless_window(int p_borderless); + virtual void set_borderless_window(bool p_borderless); virtual bool get_borderless_window(); virtual void set_ime_position(const Point2 &p_pos); virtual void set_ime_intermediate_text_callback(ImeCallback p_callback, void *p_inp); + virtual String get_unique_id() const; + virtual OS::PowerState get_power_state(); virtual int get_power_seconds_left(); virtual int get_power_percent_left(); virtual bool _check_internal_feature_support(const String &p_feature); - virtual void set_use_vsync(bool p_enable); - virtual bool is_vsync_enabled() const; + virtual void _set_use_vsync(bool p_enable); + //virtual bool is_vsync_enabled() const; void run(); @@ -232,7 +250,15 @@ public: virtual Error move_to_trash(const String &p_path); + void force_process_input(); + OS_OSX(); + +private: + Point2 get_native_screen_position(int p_screen) const; + Point2 get_native_window_position() const; + void set_native_window_position(const Point2 &p_position); + Point2 get_screens_origin() const; }; #endif diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index 33586086dc..a811ff585d 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,16 +27,20 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "os_osx.h" #include "dir_access_osx.h" +#include "drivers/gles2/rasterizer_gles2.h" #include "drivers/gles3/rasterizer_gles3.h" #include "main/main.h" #include "os/keyboard.h" #include "print_string.h" #include "sem_osx.h" -#include "servers/physics/physics_server_sw.h" #include "servers/visual/visual_server_raster.h" +#include "version_generated.gen.h" + +#include <mach-o/dyld.h> #include <Carbon/Carbon.h> #import <Cocoa/Cocoa.h> @@ -48,6 +52,7 @@ #include <os/log.h> #endif +#include <dlfcn.h> #include <fcntl.h> #include <libproc.h> #include <stdio.h> @@ -57,25 +62,40 @@ #include <unistd.h> #if MAC_OS_X_VERSION_MAX_ALLOWED < 101200 +#define NSEventMaskAny NSAnyEventMask +#define NSEventTypeKeyDown NSKeyDown +#define NSEventTypeKeyUp NSKeyUp +#define NSEventModifierFlagShift NSShiftKeyMask +#define NSEventModifierFlagCommand NSCommandKeyMask +#define NSEventModifierFlagControl NSControlKeyMask +#define NSEventModifierFlagOption NSAlternateKeyMask +#define NSWindowStyleMaskTitled NSTitledWindowMask +#define NSWindowStyleMaskResizable NSResizableWindowMask +#define NSWindowStyleMaskMiniaturizable NSMiniaturizableWindowMask +#define NSWindowStyleMaskClosable NSClosableWindowMask #define NSWindowStyleMaskBorderless NSBorderlessWindowMask #endif static NSRect convertRectToBacking(NSRect contentRect) { -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 - if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6) - return [OS_OSX::singleton->window_view convertRectToBacking:contentRect]; - else -#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/ - return contentRect; + return [OS_OSX::singleton->window_view convertRectToBacking:contentRect]; } static void get_key_modifier_state(unsigned int p_osx_state, Ref<InputEventWithModifiers> state) { - state->set_shift((p_osx_state & NSShiftKeyMask)); - state->set_control((p_osx_state & NSControlKeyMask)); - state->set_alt((p_osx_state & NSAlternateKeyMask)); - state->set_metakey((p_osx_state & NSCommandKeyMask)); + state->set_shift((p_osx_state & NSEventModifierFlagShift)); + state->set_control((p_osx_state & NSEventModifierFlagControl)); + state->set_alt((p_osx_state & NSEventModifierFlagOption)); + state->set_metakey((p_osx_state & NSEventModifierFlagCommand)); +} + +static void push_to_key_event_buffer(const OS_OSX::KeyEvent &p_event) { + + Vector<OS_OSX::KeyEvent> &buffer = OS_OSX::singleton->key_event_buffer; + if (OS_OSX::singleton->key_event_pos >= buffer.size()) { + buffer.resize(1 + OS_OSX::singleton->key_event_pos); + } + buffer[OS_OSX::singleton->key_event_pos++] = p_event; } static int mouse_x = 0; @@ -85,16 +105,43 @@ static int prev_mouse_y = 0; static int button_mask = 0; static bool mouse_down_control = false; +static Vector2 get_mouse_pos(NSEvent *event) { + + const NSRect contentRect = [OS_OSX::singleton->window_view frame]; + const NSPoint p = [event locationInWindow]; + mouse_x = p.x * OS_OSX::singleton->_mouse_scale([[event window] backingScaleFactor]); + mouse_y = (contentRect.size.height - p.y) * OS_OSX::singleton->_mouse_scale([[event window] backingScaleFactor]); + return Vector2(mouse_x, mouse_y); +} + @interface GodotApplication : NSApplication @end @implementation GodotApplication -// From http://cocoadev.com/index.pl?GameKeyboardHandlingAlmost -// This works around an AppKit bug, where key up events while holding -// down the command key don't get sent to the key window. - (void)sendEvent:(NSEvent *)event { - if ([event type] == NSKeyUp && ([event modifierFlags] & NSCommandKeyMask)) + + // special case handling of command-period, which is traditionally a special + // shortcut in macOS and doesn't arrive at our regular keyDown handler. + if ([event type] == NSEventTypeKeyDown) { + if (([event modifierFlags] & NSEventModifierFlagCommand) && [event keyCode] == 0x2f) { + + Ref<InputEventKey> k; + k.instance(); + + get_key_modifier_state([event modifierFlags], k); + k->set_pressed(true); + k->set_scancode(KEY_PERIOD); + k->set_echo([event isARepeat]); + + OS_OSX::singleton->push_input(k); + } + } + + // From http://cocoadev.com/index.pl?GameKeyboardHandlingAlmost + // This works around an AppKit bug, where key up events while holding + // down the command key don't get sent to the key window. + if ([event type] == NSEventTypeKeyUp && ([event modifierFlags] & NSEventModifierFlagCommand)) [[self keyWindow] sendEvent:event]; else [super sendEvent:event]; @@ -103,10 +150,51 @@ static bool mouse_down_control = false; @end @interface GodotApplicationDelegate : NSObject +- (void)forceUnbundledWindowActivationHackStep1; +- (void)forceUnbundledWindowActivationHackStep2; +- (void)forceUnbundledWindowActivationHackStep3; @end @implementation GodotApplicationDelegate +- (void)forceUnbundledWindowActivationHackStep1 { + // Step1: Switch focus to macOS Dock. + // Required to perform step 2, TransformProcessType will fail if app is already the in focus. + for (NSRunningApplication *app in [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.dock"]) { + [app activateWithOptions:NSApplicationActivateIgnoringOtherApps]; + break; + } + [self performSelector:@selector(forceUnbundledWindowActivationHackStep2) withObject:nil afterDelay:0.02]; +} + +- (void)forceUnbundledWindowActivationHackStep2 { + // Step 2: Register app as foreground process. + ProcessSerialNumber psn = { 0, kCurrentProcess }; + (void)TransformProcessType(&psn, kProcessTransformToForegroundApplication); + + [self performSelector:@selector(forceUnbundledWindowActivationHackStep3) withObject:nil afterDelay:0.02]; +} + +- (void)forceUnbundledWindowActivationHackStep3 { + // Step 3: Switch focus back to app window. + [[NSRunningApplication currentApplication] activateWithOptions:NSApplicationActivateIgnoringOtherApps]; +} + +- (void)applicationDidFinishLaunching:(NSNotification *)notice { + NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"]; + if (nsappname == nil) { + // If executable is not a bundled, macOS WindowServer won't register and activate app window correctly (menu and title bar are grayed out and input ignored). + [self performSelector:@selector(forceUnbundledWindowActivationHackStep1) withObject:nil afterDelay:0.02]; + } +} + +- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename { + // Note: called before main loop init! + char *utfs = strdup([filename UTF8String]); + OS_OSX::singleton->open_with_filename.parse_utf8(utfs); + return YES; +} + - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender { if (OS_OSX::singleton->get_main_loop()) OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST); @@ -160,7 +248,6 @@ static bool mouse_down_control = false; return NO; } -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 - (void)windowDidEnterFullScreen:(NSNotification *)notification { OS_OSX::singleton->zoomed = true; } @@ -168,7 +255,6 @@ static bool mouse_down_control = false; - (void)windowDidExitFullScreen:(NSNotification *)notification { OS_OSX::singleton->zoomed = false; } -#endif // MAC_OS_X_VERSION_MAX_ALLOWED - (void)windowDidChangeBackingProperties:(NSNotification *)notification { if (!OS_OSX::singleton) @@ -362,8 +448,8 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; - (void)cancelComposition { [self unmarkText]; - NSInputManager *currentInputManager = [NSInputManager currentInputManager]; - [currentInputManager markedTextAbandoned:self]; + NSTextInputContext *currentInputContext = [NSTextInputContext currentInputContext]; + [currentInputContext discardMarkedText]; } - (void)insertText:(id)aString { @@ -372,13 +458,6 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; - (void)insertText:(id)aString replacementRange:(NSRange)replacementRange { NSEvent *event = [NSApp currentEvent]; - Ref<InputEventKey> k; - k.instance(); - - get_key_modifier_state([event modifierFlags], k); - k->set_pressed(true); - k->set_echo(false); - k->set_scancode(0); NSString *characters; if ([aString isKindOfClass:[NSAttributedString class]]) { @@ -392,8 +471,8 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; NSCharacterSet *ctrlChars = [NSCharacterSet controlCharacterSet]; NSCharacterSet *wsnlChars = [NSCharacterSet whitespaceAndNewlineCharacterSet]; if ([characters rangeOfCharacterFromSet:ctrlChars].length && [characters rangeOfCharacterFromSet:wsnlChars].length == 0) { - NSInputManager *currentInputManager = [NSInputManager currentInputManager]; - [currentInputManager markedTextAbandoned:self]; + NSTextInputContext *currentInputContext = [NSTextInputContext currentInputContext]; + [currentInputContext discardMarkedText]; [self cancelComposition]; return; } @@ -403,8 +482,15 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; if ((codepoint & 0xFF00) == 0xF700) continue; - k->set_unicode(codepoint); - OS_OSX::singleton->push_input(k); + OS_OSX::KeyEvent ke; + + ke.osx_state = [event modifierFlags]; + ke.pressed = true; + ke.echo = false; + ke.scancode = 0; + ke.unicode = codepoint; + + push_to_key_event_buffer(ke); } [self cancelComposition]; } @@ -479,7 +565,7 @@ static void _mouseDownEvent(NSEvent *event, int index, int mask, bool pressed) { } - (void)mouseDown:(NSEvent *)event { - if (([event modifierFlags] & NSControlKeyMask)) { + if (([event modifierFlags] & NSEventModifierFlagControl)) { mouse_down_control = true; _mouseDownEvent(event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, true); } else { @@ -508,12 +594,9 @@ static void _mouseDownEvent(NSEvent *event, int index, int mask, bool pressed) { mm->set_button_mask(button_mask); prev_mouse_x = mouse_x; prev_mouse_y = mouse_y; - const NSRect contentRect = [OS_OSX::singleton->window_view frame]; - const NSPoint p = [event locationInWindow]; - mouse_x = p.x * OS_OSX::singleton->_mouse_scale([[event window] backingScaleFactor]); - mouse_y = (contentRect.size.height - p.y) * OS_OSX::singleton->_mouse_scale([[event window] backingScaleFactor]); - mm->set_position(Vector2(mouse_x, mouse_y)); - mm->set_global_position(Vector2(mouse_x, mouse_y)); + const Vector2 pos = get_mouse_pos(event); + mm->set_position(pos); + mm->set_global_position(pos); Vector2 relativeMotion = Vector2(); relativeMotion.x = [event deltaX] * OS_OSX::singleton->_mouse_scale([[event window] backingScaleFactor]); relativeMotion.y = [event deltaY] * OS_OSX::singleton->_mouse_scale([[event window] backingScaleFactor]); @@ -571,8 +654,20 @@ static void _mouseDownEvent(NSEvent *event, int index, int mask, bool pressed) { return; if (OS_OSX::singleton->main_loop && OS_OSX::singleton->mouse_mode != OS::MOUSE_MODE_CAPTURED) OS_OSX::singleton->main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_ENTER); - if (OS_OSX::singleton->input) + if (OS_OSX::singleton->input) { OS_OSX::singleton->input->set_mouse_in_window(true); + OS_OSX::singleton->cursor_shape = OS::CURSOR_MAX; + OS_OSX::singleton->set_cursor_shape(OS::CURSOR_ARROW); + } +} + +- (void)magnifyWithEvent:(NSEvent *)event { + Ref<InputEventMagnifyGesture> ev; + ev.instance(); + get_key_modifier_state([event modifierFlags], ev); + ev->set_position(get_mouse_pos(event)); + ev->set_factor([event magnification] + 1.0); + OS_OSX::singleton->push_input(ev); } - (void)viewDidChangeBackingProperties { @@ -744,80 +839,87 @@ static int translateKey(unsigned int key) { - (void)keyDown:(NSEvent *)event { - Ref<InputEventKey> k; - k.instance(); - - get_key_modifier_state([event modifierFlags], k); - k->set_pressed(true); - k->set_scancode(latin_keyboard_keycode_convert(translateKey([event keyCode]))); - k->set_echo([event isARepeat]); + //disable raw input in IME mode + if (!imeMode) { + OS_OSX::KeyEvent ke; - NSString *characters = [event characters]; - NSUInteger i, length = [characters length]; + ke.osx_state = [event modifierFlags]; + ke.pressed = true; + ke.echo = [event isARepeat]; + ke.scancode = latin_keyboard_keycode_convert(translateKey([event keyCode])); + ke.unicode = 0; - //disable raw input in IME mode - if (!imeMode) - OS_OSX::singleton->push_input(k); + push_to_key_event_buffer(ke); + } if ((OS_OSX::singleton->im_position.x != 0) && (OS_OSX::singleton->im_position.y != 0)) [self interpretKeyEvents:[NSArray arrayWithObject:event]]; } - (void)flagsChanged:(NSEvent *)event { - Ref<InputEventKey> k; - k.instance(); - int key = [event keyCode]; - int mod = [event modifierFlags]; - - if (key == 0x36 || key == 0x37) { - if (mod & NSCommandKeyMask) { - mod &= ~NSCommandKeyMask; - k->set_pressed(true); - } else { - k->set_pressed(false); - } - } else if (key == 0x38 || key == 0x3c) { - if (mod & NSShiftKeyMask) { - mod &= ~NSShiftKeyMask; - k->set_pressed(true); + if (!imeMode) { + OS_OSX::KeyEvent ke; + + ke.echo = false; + + int key = [event keyCode]; + int mod = [event modifierFlags]; + + if (key == 0x36 || key == 0x37) { + if (mod & NSEventModifierFlagCommand) { + mod &= ~NSEventModifierFlagCommand; + ke.pressed = true; + } else { + ke.pressed = false; + } + } else if (key == 0x38 || key == 0x3c) { + if (mod & NSEventModifierFlagShift) { + mod &= ~NSEventModifierFlagShift; + ke.pressed = true; + } else { + ke.pressed = false; + } + } else if (key == 0x3a || key == 0x3d) { + if (mod & NSEventModifierFlagOption) { + mod &= ~NSEventModifierFlagOption; + ke.pressed = true; + } else { + ke.pressed = false; + } + } else if (key == 0x3b || key == 0x3e) { + if (mod & NSEventModifierFlagControl) { + mod &= ~NSEventModifierFlagControl; + ke.pressed = true; + } else { + ke.pressed = false; + } } else { - k->set_pressed(false); - } - } else if (key == 0x3a || key == 0x3d) { - if (mod & NSAlternateKeyMask) { - mod &= ~NSAlternateKeyMask; - k->set_pressed(true); - } else { - k->set_pressed(false); - } - } else if (key == 0x3b || key == 0x3e) { - if (mod & NSControlKeyMask) { - mod &= ~NSControlKeyMask; - k->set_pressed(true); - } else { - k->set_pressed(false); + return; } - } else { - return; - } - get_key_modifier_state(mod, k); - k->set_scancode(latin_keyboard_keycode_convert(translateKey(key))); + ke.osx_state = mod; + ke.scancode = latin_keyboard_keycode_convert(translateKey(key)); + ke.unicode = 0; - OS_OSX::singleton->push_input(k); + push_to_key_event_buffer(ke); + } } - (void)keyUp:(NSEvent *)event { - Ref<InputEventKey> k; - k.instance(); + if (!imeMode) { - get_key_modifier_state([event modifierFlags], k); - k->set_pressed(false); - k->set_scancode(latin_keyboard_keycode_convert(translateKey([event keyCode]))); + OS_OSX::KeyEvent ke; - OS_OSX::singleton->push_input(k); + ke.osx_state = [event modifierFlags]; + ke.pressed = false; + ke.echo = false; + ke.scancode = latin_keyboard_keycode_convert(translateKey([event keyCode])); + ke.unicode = 0; + + push_to_key_event_buffer(ke); + } } inline void sendScrollEvent(int button, double factor, int modifierFlags) { @@ -838,29 +940,38 @@ inline void sendScrollEvent(int button, double factor, int modifierFlags) { OS_OSX::singleton->push_input(sc); } +inline void sendPanEvent(double dx, double dy, int modifierFlags) { + + Ref<InputEventPanGesture> pg; + pg.instance(); + + get_key_modifier_state(modifierFlags, pg); + Vector2 mouse_pos = Vector2(mouse_x, mouse_y); + pg->set_position(mouse_pos); + pg->set_delta(Vector2(-dx, -dy)); + OS_OSX::singleton->push_input(pg); +} + - (void)scrollWheel:(NSEvent *)event { double deltaX, deltaY; -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 - if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6) { - deltaX = [event scrollingDeltaX]; - deltaY = [event scrollingDeltaY]; + deltaX = [event scrollingDeltaX]; + deltaY = [event scrollingDeltaY]; - if ([event hasPreciseScrollingDeltas]) { - deltaX *= 0.03; - deltaY *= 0.03; - } - } else -#endif // MAC_OS_X_VERSION_MAX_ALLOWED - { - deltaX = [event deltaX]; - deltaY = [event deltaY]; - } - if (fabs(deltaX)) { - sendScrollEvent(0 > deltaX ? BUTTON_WHEEL_RIGHT : BUTTON_WHEEL_LEFT, fabs(deltaX * 0.3), [event modifierFlags]); + if ([event hasPreciseScrollingDeltas]) { + deltaX *= 0.03; + deltaY *= 0.03; } - if (fabs(deltaY)) { - sendScrollEvent(0 < deltaY ? BUTTON_WHEEL_UP : BUTTON_WHEEL_DOWN, fabs(deltaY * 0.3), [event modifierFlags]); + + if ([event phase] != NSEventPhaseNone || [event momentumPhase] != NSEventPhaseNone) { + sendPanEvent(deltaX, deltaY, [event modifierFlags]); + } else { + if (fabs(deltaX)) { + sendScrollEvent(0 > deltaX ? BUTTON_WHEEL_RIGHT : BUTTON_WHEEL_LEFT, fabs(deltaX * 0.3), [event modifierFlags]); + } + if (fabs(deltaY)) { + sendScrollEvent(0 < deltaY ? BUTTON_WHEEL_UP : BUTTON_WHEEL_DOWN, fabs(deltaY * 0.3), [event modifierFlags]); + } } } @@ -887,27 +998,32 @@ void OS_OSX::set_ime_intermediate_text_callback(ImeCallback p_callback, void *p_ } } -void OS_OSX::set_ime_position(const Point2 &p_pos) { - im_position = p_pos; -} +String OS_OSX::get_unique_id() const { -int OS_OSX::get_video_driver_count() const { - return 1; -} + static String serial_number; + + if (serial_number.empty()) { + io_service_t platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice")); + CFStringRef serialNumberAsCFString = NULL; + if (platformExpert) { + serialNumberAsCFString = (CFStringRef)IORegistryEntryCreateCFProperty(platformExpert, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0); + IOObjectRelease(platformExpert); + } -const char *OS_OSX::get_video_driver_name(int p_driver) const { + NSString *serialNumberAsNSString = nil; + if (serialNumberAsCFString) { + serialNumberAsNSString = [NSString stringWithString:(NSString *)serialNumberAsCFString]; + CFRelease(serialNumberAsCFString); + } - return "GLES2"; -} + serial_number = [serialNumberAsNSString UTF8String]; + } -OS::VideoMode OS_OSX::get_default_video_mode() const { + return serial_number; +} - VideoMode vm; - vm.width = 1024; - vm.height = 600; - vm.fullscreen = false; - vm.resizable = true; - return vm; +void OS_OSX::set_ime_position(const Point2 &p_pos) { + im_position = p_pos; } void OS_OSX::initialize_core() { @@ -924,24 +1040,33 @@ void OS_OSX::initialize_core() { } static bool keyboard_layout_dirty = true; -static void keyboardLayoutChanged(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) { +static void keyboard_layout_changed(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef user_info) { keyboard_layout_dirty = true; } -void OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { +static bool displays_arrangement_dirty = true; +static void displays_arrangement_changed(CGDirectDisplayID display_id, CGDisplayChangeSummaryFlags flags, void *user_info) { + displays_arrangement_dirty = true; +} + +Error OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { /*** OSX INITIALIZATION ***/ /*** OSX INITIALIZATION ***/ /*** OSX INITIALIZATION ***/ keyboard_layout_dirty = true; + displays_arrangement_dirty = true; // Register to be notified on keyboard layout changes CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(), - NULL, keyboardLayoutChanged, + NULL, keyboard_layout_changed, kTISNotifySelectedKeyboardInputSourceChanged, NULL, CFNotificationSuspensionBehaviorDeliverImmediately); + // Register to be notified on displays arrangement changes + CGDisplayRegisterReconfigurationCallback(displays_arrangement_changed, NULL); + window_delegate = [[GodotWindowDelegate alloc] init]; // Don't use accumulation buffer support; it's not accelerated @@ -952,7 +1077,7 @@ void OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_au if (p_desired.borderless_window) { styleMask = NSWindowStyleMaskBorderless; } else { - styleMask = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | (p_desired.resizable ? NSResizableWindowMask : 0); + styleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | (p_desired.resizable ? NSWindowStyleMaskResizable : 0); } window_object = [[GodotWindow alloc] @@ -961,7 +1086,7 @@ void OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_au backing:NSBackingStoreBuffered defer:NO]; - ERR_FAIL_COND(window_object == nil); + ERR_FAIL_COND_V(window_object == nil, ERR_UNAVAILABLE); window_view = [[GodotContentView alloc] init]; @@ -977,7 +1102,7 @@ void OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_au window_size.width = p_desired.width * displayScale; window_size.height = p_desired.height * displayScale; - if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6 && displayScale > 1.0) { + if (displayScale > 1.0) { [window_view setWantsBestResolutionOpenGLSurface:YES]; //if (current_videomode.resizable) [window_object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; @@ -989,15 +1114,14 @@ void OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_au [window_object setAcceptsMouseMovedEvents:YES]; [window_object center]; - if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6) - [window_object setRestorable:NO]; + [window_object setRestorable:NO]; unsigned int attributeCount = 0; - // OS X needs non-zero color size, so set resonable values + // OS X needs non-zero color size, so set reasonable values int colorBits = 32; -// Fail if a robustness strategy was requested + // Fail if a robustness strategy was requested #define ADD_ATTR(x) \ { attributes[attributeCount++] = x; } @@ -1013,8 +1137,12 @@ void OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_au ADD_ATTR(NSOpenGLPFADoubleBuffer); ADD_ATTR(NSOpenGLPFAClosestPolicy); - //we now need OpenGL 3 or better, maybe even change this to 3_3Core ? - ADD_ATTR2(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core); + if (p_video_driver == VIDEO_DRIVER_GLES2) { + ADD_ATTR2(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersionLegacy); + } else { + //we now need OpenGL 3 or better, maybe even change this to 3_3Core ? + ADD_ATTR2(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core); + } ADD_ATTR2(NSOpenGLPFAColorSize, colorBits); @@ -1040,7 +1168,7 @@ void OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_au */ // NOTE: All NSOpenGLPixelFormats on the relevant cards support sRGB - // frambuffer, so there's no need (and no way) to request it + // framebuffer, so there's no need (and no way) to request it ADD_ATTR(0); @@ -1048,16 +1176,18 @@ void OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_au #undef ADD_ATTR2 pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]; - ERR_FAIL_COND(pixelFormat == nil); + ERR_FAIL_COND_V(pixelFormat == nil, ERR_UNAVAILABLE); context = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil]; - ERR_FAIL_COND(context == nil); + ERR_FAIL_COND_V(context == nil, ERR_UNAVAILABLE); [context setView:window_view]; [context makeCurrentContext]; + set_use_vsync(p_desired.use_vsync); + [NSApp activateIgnoringOtherApps:YES]; _update_window(); @@ -1068,19 +1198,15 @@ void OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_au zoomed = true; /*** END OSX INITIALIZATION ***/ - /*** END OSX INITIALIZATION ***/ - /*** END OSX INITIALIZATION ***/ - - bool use_gl2 = p_video_driver != 1; - - AudioDriverManager::add_driver(&audio_driver); // only opengl support here... - RasterizerGLES3::register_config(); - RasterizerGLES3::make_current(); - - //rasterizer = instance_RasterizerGLES2(); - //visual_server = memnew( VisualServerRaster(rasterizer) ); + if (p_video_driver == VIDEO_DRIVER_GLES2) { + RasterizerGLES2::register_config(); + RasterizerGLES2::make_current(); + } else { + RasterizerGLES3::register_config(); + RasterizerGLES3::make_current(); + } visual_server = memnew(VisualServerRaster); if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) { @@ -1088,30 +1214,26 @@ void OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_au visual_server = memnew(VisualServerWrapMT(visual_server, get_render_thread_mode() == RENDER_SEPARATE_THREAD)); } visual_server->init(); - // visual_server->cursor_set_visible(false, 0); AudioDriverManager::initialize(p_audio_driver); - // - physics_server = memnew(PhysicsServerSW); - physics_server->init(); - //physics_2d_server = memnew( Physics2DServerSW ); - physics_2d_server = Physics2DServerWrapMT::init_server<Physics2DServerSW>(); - physics_2d_server->init(); - input = memnew(InputDefault); joypad_osx = memnew(JoypadOSX); power_manager = memnew(power_osx); - _ensure_data_dir(); + _ensure_user_data_dir(); restore_rect = Rect2(get_window_position(), get_window_size()); + + return OK; } void OS_OSX::finalize() { CFNotificationCenterRemoveObserver(CFNotificationCenterGetDistributedCenter(), NULL, kTISNotifySelectedKeyboardInputSourceChanged, NULL); + CGDisplayRemoveReconfigurationCallback(displays_arrangement_changed, NULL); + delete_main_loop(); memdelete(joypad_osx); @@ -1120,12 +1242,6 @@ void OS_OSX::finalize() { visual_server->finish(); memdelete(visual_server); //memdelete(rasterizer); - - physics_server->finish(); - memdelete(physics_server); - - physics_2d_server->finish(); - memdelete(physics_2d_server); } void OS_OSX::set_main_loop(MainLoop *p_main_loop) { @@ -1163,34 +1279,42 @@ public: switch (p_type) { case ERR_WARNING: - os_log_info(OS_LOG_DEFAULT, - "WARNING: %{public}s: %{public}s\nAt: %{public}s:%i.", - p_function, err_details, p_file, p_line); + if (floor(NSAppKitVersionNumber) >= NSAppKitVersionNumber10_12) { + os_log_info(OS_LOG_DEFAULT, + "WARNING: %{public}s: %{public}s\nAt: %{public}s:%i.", + p_function, err_details, p_file, p_line); + } logf_error("\E[1;33mWARNING: %s: \E[0m\E[1m%s\n", p_function, err_details); logf_error("\E[0;33m At: %s:%i.\E[0m\n", p_file, p_line); break; case ERR_SCRIPT: - os_log_error(OS_LOG_DEFAULT, - "SCRIPT ERROR: %{public}s: %{public}s\nAt: %{public}s:%i.", - p_function, err_details, p_file, p_line); + if (floor(NSAppKitVersionNumber) >= NSAppKitVersionNumber10_12) { + os_log_error(OS_LOG_DEFAULT, + "SCRIPT ERROR: %{public}s: %{public}s\nAt: %{public}s:%i.", + p_function, err_details, p_file, p_line); + } logf_error("\E[1;35mSCRIPT ERROR: %s: \E[0m\E[1m%s\n", p_function, err_details); logf_error("\E[0;35m At: %s:%i.\E[0m\n", p_file, p_line); break; case ERR_SHADER: - os_log_error(OS_LOG_DEFAULT, - "SHADER ERROR: %{public}s: %{public}s\nAt: %{public}s:%i.", - p_function, err_details, p_file, p_line); + if (floor(NSAppKitVersionNumber) >= NSAppKitVersionNumber10_12) { + os_log_error(OS_LOG_DEFAULT, + "SHADER ERROR: %{public}s: %{public}s\nAt: %{public}s:%i.", + p_function, err_details, p_file, p_line); + } logf_error("\E[1;36mSHADER ERROR: %s: \E[0m\E[1m%s\n", p_function, err_details); logf_error("\E[0;36m At: %s:%i.\E[0m\n", p_file, p_line); break; case ERR_ERROR: default: - os_log_error(OS_LOG_DEFAULT, - "ERROR: %{public}s: %{public}s\nAt: %{public}s:%i.", - p_function, err_details, p_file, p_line); + if (floor(NSAppKitVersionNumber) >= NSAppKitVersionNumber10_12) { + os_log_error(OS_LOG_DEFAULT, + "ERROR: %{public}s: %{public}s\nAt: %{public}s:%i.", + p_function, err_details, p_file, p_line); + } logf_error("\E[1;31mERROR: %s: \E[0m\E[1m%s\n", p_function, err_details); logf_error("\E[0;31m At: %s:%i.\E[0m\n", p_file, p_line); break; @@ -1203,15 +1327,6 @@ public: typedef UnixTerminalLogger OSXTerminalLogger; #endif -void OS_OSX::initialize_logger() { - Vector<Logger *> loggers; - loggers.push_back(memnew(OSXTerminalLogger)); - // FIXME: Reenable once we figure out how to get this properly in user:// - // instead of littering the user's working dirs (res:// + pwd) with log files (GH-12277) - //loggers.push_back(memnew(RotatedFileLogger("user://logs/log.txt"))); - _set_logger(memnew(CompositeLogger(loggers))); -} - void OS_OSX::alert(const String &p_alert, const String &p_title) { // Set OS X-compliant variables NSAlert *window = [[NSAlert alloc] init]; @@ -1228,35 +1343,111 @@ void OS_OSX::alert(const String &p_alert, const String &p_title) { [window release]; } +Error OS_OSX::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) { + + String path = p_path; + + if (!FileAccess::exists(path)) { + //this code exists so gdnative can load .dylib files from within the executable path + path = get_executable_path().get_base_dir().plus_file(p_path.get_file()); + } + + if (!FileAccess::exists(path)) { + //this code exists so gdnative can load .dylib files from a standard macOS location + path = get_executable_path().get_base_dir().plus_file("../Frameworks").plus_file(p_path.get_file()); + } + + p_library_handle = dlopen(path.utf8().get_data(), RTLD_NOW); + if (!p_library_handle) { + ERR_EXPLAIN("Can't open dynamic library: " + p_path + ". Error: " + dlerror()); + ERR_FAIL_V(ERR_CANT_OPEN); + } + return OK; +} + void OS_OSX::set_cursor_shape(CursorShape p_shape) { if (cursor_shape == p_shape) return; - switch (p_shape) { - case CURSOR_ARROW: [[NSCursor arrowCursor] set]; break; - case CURSOR_IBEAM: [[NSCursor IBeamCursor] set]; break; - case CURSOR_POINTING_HAND: [[NSCursor pointingHandCursor] set]; break; - case CURSOR_CROSS: [[NSCursor crosshairCursor] set]; break; - case CURSOR_WAIT: [[NSCursor arrowCursor] set]; break; - case CURSOR_BUSY: [[NSCursor arrowCursor] set]; break; - case CURSOR_DRAG: [[NSCursor closedHandCursor] set]; break; - case CURSOR_CAN_DROP: [[NSCursor openHandCursor] set]; break; - case CURSOR_FORBIDDEN: [[NSCursor arrowCursor] set]; break; - case CURSOR_VSIZE: [[NSCursor resizeUpDownCursor] set]; break; - case CURSOR_HSIZE: [[NSCursor resizeLeftRightCursor] set]; break; - case CURSOR_BDIAGSIZE: [[NSCursor arrowCursor] set]; break; - case CURSOR_FDIAGSIZE: [[NSCursor arrowCursor] set]; break; - case CURSOR_MOVE: [[NSCursor arrowCursor] set]; break; - case CURSOR_VSPLIT: [[NSCursor resizeUpDownCursor] set]; break; - case CURSOR_HSPLIT: [[NSCursor resizeLeftRightCursor] set]; break; - case CURSOR_HELP: [[NSCursor arrowCursor] set]; break; - default: {}; + if (cursors[p_shape] != NULL) { + [cursors[p_shape] set]; + } else { + switch (p_shape) { + case CURSOR_ARROW: [[NSCursor arrowCursor] set]; break; + case CURSOR_IBEAM: [[NSCursor IBeamCursor] set]; break; + case CURSOR_POINTING_HAND: [[NSCursor pointingHandCursor] set]; break; + case CURSOR_CROSS: [[NSCursor crosshairCursor] set]; break; + case CURSOR_WAIT: [[NSCursor arrowCursor] set]; break; + case CURSOR_BUSY: [[NSCursor arrowCursor] set]; break; + case CURSOR_DRAG: [[NSCursor closedHandCursor] set]; break; + case CURSOR_CAN_DROP: [[NSCursor openHandCursor] set]; break; + case CURSOR_FORBIDDEN: [[NSCursor arrowCursor] set]; break; + case CURSOR_VSIZE: [[NSCursor resizeUpDownCursor] set]; break; + case CURSOR_HSIZE: [[NSCursor resizeLeftRightCursor] set]; break; + case CURSOR_BDIAGSIZE: [[NSCursor arrowCursor] set]; break; + case CURSOR_FDIAGSIZE: [[NSCursor arrowCursor] set]; break; + case CURSOR_MOVE: [[NSCursor arrowCursor] set]; break; + case CURSOR_VSPLIT: [[NSCursor resizeUpDownCursor] set]; break; + case CURSOR_HSPLIT: [[NSCursor resizeLeftRightCursor] set]; break; + case CURSOR_HELP: [[NSCursor arrowCursor] set]; break; + default: {}; + } } cursor_shape = p_shape; } +void OS_OSX::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { + if (p_cursor.is_valid()) { + Ref<Texture> texture = p_cursor; + Ref<Image> image = texture->get_data(); + + int image_size = 32 * 32; + + ERR_FAIL_COND(texture->get_width() != 32 || texture->get_height() != 32); + + NSBitmapImageRep *imgrep = [[[NSBitmapImageRep alloc] + initWithBitmapDataPlanes:NULL + pixelsWide:image->get_width() + pixelsHigh:image->get_height() + bitsPerSample:8 + samplesPerPixel:4 + hasAlpha:YES + isPlanar:NO + colorSpaceName:NSDeviceRGBColorSpace + bytesPerRow:image->get_width() * 4 + bitsPerPixel:32] autorelease]; + + ERR_FAIL_COND(imgrep == nil); + uint8_t *pixels = [imgrep bitmapData]; + + int len = image->get_width() * image->get_height(); + PoolVector<uint8_t> data = image->get_data(); + PoolVector<uint8_t>::Read r = data.read(); + + /* Premultiply the alpha channel */ + for (int i = 0; i < len; i++) { + uint8_t alpha = r[i * 4 + 3]; + pixels[i * 4 + 0] = (uint8_t)(((uint16_t)r[i * 4 + 0] * alpha) / 255); + pixels[i * 4 + 1] = (uint8_t)(((uint16_t)r[i * 4 + 1] * alpha) / 255); + pixels[i * 4 + 2] = (uint8_t)(((uint16_t)r[i * 4 + 2] * alpha) / 255); + pixels[i * 4 + 3] = alpha; + } + + NSImage *nsimage = [[[NSImage alloc] initWithSize:NSMakeSize(image->get_width(), image->get_height())] autorelease]; + [nsimage addRepresentation:imgrep]; + + NSCursor *cursor = [[NSCursor alloc] initWithImage:nsimage hotSpot:NSMakePoint(p_hotspot.x, p_hotspot.y)]; + + cursors[p_shape] = cursor; + + if (p_shape == CURSOR_ARROW) { + [cursor set]; + } + } +} + void OS_OSX::set_mouse_show(bool p_show) { } @@ -1353,6 +1544,43 @@ MainLoop *OS_OSX::get_main_loop() const { return main_loop; } +String OS_OSX::get_config_path() const { + + if (has_environment("XDG_CONFIG_HOME")) { + return get_environment("XDG_CONFIG_HOME"); + } else if (has_environment("HOME")) { + return get_environment("HOME").plus_file("Library/Application Support"); + } else { + return "."; + } +} + +String OS_OSX::get_data_path() const { + + if (has_environment("XDG_DATA_HOME")) { + return get_environment("XDG_DATA_HOME"); + } else { + return get_config_path(); + } +} + +String OS_OSX::get_cache_path() const { + + if (has_environment("XDG_CACHE_HOME")) { + return get_environment("XDG_CACHE_HOME"); + } else if (has_environment("HOME")) { + return get_environment("HOME").plus_file("Library/Caches"); + } else { + return get_config_path(); + } +} + +// Get properly capitalized engine name for system paths +String OS_OSX::get_godot_dir_name() const { + + return String(VERSION_SHORT_NAME).capitalize(); +} + String OS_OSX::get_system_dir(SystemDir p_dir) const { NSSearchPathDirectory id = 0; @@ -1441,7 +1669,7 @@ void OS_OSX::make_rendering_thread() { Error OS_OSX::shell_open(String p_uri) { - [[NSWorkspace sharedWorkspace] openURL:[[NSURL alloc] initWithString:[NSString stringWithUTF8String:p_uri.utf8().get_data()]]]; + [[NSWorkspace sharedWorkspace] openURL:[[NSURL alloc] initWithString:[[NSString stringWithUTF8String:p_uri.utf8().get_data()] stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]]]]; return OK; } @@ -1480,6 +1708,32 @@ int OS_OSX::get_screen_count() const { return [screenArray count]; }; +// Returns the native top-left screen coordinate of the smallest rectangle +// that encompasses all screens. Needed in get_screen_position(), +// get_window_position, and set_window_position() +// to convert between OS X native screen coordinates and the ones expected by Godot +Point2 OS_OSX::get_screens_origin() const { + static Point2 origin; + + if (displays_arrangement_dirty) { + origin = Point2(); + + for (int i = 0; i < get_screen_count(); i++) { + Point2 position = get_native_screen_position(i); + if (position.x < origin.x) { + origin.x = position.x; + } + if (position.y > origin.y) { + origin.y = position.y; + } + } + + displays_arrangement_dirty = false; + } + + return origin; +} + static int get_screen_index(NSScreen *screen) { const NSUInteger index = [[NSScreen screens] indexOfObject:screen]; return index == NSNotFound ? 0 : index; @@ -1498,21 +1752,30 @@ void OS_OSX::set_current_screen(int p_screen) { set_window_position(wpos + get_screen_position(p_screen)); }; -Point2 OS_OSX::get_screen_position(int p_screen) const { +Point2 OS_OSX::get_native_screen_position(int p_screen) const { if (p_screen == -1) { p_screen = get_current_screen(); } NSArray *screenArray = [NSScreen screens]; if (p_screen < [screenArray count]) { - float displayScale = _display_scale([screenArray objectAtIndex:p_screen]); + float display_scale = _display_scale([screenArray objectAtIndex:p_screen]); NSRect nsrect = [[screenArray objectAtIndex:p_screen] frame]; - return Point2(nsrect.origin.x, nsrect.origin.y) * displayScale; + // Return the top-left corner of the screen, for OS X the y starts at the bottom + return Point2(nsrect.origin.x, nsrect.origin.y + nsrect.size.height) * display_scale; } return Point2(); } +Point2 OS_OSX::get_screen_position(int p_screen) const { + Point2 position = get_native_screen_position(p_screen) - get_screens_origin(); + // OS X native y-coordinate relative to get_screens_origin() is negative, + // Godot expects a positive value + position.y *= -1; + return position; +} + int OS_OSX::get_screen_dpi(int p_screen) const { if (p_screen == -1) { p_screen = get_current_screen(); @@ -1586,38 +1849,63 @@ float OS_OSX::_display_scale(id screen) const { if ([screen respondsToSelector:@selector(backingScaleFactor)]) { return fmax(1.0, [screen backingScaleFactor]); } - } else { - return 1.0; } + return 1.0; } -Point2 OS_OSX::get_window_position() const { +Point2 OS_OSX::get_native_window_position() const { + + NSRect nsrect = [window_object frame]; + Point2 pos; + float display_scale = _display_scale(); - Size2 wp([window_object frame].origin.x, [window_object frame].origin.y); - wp *= _display_scale(); - return wp; + // Return the position of the top-left corner, for OS X the y starts at the bottom + pos.x = nsrect.origin.x * display_scale; + pos.y = (nsrect.origin.y + nsrect.size.height) * display_scale; + + return pos; }; -void OS_OSX::set_window_position(const Point2 &p_position) { +Point2 OS_OSX::get_window_position() const { + Point2 position = get_native_window_position() - get_screens_origin(); + // OS X native y-coordinate relative to get_screens_origin() is negative, + // Godot expects a positive value + position.y *= -1; + return position; +} + +void OS_OSX::set_native_window_position(const Point2 &p_position) { - Size2 scr = get_screen_size(); NSPoint pos; float displayScale = _display_scale(); pos.x = p_position.x / displayScale; - // For OS X the y starts at the bottom - pos.y = (scr.height - p_position.y) / displayScale; + pos.y = p_position.y / displayScale; [window_object setFrameTopLeftPoint:pos]; _update_window(); }; +void OS_OSX::set_window_position(const Point2 &p_position) { + Point2 position = p_position; + // OS X native y-coordinate relative to get_screens_origin() is negative, + // Godot passes a positive value + position.y *= -1; + set_native_window_position(get_screens_origin() + position); +}; + Size2 OS_OSX::get_window_size() const { return window_size; }; +Size2 OS_OSX::get_real_window_size() const { + + NSRect frame = [window_object frame]; + return Size2(frame.size.width, frame.size.height); +} + void OS_OSX::set_window_size(const Size2 p_size) { Size2 size = p_size; @@ -1627,10 +1915,14 @@ void OS_OSX::set_window_size(const Size2 p_size) { 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]; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200 + if (floor(NSAppKitVersionNumber) < NSAppKitVersionNumber10_12) { +#else + { #endif + size.y += [[NSStatusBar systemStatusBar] thickness]; + } } } @@ -1643,36 +1935,27 @@ void OS_OSX::set_window_size(const Size2 p_size) { void OS_OSX::set_window_fullscreen(bool p_enabled) { if (zoomed != p_enabled) { -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 [window_object toggleFullScreen:nil]; -#else - [window_object performZoom:nil]; -#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/ } zoomed = p_enabled; }; bool OS_OSX::is_window_fullscreen() const { -#if MAC_OS_X_VERSION_MAX_ALLOWED < 1070 - if ([window_object respondsToSelector:@selector(isZoomed)]) - return [window_object isZoomed]; -#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/ - return zoomed; }; void OS_OSX::set_window_resizable(bool p_enabled) { if (p_enabled) - [window_object setStyleMask:[window_object styleMask] | NSResizableWindowMask]; + [window_object setStyleMask:[window_object styleMask] | NSWindowStyleMaskResizable]; else - [window_object setStyleMask:[window_object styleMask] & ~NSResizableWindowMask]; + [window_object setStyleMask:[window_object styleMask] & ~NSWindowStyleMaskResizable]; }; bool OS_OSX::is_window_resizable() const { - return [window_object styleMask] & NSResizableWindowMask; + return [window_object styleMask] & NSWindowStyleMaskResizable; }; void OS_OSX::set_window_minimized(bool p_enabled) { @@ -1714,12 +1997,26 @@ void OS_OSX::move_window_to_foreground() { [window_object orderFrontRegardless]; } +void OS_OSX::set_window_always_on_top(bool p_enabled) { + if (is_window_always_on_top() == p_enabled) + return; + + if (p_enabled) + [window_object setLevel:NSFloatingWindowLevel]; + else + [window_object setLevel:NSNormalWindowLevel]; +} + +bool OS_OSX::is_window_always_on_top() const { + return [window_object level] == NSFloatingWindowLevel; +} + void OS_OSX::request_attention() { [NSApp requestUserAttention:NSCriticalRequest]; } -void OS_OSX::set_borderless_window(int p_borderless) { +void OS_OSX::set_borderless_window(bool p_borderless) { // OrderOut prevents a lose focus bug with the window [window_object orderOut:nil]; @@ -1727,7 +2024,7 @@ void OS_OSX::set_borderless_window(int p_borderless) { if (p_borderless) { [window_object setStyleMask:NSWindowStyleMaskBorderless]; } else { - [window_object setStyleMask:NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask]; + [window_object setStyleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable]; // Force update of the window styles NSRect frameRect = [window_object frame]; @@ -1851,7 +2148,7 @@ void OS_OSX::process_events() { while (true) { NSEvent *event = [NSApp - nextEventMatchingMask:NSAnyEventMask + nextEventMatchingMask:NSEventMaskAny untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES]; @@ -1861,17 +2158,61 @@ void OS_OSX::process_events() { [NSApp sendEvent:event]; } + process_key_events(); [autoreleasePool drain]; autoreleasePool = [[NSAutoreleasePool alloc] init]; } +void OS_OSX::process_key_events() { + + Ref<InputEventKey> k; + for (int i = 0; i < key_event_pos; i++) { + + KeyEvent &ke = key_event_buffer[i]; + + if ((i == 0 && ke.scancode == 0) || (i > 0 && key_event_buffer[i - 1].scancode == 0)) { + k.instance(); + + get_key_modifier_state(ke.osx_state, k); + k->set_pressed(ke.pressed); + k->set_echo(ke.echo); + k->set_scancode(0); + k->set_unicode(ke.unicode); + + push_input(k); + } + if (ke.scancode != 0) { + k.instance(); + + get_key_modifier_state(ke.osx_state, k); + k->set_pressed(ke.pressed); + k->set_echo(ke.echo); + k->set_scancode(ke.scancode); + + if (i + 1 < key_event_pos && key_event_buffer[i + 1].scancode == 0) { + k->set_unicode(key_event_buffer[i + 1].unicode); + } + + push_input(k); + } + } + + key_event_pos = 0; +} + void OS_OSX::push_input(const Ref<InputEvent> &p_event) { Ref<InputEvent> ev = p_event; input->parse_input_event(ev); } +void OS_OSX::force_process_input() { + + process_events(); // get rid of pending events + joypad_osx->process_joypads(); +} + void OS_OSX::run() { force_quit = false; @@ -1958,14 +2299,14 @@ Error OS_OSX::move_to_trash(const String &p_path) { return OK; } -void OS_OSX::set_use_vsync(bool p_enable) { +void OS_OSX::_set_use_vsync(bool p_enable) { CGLContextObj ctx = CGLGetCurrentContext(); if (ctx) { GLint swapInterval = p_enable ? 1 : 0; CGLSetParameter(ctx, kCGLCPSwapInterval, &swapInterval); } } - +/* bool OS_OSX::is_vsync_enabled() const { GLint swapInterval = 0; CGLContextObj ctx = CGLGetCurrentContext(); @@ -1974,11 +2315,12 @@ bool OS_OSX::is_vsync_enabled() const { } return swapInterval ? true : false; } - +*/ OS_OSX *OS_OSX::singleton = NULL; OS_OSX::OS_OSX() { + key_event_pos = 0; mouse_mode = OS::MOUSE_MODE_VISIBLE; main_loop = NULL; singleton = this; @@ -2014,7 +2356,7 @@ OS_OSX::OS_OSX() { NSMenuItem *menu_item; NSString *title; - NSString *nsappname = [[[NSBundle mainBundle] performSelector:@selector(localizedInfoDictionary)] objectForKey:@"CFBundleName"]; + NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"]; if (nsappname == nil) nsappname = [[NSProcessInfo processInfo] processName]; @@ -2037,7 +2379,7 @@ OS_OSX::OS_OSX() { [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)]; + [menu_item setKeyEquivalentModifierMask:(NSEventModifierFlagOption | NSEventModifierFlagCommand)]; [apple_menu addItemWithTitle:NSLocalizedString(@"Show all", nil) action:@selector(unhideAllApplications:) keyEquivalent:@""]; @@ -2068,7 +2410,25 @@ OS_OSX::OS_OSX() { window_size = Vector2(1024, 600); zoomed = false; - _set_logger(memnew(OSXTerminalLogger)); + Vector<Logger *> loggers; + loggers.push_back(memnew(OSXTerminalLogger)); + _set_logger(memnew(CompositeLogger(loggers))); + + //process application:openFile: event + while (true) { + NSEvent *event = [NSApp + nextEventMatchingMask:NSEventMaskAny + untilDate:[NSDate distantPast] + inMode:NSDefaultRunLoopMode + dequeue:YES]; + + if (event == nil) + break; + + [NSApp sendEvent:event]; + } + + AudioDriverManager::add_driver(&audio_driver); } bool OS_OSX::_check_internal_feature_support(const String &p_feature) { diff --git a/platform/osx/platform_config.h b/platform/osx/platform_config.h index d2474fcfc7..3f72831d77 100644 --- a/platform/osx/platform_config.h +++ b/platform/osx/platform_config.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,7 +27,9 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include <alloca.h> #define GLES3_INCLUDE_H "glad/glad.h" +#define GLES2_INCLUDE_H "glad/glad.h" #define PTHREAD_RENAME_SELF diff --git a/platform/osx/power_osx.cpp b/platform/osx/power_osx.cpp index eed03e63c1..22e279dfa7 100644 --- a/platform/osx/power_osx.cpp +++ b/platform/osx/power_osx.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -242,8 +242,10 @@ int power_osx::get_power_percent_left() { } } -power_osx::power_osx() - : nsecs_left(-1), percent_left(-1), power_state(OS::POWERSTATE_UNKNOWN) { +power_osx::power_osx() : + nsecs_left(-1), + percent_left(-1), + power_state(OS::POWERSTATE_UNKNOWN) { } power_osx::~power_osx() { diff --git a/platform/osx/power_osx.h b/platform/osx/power_osx.h index 20e47e9cd9..7123e34a03 100644 --- a/platform/osx/power_osx.h +++ b/platform/osx/power_osx.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ diff --git a/platform/osx/sem_osx.cpp b/platform/osx/sem_osx.cpp index f75ac181bf..92f749322e 100644 --- a/platform/osx/sem_osx.cpp +++ b/platform/osx/sem_osx.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "sem_osx.h" #include <fcntl.h> diff --git a/platform/osx/sem_osx.h b/platform/osx/sem_osx.h index 2b408b00de..ce31e966b7 100644 --- a/platform/osx/sem_osx.h +++ b/platform/osx/sem_osx.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef SEM_OSX_H #define SEM_OSX_H diff --git a/platform/windows/tcp_server_winsock.h b/platform/register_platform_apis.h index a3e01098ed..1c50eb95e3 100644 --- a/platform/windows/tcp_server_winsock.h +++ b/platform/register_platform_apis.h @@ -1,12 +1,12 @@ /*************************************************************************/ -/* tcp_server_winsock.h */ +/* register_platform_apis.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,30 +27,11 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef TCP_SERVER_WINSOCK_H -#define TCP_SERVER_WINSOCK_H -#include "core/io/tcp_server.h" +#ifndef REGISTER_APIS_H +#define REGISTER_APIS_H -class TCPServerWinsock : public TCP_Server { - - int listen_sockfd; - IP::Type sock_type; - - static TCP_Server *_create(); - -public: - virtual Error listen(uint16_t p_port, const IP_Address &p_bind_address = IP_Address("*")); - virtual bool is_connection_available() const; - virtual Ref<StreamPeerTCP> take_connection(); - - virtual void stop(); //stop listening - - static void make_default(); - static void cleanup(); - - TCPServerWinsock(); - ~TCPServerWinsock(); -}; +void register_platform_apis(); +void unregister_platform_apis(); #endif diff --git a/platform/server/SCsub b/platform/server/SCsub index 30195bb908..0788ad75ae 100644 --- a/platform/server/SCsub +++ b/platform/server/SCsub @@ -5,6 +5,8 @@ Import('env') common_server = [\ "os_server.cpp",\ + "#platform/x11/crash_handler_x11.cpp", + "#platform/x11/power_x11.cpp", ] -env.Program('#bin/godot_server', ['godot_server.cpp'] + common_server) +prog = env.add_program('#bin/godot_server', ['godot_server.cpp'] + common_server) diff --git a/platform/server/detect.py b/platform/server/detect.py index ffec2af933..7bf445b43f 100644 --- a/platform/server/detect.py +++ b/platform/server/detect.py @@ -1,4 +1,5 @@ import os +import platform import sys @@ -12,9 +13,6 @@ def get_name(): def can_build(): - # Doesn't build against Godot 3.0 for now, disable to avoid confusing users - return False - if (os.name != "posix" or sys.platform == "darwin"): return False @@ -25,12 +23,14 @@ def get_opts(): from SCons.Variables import BoolVariable return [ BoolVariable('use_llvm', 'Use the LLVM compiler', False), + BoolVariable('use_static_cpp', 'Link libgcc and libstdc++ statically for better portability', False), ] def get_flags(): return [ + ("module_mobile_vr_enabled", False), ] @@ -59,7 +59,7 @@ def configure(env): if ('clang++' not in env['CXX']): env["CC"] = "clang" env["CXX"] = "clang++" - env["LD"] = "clang++" + env["LINK"] = "clang++" env.Append(CPPFLAGS=['-DTYPED_METHOD_BIND']) env.extra_suffix = ".llvm" + env.extra_suffix @@ -67,9 +67,6 @@ def configure(env): # FIXME: Check for existence of the libs before parsing their flags with pkg-config - if not env['builtin_openssl']: - env.ParseConfig('pkg-config openssl --cflags --libs') - if not env['builtin_libwebp']: env.ParseConfig('pkg-config libwebp --cflags --libs') @@ -86,6 +83,16 @@ def configure(env): if not env['builtin_libpng']: env.ParseConfig('pkg-config libpng --cflags --libs') + if not env['builtin_bullet']: + # We need at least version 2.88 + import subprocess + bullet_version = subprocess.check_output(['pkg-config', 'bullet', '--modversion']).strip() + if bullet_version < "2.88": + # Abort as system bullet was requested but too old + print("Bullet: System version {0} does not match minimal requirements ({1}). Aborting.".format(bullet_version, "2.88")) + sys.exit(255) + env.ParseConfig('pkg-config bullet --cflags --libs') + if not env['builtin_enet']: env.ParseConfig('pkg-config libenet --cflags --libs') @@ -117,6 +124,11 @@ def configure(env): if not env['builtin_libogg']: env.ParseConfig('pkg-config ogg --cflags --libs') + # On Linux wchar_t should be 32-bits + # 16-bit library shouldn't be required due to compiler optimisations + if not env['builtin_pcre2']: + env.ParseConfig('pkg-config libpcre2-32 --cflags --libs') + ## Flags # Linkflags below this line should typically stay the last ones @@ -126,3 +138,13 @@ def configure(env): env.Append(CPPPATH=['#platform/server']) env.Append(CPPFLAGS=['-DSERVER_ENABLED', '-DUNIX_ENABLED']) env.Append(LIBS=['pthread']) + + if (platform.system() == "Linux"): + env.Append(LIBS=['dl']) + + if (platform.system().find("BSD") >= 0): + env.Append(LIBS=['execinfo']) + + # Link those statically for portability + if env['use_static_cpp']: + env.Append(LINKFLAGS=['-static-libgcc', '-static-libstdc++']) diff --git a/platform/server/godot_server.cpp b/platform/server/godot_server.cpp index 2fd8c315de..3e48f0bf7f 100644 --- a/platform/server/godot_server.cpp +++ b/platform/server/godot_server.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "main/main.h" #include "os_server.h" diff --git a/platform/server/os_server.cpp b/platform/server/os_server.cpp index 300c5cffcc..a8be4fbc35 100644 --- a/platform/server/os_server.cpp +++ b/platform/server/os_server.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,11 +27,11 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -//#include "servers/visual/visual_server_raster.h" -//#include "servers/visual/rasterizer_dummy.h" #include "os_server.h" +#include "drivers/dummy/audio_driver_dummy.h" +#include "drivers/dummy/rasterizer_dummy.h" #include "print_string.h" -#include "servers/physics/physics_server_sw.h" +#include "servers/visual/visual_server_raster.h" #include <stdio.h> #include <stdlib.h> @@ -47,87 +47,69 @@ const char *OS_Server::get_video_driver_name(int p_driver) const { return "Dummy"; } -OS::VideoMode OS_Server::get_default_video_mode() const { - return OS::VideoMode(800, 600, false); +int OS_Server::get_audio_driver_count() const { + return 1; +} + +const char *OS_Server::get_audio_driver_name(int p_driver) const { + + return "Dummy"; +} + +void OS_Server::initialize_core() { + + crash_handler.initialize(); + + OS_Unix::initialize_core(); } -void OS_Server::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { +Error OS_Server::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { args = OS::get_singleton()->get_cmdline_args(); current_videomode = p_desired; main_loop = NULL; - //rasterizer = memnew( RasterizerDummy ); + RasterizerDummy::make_current(); - //visual_server = memnew( VisualServerRaster(rasterizer) ); + visual_server = memnew(VisualServerRaster); + visual_server->init(); AudioDriverManager::initialize(p_audio_driver); - sample_manager = memnew(SampleManagerMallocSW); - audio_server = memnew(AudioServerSW(sample_manager)); - audio_server->init(); - spatial_sound_server = memnew(SpatialSoundServerSW); - spatial_sound_server->init(); - spatial_sound_2d_server = memnew(SpatialSound2DServerSW); - spatial_sound_2d_server->init(); - - ERR_FAIL_COND(!visual_server); + input = memnew(InputDefault); - visual_server->init(); - // - physics_server = memnew(PhysicsServerSW); - physics_server->init(); - physics_2d_server = memnew(Physics2DServerSW); - physics_2d_server->init(); + power_manager = memnew(PowerX11); - input = memnew(InputDefault); + _ensure_user_data_dir(); - _ensure_data_dir(); + return OK; } + void OS_Server::finalize() { if (main_loop) memdelete(main_loop); main_loop = NULL; - spatial_sound_server->finish(); - memdelete(spatial_sound_server); - spatial_sound_2d_server->finish(); - memdelete(spatial_sound_2d_server); - - /* - if (debugger_connection_console) { - memdelete(debugger_connection_console); - } - */ - - memdelete(sample_manager); - - audio_server->finish(); - memdelete(audio_server); - visual_server->finish(); memdelete(visual_server); - //memdelete(rasterizer); - - physics_server->finish(); - memdelete(physics_server); - - physics_2d_server->finish(); - memdelete(physics_2d_server); memdelete(input); + memdelete(power_manager); + args.clear(); } void OS_Server::set_mouse_show(bool p_show) { } + void OS_Server::set_mouse_grab(bool p_grab) { grab = p_grab; } + bool OS_Server::is_mouse_grab_enabled() const { return grab; @@ -148,6 +130,7 @@ void OS_Server::set_window_title(const String &p_title) { void OS_Server::set_video_mode(const VideoMode &p_video_mode, int p_screen) { } + OS::VideoMode OS_Server::get_video_mode(int p_screen) const { return current_videomode; @@ -195,6 +178,9 @@ void OS_Server::move_window_to_foreground() { void OS_Server::set_cursor_shape(CursorShape p_shape) { } +void OS_Server::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { +} + OS::PowerState OS_Server::get_power_state() { return power_manager->get_power_state(); } @@ -207,6 +193,10 @@ int OS_Server::get_power_percent_left() { return power_manager->get_power_percent_left(); } +bool OS_Server::_check_internal_feature_support(const String &p_feature) { + return p_feature == "pc"; +} + void OS_Server::run() { force_quit = false; @@ -225,6 +215,102 @@ void OS_Server::run() { main_loop->finish(); } +String OS_Server::get_config_path() const { + + if (has_environment("XDG_CONFIG_HOME")) { + return get_environment("XDG_CONFIG_HOME"); + } else if (has_environment("HOME")) { + return get_environment("HOME").plus_file(".config"); + } else { + return "."; + } +} + +String OS_Server::get_data_path() const { + + if (has_environment("XDG_DATA_HOME")) { + return get_environment("XDG_DATA_HOME"); + } else if (has_environment("HOME")) { + return get_environment("HOME").plus_file(".local/share"); + } else { + return get_config_path(); + } +} + +String OS_Server::get_cache_path() const { + + if (has_environment("XDG_CACHE_HOME")) { + return get_environment("XDG_CACHE_HOME"); + } else if (has_environment("HOME")) { + return get_environment("HOME").plus_file(".cache"); + } else { + return get_config_path(); + } +} + +String OS_Server::get_system_dir(SystemDir p_dir) const { + + String xdgparam; + + switch (p_dir) { + case SYSTEM_DIR_DESKTOP: { + + xdgparam = "DESKTOP"; + } break; + case SYSTEM_DIR_DCIM: { + + xdgparam = "PICTURES"; + + } break; + case SYSTEM_DIR_DOCUMENTS: { + + xdgparam = "DOCUMENTS"; + + } break; + case SYSTEM_DIR_DOWNLOADS: { + + xdgparam = "DOWNLOAD"; + + } break; + case SYSTEM_DIR_MOVIES: { + + xdgparam = "VIDEOS"; + + } break; + case SYSTEM_DIR_MUSIC: { + + xdgparam = "MUSIC"; + + } break; + case SYSTEM_DIR_PICTURES: { + + xdgparam = "PICTURES"; + + } break; + case SYSTEM_DIR_RINGTONES: { + + xdgparam = "MUSIC"; + + } break; + } + + String pipe; + List<String> arg; + arg.push_back(xdgparam); + Error err = const_cast<OS_Server *>(this)->execute("xdg-user-dir", arg, true, NULL, &pipe); + if (err != OK) + return "."; + return pipe.strip_edges(); +} + +void OS_Server::disable_crash_handler() { + crash_handler.disable(); +} + +bool OS_Server::is_disable_crash_handler() const { + return crash_handler.is_disabled(); +} + OS_Server::OS_Server() { //adriver here diff --git a/platform/server/os_server.h b/platform/server/os_server.h index ba12f649be..2cc6f0c47e 100644 --- a/platform/server/os_server.h +++ b/platform/server/os_server.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -30,17 +30,15 @@ #ifndef OS_SERVER_H #define OS_SERVER_H +#include "../x11/crash_handler_x11.h" #include "../x11/power_x11.h" #include "drivers/rtaudio/audio_driver_rtaudio.h" #include "drivers/unix/os_unix.h" #include "main/input_default.h" #include "servers/audio_server.h" -#include "servers/physics_2d/physics_2d_server_sw.h" -#include "servers/physics_server.h" #include "servers/visual/rasterizer.h" #include "servers/visual_server.h" -//bitch #undef CursorShape /** @author Juan Linietsky <reduzio@gmail.com> @@ -56,9 +54,6 @@ class OS_Server : public OS_Unix { bool grab; - PhysicsServer *physics_server; - Physics2DServer *physics_2d_server; - virtual void delete_main_loop(); IP_Unix *ip_unix; @@ -68,12 +63,17 @@ class OS_Server : public OS_Unix { PowerX11 *power_manager; + CrashHandler crash_handler; + protected: virtual int get_video_driver_count() const; virtual const char *get_video_driver_name(int p_driver) const; - virtual VideoMode get_default_video_mode() const; - virtual void initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver); + virtual int get_audio_driver_count() const; + virtual const char *get_audio_driver_name(int p_driver) const; + + virtual void initialize_core(); + virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver); virtual void finalize(); virtual void set_main_loop(MainLoop *p_main_loop); @@ -82,6 +82,7 @@ public: virtual String get_name(); virtual void set_cursor_shape(CursorShape p_shape); + virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); virtual void set_mouse_show(bool p_show); virtual void set_mouse_grab(bool p_grab); @@ -107,6 +108,16 @@ public: virtual OS::PowerState get_power_state(); virtual int get_power_seconds_left(); virtual int get_power_percent_left(); + virtual bool _check_internal_feature_support(const String &p_feature); + + virtual String get_config_path() const; + virtual String get_data_path() const; + virtual String get_cache_path() const; + + virtual String get_system_dir(SystemDir p_dir) const; + + void disable_crash_handler(); + bool is_disable_crash_handler() const; OS_Server(); }; diff --git a/platform/server/platform_config.h b/platform/server/platform_config.h index b1c3f027f3..2fa8eda337 100644 --- a/platform/server/platform_config.h +++ b/platform/server/platform_config.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,4 +27,11 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + +#ifdef __linux__ #include <alloca.h> +#endif +#if defined(__FreeBSD__) || defined(__OpenBSD__) +#include <stdlib.h> +#define PTHREAD_BSD_SET_NAME +#endif diff --git a/platform/uwp/SCsub b/platform/uwp/SCsub index bbd329a7e5..fb0c4a92ae 100644 --- a/platform/uwp/SCsub +++ b/platform/uwp/SCsub @@ -4,9 +4,6 @@ Import('env') files = [ 'thread_uwp.cpp', - '#platform/windows/tcp_server_winsock.cpp', - '#platform/windows/packet_peer_udp_winsock.cpp', - '#platform/windows/stream_peer_winsock.cpp', '#platform/windows/key_mapping_win.cpp', '#platform/windows/windows_terminal_logger.cpp', 'joypad_uwp.cpp', @@ -19,7 +16,7 @@ files = [ if "build_angle" in env and env["build_angle"]: cmd = env.AlwaysBuild(env.ANGLE('libANGLE.lib', None)) -prog = env.Program('#bin/godot', files) +prog = env.add_program('#bin/godot', files) if "build_angle" in env and env["build_angle"]: env.Depends(prog, [cmd]) diff --git a/platform/uwp/app.cpp b/platform/uwp/app.cpp index c565999d82..5ff62b38f9 100644 --- a/platform/uwp/app.cpp +++ b/platform/uwp/app.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + // // This file demonstrates how to initialize EGL in a Windows Store app, using ICoreWindow. // @@ -77,15 +78,15 @@ public: return 0; } -App::App() - : mWindowClosed(false), - mWindowVisible(true), - mWindowWidth(0), - mWindowHeight(0), - mEglDisplay(EGL_NO_DISPLAY), - mEglContext(EGL_NO_CONTEXT), - mEglSurface(EGL_NO_SURFACE), - number_of_contacts(0) { +App::App() : + mWindowClosed(false), + mWindowVisible(true), + mWindowWidth(0), + mWindowHeight(0), + mEglDisplay(EGL_NO_DISPLAY), + mEglContext(EGL_NO_CONTEXT), + mEglSurface(EGL_NO_SURFACE), + number_of_contacts(0) { } // The first method called when the IFrameworkView is being created. diff --git a/platform/uwp/app.h b/platform/uwp/app.h index e079fa9c9d..c23270b8ba 100644 --- a/platform/uwp/app.h +++ b/platform/uwp/app.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,12 +27,14 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #pragma once #include <string> #include <wrl.h> +// ANGLE doesn't provide a specific lib for GLES3, so we keep using GLES2 #include "GLES2/gl2.h" #include "os_uwp.h" diff --git a/platform/uwp/detect.py b/platform/uwp/detect.py index af53f97446..f2c5c5d42e 100644 --- a/platform/uwp/detect.py +++ b/platform/uwp/detect.py @@ -84,7 +84,7 @@ def configure(env): ## Architecture arch = "" - if os.getenv('Platform') == "ARM": + if str(os.getenv('Platform')).lower() == "arm": print("Compiled program architecture will be an ARM executable. (forcing bits=32).") @@ -136,7 +136,7 @@ def configure(env): 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']) + env.Append(CCFLAGS=['/DGLES_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]) @@ -160,6 +160,7 @@ def configure(env): 'libANGLE', 'libEGL', 'libGLESv2', + 'bcrypt', ] env.Append(LINKFLAGS=[p + ".lib" for p in LIBS]) diff --git a/platform/uwp/export/export.cpp b/platform/uwp/export/export.cpp index d66bcaa91c..7d7bee9227 100644 --- a/platform/uwp/export/export.cpp +++ b/platform/uwp/export/export.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -137,7 +137,7 @@ 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(const uint8_t *p_block_data, size_t p_block_len); void make_block_map(); void make_content_types(); @@ -188,14 +188,14 @@ public: /////////////////////////////////////////////////////////////////////////// -String AppxPackager::hash_block(uint8_t *p_block_data, size_t p_block_len) { +String AppxPackager::hash_block(const uint8_t *p_block_data, size_t p_block_len) { char hash[32]; char base64[45]; sha256_context ctx; sha256_init(&ctx); - sha256_hash(&ctx, p_block_data, p_block_len); + sha256_hash(&ctx, (uint8_t *)p_block_data, p_block_len); sha256_done(&ctx, (uint8_t *)hash); base64_encode(base64, hash, 32); @@ -456,8 +456,8 @@ 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"; + tmp_blockmap_file_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmpblockmap.xml"); + tmp_content_types_file_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmpcontenttypes.xml"); } 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) { @@ -510,8 +510,8 @@ void AppxPackager::add_file(String p_file_name, const uint8_t *p_buffer, size_t strm.avail_in = block_size; strm.avail_out = strm_out.size(); - strm.next_in = strm_in.ptr(); - strm.next_out = strm_out.ptr(); + strm.next_in = (uint8_t *)strm_in.ptr(); + strm.next_out = strm_out.ptrw(); int total_out_before = strm.total_out; @@ -541,8 +541,8 @@ void AppxPackager::add_file(String p_file_name, const uint8_t *p_buffer, size_t strm.avail_in = 0; strm.avail_out = strm_out.size(); - strm.next_in = strm_in.ptr(); - strm.next_out = strm_out.ptr(); + strm.next_in = (uint8_t *)strm_in.ptr(); + strm.next_out = strm_out.ptrw(); int total_out_before = strm.total_out; @@ -588,7 +588,7 @@ void AppxPackager::finish() { Vector<uint8_t> blockmap_buffer; blockmap_buffer.resize(blockmap_file->get_len()); - blockmap_file->get_buffer(blockmap_buffer.ptr(), blockmap_buffer.size()); + blockmap_file->get_buffer(blockmap_buffer.ptrw(), blockmap_buffer.size()); add_file("AppxBlockMap.xml", blockmap_buffer.ptr(), blockmap_buffer.size(), -1, -1, true); @@ -604,7 +604,7 @@ void AppxPackager::finish() { Vector<uint8_t> types_buffer; types_buffer.resize(types_file->get_len()); - types_file->get_buffer(types_buffer.ptr(), types_buffer.size()); + types_file->get_buffer(types_buffer.ptrw(), types_buffer.size()); add_file("[Content_Types].xml", types_buffer.ptr(), types_buffer.size(), -1, -1, true); @@ -886,7 +886,7 @@ class EditorExportUWP : public EditorExportPlatform { if (!image) return data; - String tmp_path = EditorSettings::get_singleton()->get_settings_path().plus_file("tmp/uwp_tmp_logo.png"); + String tmp_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("uwp_tmp_logo.png"); Error err = image->get_data()->save_png(tmp_path); @@ -911,7 +911,7 @@ class EditorExportUWP : public EditorExportPlatform { } data.resize(f->get_len()); - f->get_buffer(data.ptr(), data.size()); + f->get_buffer(data.ptrw(), data.size()); f->close(); memdelete(f); @@ -1013,7 +1013,7 @@ public: return "UWP"; } - virtual String get_binary_extension() const { + virtual String get_binary_extension(const Ref<EditorExportPreset> &p_preset) const { return "appx"; } @@ -1024,6 +1024,17 @@ public: virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) { r_features->push_back("s3tc"); r_features->push_back("etc"); + switch ((int)p_preset->get("architecture/target")) { + case EditorExportUWP::ARM: { + r_features->push_back("arm"); + } break; + case EditorExportUWP::X86: { + r_features->push_back("32"); + } break; + case EditorExportUWP::X64: { + r_features->push_back("64"); + } break; + } } virtual void get_export_options(List<ExportOption> *r_options) { @@ -1041,6 +1052,10 @@ public: 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")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "signing/certificate", PROPERTY_HINT_GLOBAL_FILE, "*.pfx"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "signing/password"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "signing/algorithm", PROPERTY_HINT_ENUM, "MD5,SHA1,SHA256"), 2)); + 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)); @@ -1067,7 +1082,7 @@ public: 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"), "")); - // Capabilites + // Capabilities const char **basic = uwp_capabilities; while (*basic) { r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/" + String(*basic).camelcase_to_underscore(false)), false)); @@ -1301,7 +1316,7 @@ public: if (do_read) { data.resize(info.uncompressed_size); unzOpenCurrentFile(pkg); - unzReadCurrentFile(pkg, data.ptr(), data.size()); + unzReadCurrentFile(pkg, data.ptrw(), data.size()); unzCloseCurrentFile(pkg); } @@ -1341,7 +1356,7 @@ public: // Argc clf.resize(4); - encode_uint32(cl.size(), clf.ptr()); + encode_uint32(cl.size(), clf.ptrw()); for (int i = 0; i < cl.size(); i++) { @@ -1370,6 +1385,58 @@ public: packager.finish(); +#ifdef WINDOWS_ENABLED + // Sign with signtool + String signtool_path = EditorSettings::get_singleton()->get("export/uwp/signtool"); + if (signtool_path == String()) { + return OK; + } + + if (!FileAccess::exists(signtool_path)) { + ERR_PRINTS("Could not find signtool executable at " + signtool_path + ", aborting."); + return ERR_FILE_NOT_FOUND; + } + + static String algs[] = { "MD5", "SHA1", "SHA256" }; + + String cert_path = EditorSettings::get_singleton()->get("export/uwp/debug_certificate"); + String cert_pass = EditorSettings::get_singleton()->get("export/uwp/debug_password"); + int cert_alg = EditorSettings::get_singleton()->get("export/uwp/debug_algorithm"); + + if (!p_debug) { + cert_path = p_preset->get("signing/certificate"); + cert_pass = p_preset->get("signing/password"); + cert_alg = p_preset->get("signing/algorithm"); + } + + if (cert_path == String()) { + return OK; // Certificate missing, don't try to sign + } + + if (!FileAccess::exists(cert_path)) { + ERR_PRINTS("Could not find certificate file at " + cert_path + ", aborting."); + return ERR_FILE_NOT_FOUND; + } + + if (cert_alg < 0 || cert_alg > 2) { + ERR_PRINTS("Invalid certificate algorithm " + itos(cert_alg) + ", aborting."); + return ERR_INVALID_DATA; + } + + List<String> args; + args.push_back("sign"); + args.push_back("/fd"); + args.push_back(algs[cert_alg]); + args.push_back("/a"); + args.push_back("/f"); + args.push_back(cert_path); + args.push_back("/p"); + args.push_back(cert_pass); + args.push_back(p_path); + + OS::get_singleton()->execute(signtool_path, args, true); +#endif // WINDOWS_ENABLED + return OK; } @@ -1387,6 +1454,18 @@ public: }; void register_uwp_exporter() { - Ref<EditorExportUWP> exporter = Ref<EditorExportUWP>(memnew(EditorExportUWP)); + +#ifdef WINDOWS_ENABLED + EDITOR_DEF("export/uwp/signtool", ""); + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/uwp/signtool", PROPERTY_HINT_GLOBAL_FILE, "*.exe")); + EDITOR_DEF("export/uwp/debug_certificate", ""); + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/uwp/debug_certificate", PROPERTY_HINT_GLOBAL_FILE, "*.pfx")); + EDITOR_DEF("export/uwp/debug_password", ""); + EDITOR_DEF("export/uwp/debug_algorithm", 2); // SHA256 is the default + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "export/uwp/debug_algorithm", PROPERTY_HINT_ENUM, "MD5,SHA1,SHA256")); +#endif // WINDOWS_ENABLED + + Ref<EditorExportUWP> exporter; + exporter.instance(); EditorExport::get_singleton()->add_export_platform(exporter); } diff --git a/platform/uwp/export/export.h b/platform/uwp/export/export.h index 558af38a70..bd759c8647 100644 --- a/platform/uwp/export/export.h +++ b/platform/uwp/export/export.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,4 +27,5 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + void register_uwp_exporter(); diff --git a/platform/uwp/gl_context_egl.cpp b/platform/uwp/gl_context_egl.cpp index ed3db65cdf..88c9c8d687 100644 --- a/platform/uwp/gl_context_egl.cpp +++ b/platform/uwp/gl_context_egl.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "gl_context_egl.h" #include "EGL/eglext.h" @@ -101,26 +102,27 @@ Error ContextEGL::initialize() { try { - const EGLint displayAttributes[] = - { - /*EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, - EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, 9, - EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE, 3, - EGL_NONE,*/ - // These are the default display attributes, used to request ANGLE's D3D11 renderer. - // eglInitialize will only succeed with these attributes if the hardware supports D3D11 Feature Level 10_0+. - EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, - - // EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER is an optimization that can have large performance benefits on mobile devices. - // Its syntax is subject to change, though. Please update your Visual Studio templates if you experience compilation issues with it. - //EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER, EGL_TRUE, - - // EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE is an option that enables ANGLE to automatically call - // the IDXGIDevice3::Trim method on behalf of the application when it gets suspended. - // Calling IDXGIDevice3::Trim when an application is suspended is a Windows Store application certification requirement. - EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE, EGL_TRUE, - EGL_NONE, - }; + const EGLint displayAttributes[] = { + /*EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, + EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, 9, + EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE, 3, + EGL_NONE,*/ + // These are the default display attributes, used to request ANGLE's D3D11 renderer. + // eglInitialize will only succeed with these attributes if the hardware supports D3D11 Feature Level 10_0+. + EGL_PLATFORM_ANGLE_TYPE_ANGLE, + EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, + + // EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER is an optimization that can have large performance benefits on mobile devices. + // Its syntax is subject to change, though. Please update your Visual Studio templates if you experience compilation issues with it. + //EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER, EGL_TRUE, + + // EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE is an option that enables ANGLE to automatically call + // the IDXGIDevice3::Trim method on behalf of the application when it gets suspended. + // Calling IDXGIDevice3::Trim when an application is suspended is a Windows Store application certification requirement. + EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE, + EGL_TRUE, + EGL_NONE, + }; PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(eglGetProcAddress("eglGetPlatformDisplayEXT")); @@ -191,10 +193,10 @@ void ContextEGL::cleanup() { } }; -ContextEGL::ContextEGL(CoreWindow ^ p_window) - : mEglDisplay(EGL_NO_DISPLAY), - mEglContext(EGL_NO_CONTEXT), - mEglSurface(EGL_NO_SURFACE) { +ContextEGL::ContextEGL(CoreWindow ^ p_window) : + mEglDisplay(EGL_NO_DISPLAY), + mEglContext(EGL_NO_CONTEXT), + mEglSurface(EGL_NO_SURFACE) { window = p_window; }; diff --git a/platform/uwp/gl_context_egl.h b/platform/uwp/gl_context_egl.h index 1b24780b29..527baf1054 100644 --- a/platform/uwp/gl_context_egl.h +++ b/platform/uwp/gl_context_egl.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef CONTEXT_EGL_H #define CONTEXT_EGL_H diff --git a/platform/uwp/joypad_uwp.cpp b/platform/uwp/joypad_uwp.cpp index 0f84bd55a3..dcc9f7d3af 100644 --- a/platform/uwp/joypad_uwp.cpp +++ b/platform/uwp/joypad_uwp.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -54,7 +54,7 @@ void JoypadUWP::process_controllers() { case ControllerType::GAMEPAD_CONTROLLER: { - GamepadReading reading = ((Gamepad ^)joy.controller_reference)->GetCurrentReading(); + GamepadReading reading = ((Gamepad ^) joy.controller_reference)->GetCurrentReading(); int button_mask = (int)GamepadButtons::Menu; for (int j = 0; j < 14; j++) { @@ -161,7 +161,7 @@ void JoypadUWP::joypad_vibration_start(int p_device, float p_weak_magnitude, flo GamepadVibration vibration; vibration.LeftMotor = p_strong_magnitude; vibration.RightMotor = p_weak_magnitude; - ((Gamepad ^)joy.controller_reference)->Vibration = vibration; + ((Gamepad ^) joy.controller_reference)->Vibration = vibration; joy.ff_timestamp = p_timestamp; joy.ff_end_timestamp = p_duration == 0 ? 0 : p_timestamp + (uint64_t)(p_duration * 1000000.0); @@ -175,7 +175,7 @@ void JoypadUWP::joypad_vibration_stop(int p_device, uint64_t p_timestamp) { GamepadVibration vibration; vibration.LeftMotor = 0.0; vibration.RightMotor = 0.0; - ((Gamepad ^)joy.controller_reference)->Vibration = vibration; + ((Gamepad ^) joy.controller_reference)->Vibration = vibration; joy.ff_timestamp = p_timestamp; joy.vibrating = false; diff --git a/platform/uwp/joypad_uwp.h b/platform/uwp/joypad_uwp.h index 4efec485fa..98202cce9e 100644 --- a/platform/uwp/joypad_uwp.h +++ b/platform/uwp/joypad_uwp.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef JOYPAD_UWP_H #define JOYPAD_UWP_H diff --git a/platform/uwp/logo.png b/platform/uwp/logo.png Binary files differindex 4376abd563..9017a30636 100644 --- a/platform/uwp/logo.png +++ b/platform/uwp/logo.png diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp index c67e5bae05..d00da3dbcd 100644 --- a/platform/uwp/os_uwp.cpp +++ b/platform/uwp/os_uwp.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,19 +27,21 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "os_uwp.h" + #include "drivers/gles3/rasterizer_gles3.h" #include "drivers/unix/ip_unix.h" #include "drivers/windows/dir_access_windows.h" #include "drivers/windows/file_access_windows.h" #include "drivers/windows/mutex_windows.h" +#include "drivers/windows/packet_peer_udp_winsock.h" #include "drivers/windows/rw_lock_windows.h" #include "drivers/windows/semaphore_windows.h" +#include "drivers/windows/stream_peer_tcp_winsock.h" +#include "drivers/windows/tcp_server_winsock.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 "platform/windows/windows_terminal_logger.h" #include "project_settings.h" #include "servers/audio_server.h" @@ -69,12 +71,7 @@ int OSUWP::get_video_driver_count() const { } const char *OSUWP::get_video_driver_name(int p_driver) const { - return "GLES2"; -} - -OS::VideoMode OSUWP::get_default_video_mode() const { - - return video_mode; + return "GLES3"; } Size2 OSUWP::get_window_size() const { @@ -167,7 +164,7 @@ void OSUWP::initialize_core() { DirAccess::make_default<DirAccessWindows>(DirAccess::ACCESS_FILESYSTEM); TCPServerWinsock::make_default(); - StreamPeerWinsock::make_default(); + StreamPeerTCPWinsock::make_default(); PacketPeerUDPWinsock::make_default(); // We need to know how often the clock is updated @@ -183,15 +180,6 @@ void OSUWP::initialize_core() { cursor_shape = CURSOR_ARROW; } -void OSUWP::initialize_logger() { - Vector<Logger *> loggers; - loggers.push_back(memnew(WindowsTerminalLogger)); - // FIXME: Reenable once we figure out how to get this properly in user:// - // instead of littering the user's working dirs (res:// + pwd) with log files (GH-12277) - //loggers.push_back(memnew(RotatedFileLogger("user://logs/log.txt"))); - _set_logger(memnew(CompositeLogger(loggers))); -} - bool OSUWP::can_draw() const { return !minimized; @@ -207,7 +195,7 @@ void OSUWP::screen_size_changed() { gl_context->reset(); }; -void OSUWP::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { +Error OSUWP::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { main_loop = NULL; outside = true; @@ -254,6 +242,7 @@ void OSUWP::initialize(const VideoMode &p_desired, int p_video_driver, int p_aud RasterizerGLES3::register_config(); RasterizerGLES3::make_current(); + gl_context->set_use_vsync(vm.use_vsync); visual_server = memnew(VisualServerRaster); // FIXME: Reimplement threaded rendering? Or remove? @@ -264,13 +253,6 @@ void OSUWP::initialize(const VideoMode &p_desired, int p_video_driver, int p_aud } */ - // - physics_server = memnew(PhysicsServerSW); - physics_server->init(); - - physics_2d_server = memnew(Physics2DServerSW); - physics_2d_server->init(); - visual_server->init(); input = memnew(InputDefault); @@ -310,12 +292,14 @@ void OSUWP::initialize(const VideoMode &p_desired, int p_video_driver, int p_aud ref new TypedEventHandler<Gyrometer ^, GyrometerReadingChangedEventArgs ^>(managed_object, &ManagedType::on_gyroscope_reading_changed); } - _ensure_data_dir(); + _ensure_user_data_dir(); if (is_keep_screen_on()) display_request->RequestActive(); set_keep_screen_on(GLOBAL_DEF("display/window/keep_screen_on", true)); + + return OK; } void OSUWP::set_clipboard(const String &p_text) { @@ -369,12 +353,6 @@ void OSUWP::finalize() { memdelete(input); - physics_server->finish(); - memdelete(physics_server); - - physics_2d_server->finish(); - memdelete(physics_2d_server); - joypad = nullptr; } @@ -676,6 +654,10 @@ void OSUWP::set_cursor_shape(CursorShape p_shape) { cursor_shape = p_shape; } +void OSUWP::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { + // TODO +} + Error OSUWP::execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id, String *r_pipe, int *r_exitcode, bool read_stderr) { return FAILED; @@ -770,6 +752,51 @@ void OSUWP::hide_virtual_keyboard() { pane->TryHide(); } +static String format_error_message(DWORD id) { + + LPWSTR messageBuffer = NULL; + size_t size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&messageBuffer, 0, NULL); + + String msg = "Error " + itos(id) + ": " + String(messageBuffer, size); + + LocalFree(messageBuffer); + + return msg; +} + +Error OSUWP::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) { + + String full_path = "game/" + p_path; + p_library_handle = (void *)LoadPackagedLibrary(full_path.c_str(), 0); + + if (!p_library_handle) { + ERR_EXPLAIN("Can't open dynamic library: " + full_path + ". Error: " + format_error_message(GetLastError())); + ERR_FAIL_V(ERR_CANT_OPEN); + } + return OK; +} + +Error OSUWP::close_dynamic_library(void *p_library_handle) { + if (!FreeLibrary((HMODULE)p_library_handle)) { + return FAILED; + } + return OK; +} + +Error OSUWP::get_dynamic_library_symbol_handle(void *p_library_handle, const String p_name, void *&p_symbol_handle, bool p_optional) { + p_symbol_handle = (void *)GetProcAddress((HMODULE)p_library_handle, p_name.utf8().get_data()); + if (!p_symbol_handle) { + 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; +} + void OSUWP::run() { if (!main_loop) @@ -799,7 +826,7 @@ MainLoop *OSUWP::get_main_loop() const { return main_loop; } -String OSUWP::get_data_dir() const { +String OSUWP::get_user_data_dir() const { Windows::Storage::StorageFolder ^ data_folder = Windows::Storage::ApplicationData::Current->LocalFolder; @@ -851,7 +878,9 @@ OSUWP::OSUWP() { AudioDriverManager::add_driver(&audio_driver); - _set_logger(memnew(WindowsTerminalLogger)); + Vector<Logger *> loggers; + loggers.push_back(memnew(WindowsTerminalLogger)); + _set_logger(memnew(CompositeLogger(loggers))); } OSUWP::~OSUWP() { diff --git a/platform/uwp/os_uwp.h b/platform/uwp/os_uwp.h index 22f8938049..95afdab469 100644 --- a/platform/uwp/os_uwp.h +++ b/platform/uwp/os_uwp.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef OSUWP_H #define OSUWP_H @@ -40,8 +41,6 @@ #include "os/os.h" #include "power_uwp.h" #include "servers/audio_server.h" -#include "servers/physics/physics_server_sw.h" -#include "servers/physics_2d/physics_2d_server_sw.h" #include "servers/visual/rasterizer.h" #include "servers/visual_server.h" @@ -94,8 +93,6 @@ private: int old_x, old_y; Point2i center; VisualServer *visual_server; - PhysicsServer *physics_server; - Physics2DServer *physics_2d_server; int pressrc; ContextEGL *gl_context; @@ -158,14 +155,11 @@ protected: virtual int get_video_driver_count() const; virtual const char *get_video_driver_name(int p_driver) const; - virtual VideoMode get_default_video_mode() const; - virtual int get_audio_driver_count() const; virtual const char *get_audio_driver_name(int p_driver) const; - virtual void initialize_logger(); virtual void initialize_core(); - virtual void initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver); + virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver); virtual void set_main_loop(MainLoop *p_main_loop); virtual void delete_main_loop(); @@ -225,6 +219,7 @@ public: virtual String get_clipboard() const; void set_cursor_shape(CursorShape p_shape); + virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); void set_icon(const Ref<Image> &p_icon); virtual String get_executable_path() const; @@ -232,7 +227,7 @@ public: virtual String get_locale() const; virtual void move_window_to_foreground(); - virtual String get_data_dir() const; + virtual String get_user_data_dir() const; virtual bool _check_internal_feature_support(const String &p_feature); @@ -249,6 +244,10 @@ public: virtual void show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2()); virtual void hide_virtual_keyboard(); + virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false); + 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, bool p_optional = false); + virtual Error shell_open(String p_uri); void run(); diff --git a/platform/uwp/platform_config.h b/platform/uwp/platform_config.h index abe14f7578..606795479b 100644 --- a/platform/uwp/platform_config.h +++ b/platform/uwp/platform_config.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,4 +27,5 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include <malloc.h> diff --git a/platform/uwp/power_uwp.cpp b/platform/uwp/power_uwp.cpp index 81e97b1391..4eb48d2dd9 100644 --- a/platform/uwp/power_uwp.cpp +++ b/platform/uwp/power_uwp.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -30,8 +30,10 @@ #include "power_uwp.h" -PowerUWP::PowerUWP() - : nsecs_left(-1), percent_left(-1), power_state(OS::POWERSTATE_UNKNOWN) { +PowerUWP::PowerUWP() : + nsecs_left(-1), + percent_left(-1), + power_state(OS::POWERSTATE_UNKNOWN) { } PowerUWP::~PowerUWP() { diff --git a/platform/uwp/power_uwp.h b/platform/uwp/power_uwp.h index 0c57689c50..09572a15f4 100644 --- a/platform/uwp/power_uwp.h +++ b/platform/uwp/power_uwp.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ diff --git a/platform/uwp/thread_uwp.cpp b/platform/uwp/thread_uwp.cpp index 7c8c2b204a..25cd29190f 100644 --- a/platform/uwp/thread_uwp.cpp +++ b/platform/uwp/thread_uwp.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "thread_uwp.h" #include "os/memory.h" diff --git a/platform/uwp/thread_uwp.h b/platform/uwp/thread_uwp.h index 0446bd36ca..89081f3b2b 100644 --- a/platform/uwp/thread_uwp.h +++ b/platform/uwp/thread_uwp.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef THREAD_UWP_H #define THREAD_UWP_H diff --git a/platform/windows/SCsub b/platform/windows/SCsub index aa9eb3e69b..ed3827353d 100644 --- a/platform/windows/SCsub +++ b/platform/windows/SCsub @@ -4,9 +4,14 @@ import os Import('env') def make_debug_mingw(target, source, env): - os.system('objcopy --only-keep-debug %s %s.debug' % (target[0], target[0])) - os.system('strip --strip-debug --strip-unneeded %s' % (target[0])) - os.system('objcopy --add-gnu-debuglink=%s.debug %s' % (target[0], target[0])) + mingw_prefix = "" + if (env["bits"] == "32"): + mingw_prefix = env["mingw_prefix_32"] + else: + mingw_prefix = env["mingw_prefix_64"] + os.system(mingw_prefix + 'objcopy --only-keep-debug {0} {0}.debugsymbols'.format(target[0])) + os.system(mingw_prefix + 'strip --strip-debug --strip-unneeded {0}'.format(target[0])) + os.system(mingw_prefix + 'objcopy --add-gnu-debuglink={0}.debugsymbols {0}'.format(target[0])) common_win = [ "context_gl_win.cpp", @@ -14,9 +19,6 @@ common_win = [ "os_windows.cpp", "ctxgl_procaddr.cpp", "key_mapping_win.cpp", - "tcp_server_winsock.cpp", - "packet_peer_udp_winsock.cpp", - "stream_peer_winsock.cpp", "joypad.cpp", "power_windows.cpp", "windows_terminal_logger.cpp" @@ -28,7 +30,7 @@ obj = env.RES(restarget, 'godot_res.rc') common_win.append(obj) -binary = env.Program('#bin/godot', ['godot_win.cpp'] + common_win, PROGSUFFIX=env["PROGSUFFIX"]) +prog = env.add_program('#bin/godot', ['godot_win.cpp'] + common_win, PROGSUFFIX=env["PROGSUFFIX"]) # Microsoft Visual Studio Project Generation if env['vsproj']: @@ -37,5 +39,5 @@ if env['vsproj']: env.vs_srcs = env.vs_srcs + ["platform/windows/" + str(x)] if not os.getenv("VCINSTALLDIR"): - if env["debug_symbols"] == "full" or env["debug_symbols"] == "yes": - env.AddPostAction(binary, make_debug_mingw) + if (env["debug_symbols"] == "full" or env["debug_symbols"] == "yes") and env["separate_debug_symbols"]: + env.AddPostAction(prog, make_debug_mingw) diff --git a/platform/windows/context_gl_win.cpp b/platform/windows/context_gl_win.cpp index 64b6d202a1..d312fbcb12 100644 --- a/platform/windows/context_gl_win.cpp +++ b/platform/windows/context_gl_win.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,29 +27,19 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#if defined(OPENGL_ENABLED) || defined(GLES2_ENABLED) - -// -// C++ Implementation: context_gl_x11 -// -// Description: -// -// + +#if defined(OPENGL_ENABLED) || defined(GLES_ENABLED) + // Author: Juan Linietsky <reduzio@gmail.com>, (C) 2008 -// -// Copyright: See COPYING file that comes with this distribution -// -// #include "context_gl_win.h" -//#include "drivers/opengl/glwrapper.h" -//#include "ctxgl_procaddr.h" - #define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 #define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 #define WGL_CONTEXT_FLAGS_ARB 0x2094 #define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 +#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 +#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 typedef HGLRC(APIENTRY *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC, HGLRC, const int *); @@ -165,7 +155,8 @@ Error ContextGL_Win::initialize() { WGL_CONTEXT_MAJOR_VERSION_ARB, 3, //we want a 3.3 context WGL_CONTEXT_MINOR_VERSION_ARB, 3, //and it shall be forward compatible so that we can only use up to date functionality - WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB | _WGL_CONTEXT_DEBUG_BIT_ARB, + WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, + WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB /*| _WGL_CONTEXT_DEBUG_BIT_ARB*/, 0 }; //zero indicates the end of the array @@ -194,8 +185,6 @@ Error ContextGL_Win::initialize() { MessageBox(NULL, "Can't Activate The GL 3.3 Rendering Context.", "ERROR", MB_OK | MB_ICONEXCLAMATION); return ERR_CANT_CREATE; // Return FALSE } - - printf("Activated GL 3.3 context"); } wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT"); diff --git a/platform/windows/context_gl_win.h b/platform/windows/context_gl_win.h index 0059cbc311..e7578a1aeb 100644 --- a/platform/windows/context_gl_win.h +++ b/platform/windows/context_gl_win.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,18 +27,10 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#if defined(OPENGL_ENABLED) || defined(GLES2_ENABLED) -// -// C++ Interface: context_gl_x11 -// -// Description: -// -// + +#if defined(OPENGL_ENABLED) || defined(GLES_ENABLED) + // Author: Juan Linietsky <reduzio@gmail.com>, (C) 2008 -// -// Copyright: See COPYING file that comes with this distribution -// -// #ifndef CONTEXT_GL_WIN_H #define CONTEXT_GL_WIN_H diff --git a/platform/windows/crash_handler_win.cpp b/platform/windows/crash_handler_win.cpp index 2f5ee7956e..804c2d44eb 100644 --- a/platform/windows/crash_handler_win.cpp +++ b/platform/windows/crash_handler_win.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,8 +27,10 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "main/main.h" #include "os_windows.h" +#include "project_settings.h" #ifdef CRASH_HANDLER_EXCEPTION @@ -60,8 +62,8 @@ class symbol { static const int max_name_len = 1024; public: - symbol(HANDLE process, DWORD64 address) - : sym((sym_type *)::operator new(sizeof(*sym) + max_name_len)) { + symbol(HANDLE process, DWORD64 address) : + sym((sym_type *)::operator new(sizeof(*sym) + max_name_len)) { memset(sym, '\0', sizeof(*sym) + max_name_len); sym->SizeOfStruct = sizeof(*sym); sym->MaxNameLength = max_name_len; @@ -84,8 +86,8 @@ class get_mod_info { HANDLE process; public: - get_mod_info(HANDLE h) - : process(h) {} + get_mod_info(HANDLE h) : + process(h) {} module_data operator()(HMODULE module) { module_data ret; diff --git a/platform/windows/crash_handler_win.h b/platform/windows/crash_handler_win.h index 0b1889e4fe..95b1468197 100644 --- a/platform/windows/crash_handler_win.h +++ b/platform/windows/crash_handler_win.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef CRASH_HANDLER_WIN_H #define CRASH_HANDLER_WIN_H diff --git a/platform/windows/ctxgl_procaddr.cpp b/platform/windows/ctxgl_procaddr.cpp index c0c900acca..434eeea16e 100644 --- a/platform/windows/ctxgl_procaddr.cpp +++ b/platform/windows/ctxgl_procaddr.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifdef OPENGL_ENABLED #include "ctxgl_procaddr.h" #include <GL/gl.h> diff --git a/platform/windows/ctxgl_procaddr.h b/platform/windows/ctxgl_procaddr.h index 61fa6d7fbd..cd229fb8db 100644 --- a/platform/windows/ctxgl_procaddr.h +++ b/platform/windows/ctxgl_procaddr.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef CTXGL_PROCADDR_H #define CTXGL_PROCADDR_H diff --git a/platform/windows/detect.py b/platform/windows/detect.py index bac5df5668..b8ee2cd19f 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -64,8 +64,12 @@ def get_opts(): return [ ('mingw_prefix_32', 'MinGW prefix (Win32)', mingw32), ('mingw_prefix_64', 'MinGW prefix (Win64)', mingw64), - BoolVariable('use_lto', 'Use link time optimization (when using MingW)', False), + # Targeted Windows version: 7 (and later), minimum supported version + # XP support dropped after EOL due to missing API for IPv6 and other issues + # Vista support dropped after EOL due to GH-10243 + ('target_win_version', 'Targeted Windows version, >= 0x0601 (Windows 7)', '0x0601'), EnumVariable('debug_symbols', 'Add debug symbols to release version', 'yes', ('yes', 'no', 'full')), + BoolVariable('separate_debug_symbols', 'Create a separate file with the debug symbols', False), ] @@ -98,11 +102,6 @@ def configure(env): env.Append(CPPPATH=['#platform/windows']) - # Targeted Windows version: 7 (and later), minimum supported version - # XP support dropped after EOL due to missing API for IPv6 and other issues - # Vista support dropped after EOL due to GH-10243 - winver = "0x0601" - if (os.name == "nt" and os.getenv("VCINSTALLDIR")): # MSVC env['ENV']['TMP'] = os.environ['TMP'] @@ -149,11 +148,11 @@ def configure(env): ## 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 + # This detection function needs the tools env (that is env['ENV'], not SCons's env), and that is why it's this far below 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 building for 64bit architecture, disable assembly optimisations for 32 bit builds (theora as of writing)... 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 @@ -176,11 +175,11 @@ def configure(env): env.Append(CCFLAGS=['/DWASAPI_ENABLED']) env.Append(CCFLAGS=['/DTYPED_METHOD_BIND']) env.Append(CCFLAGS=['/DWIN32']) - env.Append(CCFLAGS=['/DWINVER=%s' % winver, '/D_WIN32_WINNT=%s' % winver]) + env.Append(CCFLAGS=['/DWINVER=%s' % env['target_win_version'], '/D_WIN32_WINNT=%s' % env['target_win_version']]) if env["bits"] == "64": env.Append(CCFLAGS=['/D_WIN64']) - LIBS = ['winmm', 'opengl32', 'dsound', 'kernel32', 'ole32', 'oleaut32', 'user32', 'gdi32', 'IPHLPAPI', 'Shlwapi', 'wsock32', 'Ws2_32', 'shell32', 'advapi32', 'dinput8', 'dxguid'] + LIBS = ['winmm', 'opengl32', 'dsound', 'kernel32', 'ole32', 'oleaut32', 'user32', 'gdi32', 'IPHLPAPI', 'Shlwapi', 'wsock32', 'Ws2_32', 'shell32', 'advapi32', 'dinput8', 'dxguid', 'imm32', 'bcrypt'] env.Append(LINKFLAGS=[p + env["LIBSUFFIX"] for p in LIBS]) env.Append(LIBPATH=[os.getenv("WindowsSdkDir") + "/Lib"]) @@ -190,6 +189,14 @@ def configure(env): else: VC_PATH = "" + if (env["use_lto"]): + env.Append(CCFLAGS=['/GL']) + env.Append(ARFLAGS=['/LTCG']) + if env["progress"]: + env.Append(LINKFLAGS=['/LTCG:STATUS']) + else: + env.Append(LINKFLAGS=['/LTCG']) + env.Append(CCFLAGS=["/I" + p for p in os.getenv("INCLUDE").split(";")]) env.Append(LIBPATH=[p for p in os.getenv("LIB").split(";")]) @@ -259,21 +266,22 @@ def configure(env): env['CXX'] = mingw_prefix + "g++" env['AR'] = mingw_prefix + "gcc-ar" env['RANLIB'] = mingw_prefix + "gcc-ranlib" - env['LD'] = mingw_prefix + "g++" + env['LINK'] = mingw_prefix + "g++" env["x86_libtheora_opt_gcc"] = True if env['use_lto']: env.Append(CCFLAGS=['-flto']) env.Append(LINKFLAGS=['-flto=' + str(env.GetOption("num_jobs"))]) + ## Compile flags env.Append(CCFLAGS=['-DWINDOWS_ENABLED', '-mwindows']) env.Append(CCFLAGS=['-DOPENGL_ENABLED']) env.Append(CCFLAGS=['-DRTAUDIO_ENABLED']) env.Append(CCFLAGS=['-DWASAPI_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', 'ksuser']) + env.Append(CCFLAGS=['-DWINVER=%s' % env['target_win_version'], '-D_WIN32_WINNT=%s' % env['target_win_version']]) + env.Append(LIBS=['mingw32', 'opengl32', 'dsound', 'ole32', 'd3d9', 'winmm', 'gdi32', 'iphlpapi', 'shlwapi', 'wsock32', 'ws2_32', 'kernel32', 'oleaut32', 'dinput8', 'dxguid', 'ksuser', 'imm32', 'bcrypt']) env.Append(CPPFLAGS=['-DMINGW_ENABLED']) diff --git a/platform/windows/export/export.cpp b/platform/windows/export/export.cpp index 5301aa9e95..97544c18f5 100644 --- a/platform/windows/export/export.cpp +++ b/platform/windows/export/export.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,12 +27,137 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "editor/editor_export.h" +#include "editor/editor_settings.h" +#include "os/file_access.h" +#include "os/os.h" #include "platform/windows/logo.gen.h" +class EditorExportPlatformWindows : public EditorExportPlatformPC { + +public: + virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0); + virtual void get_export_options(List<ExportOption> *r_options); +}; + +Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { + Error err = EditorExportPlatformPC::export_project(p_preset, p_debug, p_path, p_flags); + + if (err != OK) { + return err; + } + + String rcedit_path = EditorSettings::get_singleton()->get("export/windows/rcedit"); + + if (rcedit_path == String()) { + return OK; + } + + if (!FileAccess::exists(rcedit_path)) { + ERR_PRINTS("Could not find rcedit executable at " + rcedit_path + ", aborting."); + return ERR_FILE_NOT_FOUND; + } + +#ifndef WINDOWS_ENABLED + // On non-Windows we need WINE to run rcedit + String wine_path = EditorSettings::get_singleton()->get("export/windows/wine"); + + if (wine_path != String() && !FileAccess::exists(wine_path)) { + ERR_PRINTS("Could not find wine executable at " + wine_path + ", aborting."); + return ERR_FILE_NOT_FOUND; + } + + if (wine_path == String()) { + wine_path = "wine"; // try to run wine from PATH + } +#endif + + String icon_path = p_preset->get("application/icon"); + String file_verion = p_preset->get("application/file_version"); + String product_version = p_preset->get("application/product_version"); + String company_name = p_preset->get("application/company_name"); + String product_name = p_preset->get("application/product_name"); + String file_description = p_preset->get("application/file_description"); + String copyright = p_preset->get("application/copyright"); + String trademarks = p_preset->get("application/trademarks"); + String comments = p_preset->get("application/comments"); + + List<String> args; + args.push_back(p_path); + if (icon_path != String()) { + args.push_back("--set-icon"); + args.push_back(icon_path); + } + if (file_verion != String()) { + args.push_back("--set-file-version"); + args.push_back(file_verion); + } + if (product_version != String()) { + args.push_back("--set-product-version"); + args.push_back(product_version); + } + if (company_name != String()) { + args.push_back("--set-version-string"); + args.push_back("CompanyName"); + args.push_back(company_name); + } + if (product_name != String()) { + args.push_back("--set-version-string"); + args.push_back("ProductName"); + args.push_back(product_name); + } + if (file_description != String()) { + args.push_back("--set-version-string"); + args.push_back("FileDescription"); + args.push_back(file_description); + } + if (copyright != String()) { + args.push_back("--set-version-string"); + args.push_back("LegalCopyright"); + args.push_back(copyright); + } + if (trademarks != String()) { + args.push_back("--set-version-string"); + args.push_back("LegalTrademarks"); + args.push_back(trademarks); + } + +#ifdef WINDOWS_ENABLED + OS::get_singleton()->execute(rcedit_path, args, true); +#else + // On non-Windows we need WINE to run rcedit + args.push_front(rcedit_path); + OS::get_singleton()->execute(wine_path, args, true); +#endif + + return OK; +} + +void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_options) { + EditorExportPlatformPC::get_export_options(r_options); + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_GLOBAL_FILE, "*.ico"), String())); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_version"), String())); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_version"), String())); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/company_name"), String())); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_name"), String())); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_description"), String())); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), String())); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/trademarks"), String())); +} + void register_windows_exporter() { - Ref<EditorExportPlatformPC> platform; + EDITOR_DEF("export/windows/rcedit", ""); + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/windows/rcedit", PROPERTY_HINT_GLOBAL_FILE, "*.exe")); +#ifndef WINDOWS_ENABLED + // On non-Windows we need WINE to run rcedit + EDITOR_DEF("export/windows/wine", ""); + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/windows/wine", PROPERTY_HINT_GLOBAL_FILE)); +#endif + + Ref<EditorExportPlatformWindows> platform; platform.instance(); Ref<Image> img = memnew(Image(_windows_logo)); diff --git a/platform/windows/export/export.h b/platform/windows/export/export.h index cc85339312..7f62e8955c 100644 --- a/platform/windows/export/export.h +++ b/platform/windows/export/export.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,4 +27,5 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + void register_windows_exporter(); diff --git a/platform/windows/godot_res.rc b/platform/windows/godot_res.rc index b86869d316..f2dca10d55 100644 --- a/platform/windows/godot_res.rc +++ b/platform/windows/godot_res.rc @@ -3,11 +3,9 @@ #define _STR(m_x) #m_x #define _MKSTR(m_x) _STR(m_x) #endif + #ifndef VERSION_PATCH #define VERSION_PATCH 0 -#define PATCH_STRING -#else -#define PATCH_STRING "." _MKSTR(VERSION_PATCH) #endif GODOT_ICON ICON platform/windows/godot.ico @@ -23,13 +21,13 @@ BEGIN BLOCK "040904b0" BEGIN VALUE "CompanyName", "Godot Engine" - VALUE "FileDescription", _MKSTR(VERSION_NAME) " Editor" - VALUE "FileVersion", _MKSTR(VERSION_MAJOR) "." _MKSTR(VERSION_MINOR) "." _MKSTR(VERSION_PATCH) - VALUE "ProductName", _MKSTR(VERSION_NAME) + VALUE "FileDescription", VERSION_NAME " Editor" + VALUE "FileVersion", VERSION_NUMBER + VALUE "ProductName", VERSION_NAME VALUE "Licence", "MIT" - VALUE "LegalCopyright", "Copyright (c) 2007-" _MKSTR(VERSION_YEAR) " Juan Linietsky, Ariel Manzur" - VALUE "Info", "http://www.godotengine.org" - VALUE "ProductVersion", _MKSTR(VERSION_MAJOR) "." _MKSTR(VERSION_MINOR) PATCH_STRING "." _MKSTR(VERSION_REVISION) + VALUE "LegalCopyright", "Copyright (c) 2007-" _MKSTR(VERSION_YEAR) " Juan Linietsky, Ariel Manzur and contributors" + VALUE "Info", "https://godotengine.org" + VALUE "ProductVersion", VERSION_FULL_BUILD END END BLOCK "VarFileInfo" diff --git a/platform/windows/godot_win.cpp b/platform/windows/godot_win.cpp index cff2cbad42..80f53dd1a1 100644 --- a/platform/windows/godot_win.cpp +++ b/platform/windows/godot_win.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "main/main.h" #include "os_windows.h" #include <locale.h> diff --git a/platform/windows/joypad.cpp b/platform/windows/joypad.cpp index 0c7358f499..796531fe24 100644 --- a/platform/windows/joypad.cpp +++ b/platform/windows/joypad.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "joypad.h" #include <oleauto.h> #include <wbemidl.h> diff --git a/platform/windows/joypad.h b/platform/windows/joypad.h index a76caf6fac..0d14480733 100644 --- a/platform/windows/joypad.h +++ b/platform/windows/joypad.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef JOYPAD_H #define JOYPAD_H diff --git a/platform/windows/key_mapping_win.cpp b/platform/windows/key_mapping_win.cpp index 76bb5d5723..69dd385354 100644 --- a/platform/windows/key_mapping_win.cpp +++ b/platform/windows/key_mapping_win.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "key_mapping_win.h" #include <stdio.h> diff --git a/platform/windows/key_mapping_win.h b/platform/windows/key_mapping_win.h index 139f047d17..8d6461f27d 100644 --- a/platform/windows/key_mapping_win.h +++ b/platform/windows/key_mapping_win.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef KEY_MAPPING_WINDOWS_H #define KEY_MAPPING_WINDOWS_H diff --git a/platform/windows/lang_table.h b/platform/windows/lang_table.h index d54894afce..1a966b502a 100644 --- a/platform/windows/lang_table.h +++ b/platform/windows/lang_table.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef LANG_TABLE_H #define LANG_TABLE_H diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index ac78dddf0c..13fe781ff3 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -30,24 +30,25 @@ #include "os_windows.h" +#include "drivers/gles2/rasterizer_gles2.h" #include "drivers/gles3/rasterizer_gles3.h" #include "drivers/windows/dir_access_windows.h" #include "drivers/windows/file_access_windows.h" #include "drivers/windows/mutex_windows.h" +#include "drivers/windows/packet_peer_udp_winsock.h" #include "drivers/windows/rw_lock_windows.h" #include "drivers/windows/semaphore_windows.h" +#include "drivers/windows/stream_peer_tcp_winsock.h" +#include "drivers/windows/tcp_server_winsock.h" #include "drivers/windows/thread_windows.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" -#include "stream_peer_winsock.h" -#include "tcp_server_winsock.h" +#include "version_generated.gen.h" #include "windows_terminal_logger.h" #include <process.h> @@ -69,6 +70,19 @@ __attribute__((visibility("default"))) DWORD NvOptimusEnablement = 0x00000001; #define WM_TOUCH 576 #endif +static String format_error_message(DWORD id) { + + LPWSTR messageBuffer = NULL; + size_t size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&messageBuffer, 0, NULL); + + String msg = "Error " + itos(id) + ": " + String(messageBuffer, size); + + LocalFree(messageBuffer); + + return msg; +} + extern HINSTANCE godot_hinstance; void RedirectIOToConsole() { @@ -138,31 +152,6 @@ void RedirectIOToConsole() { // point to console as well } -int OS_Windows::get_video_driver_count() const { - - return 1; -} -const char *OS_Windows::get_video_driver_name(int p_driver) const { - - return "GLES2"; -} - -OS::VideoMode OS_Windows::get_default_video_mode() const { - - return VideoMode(1024, 600, false); -} - -int OS_Windows::get_audio_driver_count() const { - - return AudioDriverManager::get_driver_count(); -} -const char *OS_Windows::get_audio_driver_name(int p_driver) const { - - AudioDriver *driver = AudioDriverManager::get_driver(p_driver); - ERR_FAIL_COND_V(!driver, ""); - return AudioDriverManager::get_driver(p_driver)->get_name(); -} - void OS_Windows::initialize_core() { crash_handler.initialize(); @@ -188,7 +177,7 @@ void OS_Windows::initialize_core() { DirAccess::make_default<DirAccessWindows>(DirAccess::ACCESS_FILESYSTEM); TCPServerWinsock::make_default(); - StreamPeerWinsock::make_default(); + StreamPeerTCPWinsock::make_default(); PacketPeerUDPWinsock::make_default(); // We need to know how often the clock is updated @@ -206,15 +195,6 @@ void OS_Windows::initialize_core() { cursor_shape = CURSOR_ARROW; } -void OS_Windows::initialize_logger() { - Vector<Logger *> loggers; - loggers.push_back(memnew(WindowsTerminalLogger)); - // FIXME: Reenable once we figure out how to get this properly in user:// - // instead of littering the user's working dirs (res:// + pwd) with log files (GH-12277) - //loggers.push_back(memnew(RotatedFileLogger("user://logs/log.txt"))); - _set_logger(memnew(CompositeLogger(loggers))); -} - bool OS_Windows::can_draw() const { return !minimized; @@ -224,7 +204,17 @@ bool OS_Windows::can_draw() const { #define SIGNATURE_MASK 0xFFFFFF00 #define IsPenEvent(dw) (((dw)&SIGNATURE_MASK) == MI_WP_SIGNATURE) -void OS_Windows::_touch_event(bool p_pressed, int p_x, int p_y, int idx) { +void OS_Windows::_touch_event(bool p_pressed, float p_x, float p_y, int idx) { + + // Defensive + if (touch_state.has(idx) == p_pressed) + return; + + if (p_pressed) { + touch_state.insert(idx, Vector2(p_x, p_y)); + } else { + touch_state.erase(idx); + } Ref<InputEventScreenTouch> event; event.instance(); @@ -237,7 +227,17 @@ void OS_Windows::_touch_event(bool p_pressed, int p_x, int p_y, int idx) { } }; -void OS_Windows::_drag_event(int p_x, int p_y, int idx) { +void OS_Windows::_drag_event(float p_x, float p_y, int idx) { + + Map<int, Vector2>::Element *curr = touch_state.find(idx); + // Defensive + if (!curr) + return; + + if (curr->get() == Vector2(p_x, p_y)) + return; + + curr->get() = Vector2(p_x, p_y); Ref<InputEventScreenDrag> event; event.instance(); @@ -267,6 +267,13 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED) { ReleaseCapture(); } + + // Release every touch to avoid sticky points + for (Map<int, Vector2>::Element *E = touch_state.front(); E; E = E->next()) { + _touch_event(false, E->get().x, E->get().y, E->key()); + } + touch_state.clear(); + break; } case WM_ACTIVATE: // Watch For Window Activate Message @@ -390,6 +397,7 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) /*mm->get_button_mask()|=(wParam&MK_XBUTTON1)?(1<<5):0; mm->get_button_mask()|=(wParam&MK_XBUTTON2)?(1<<6):0;*/ mm->set_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); + mm->set_global_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); if (mouse_mode == MOUSE_MODE_CAPTURED) { @@ -435,6 +443,7 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) case WM_MOUSEWHEEL: case WM_MOUSEHWHEEL: case WM_LBUTTONDBLCLK: + case WM_MBUTTONDBLCLK: case WM_RBUTTONDBLCLK: /*case WM_XBUTTONDOWN: case WM_XBUTTONUP: */ { @@ -493,6 +502,12 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) mb->set_button_index(2); mb->set_doubleclick(true); } break; + case WM_MBUTTONDBLCLK: { + + mb->set_pressed(true); + mb->set_button_index(3); + mb->set_doubleclick(true); + } break; case WM_MOUSEWHEEL: { mb->set_pressed(true); @@ -554,7 +569,7 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) mb->set_position(Vector2(old_x, old_y)); } - if (uMsg != WM_MOUSEWHEEL) { + if (uMsg != WM_MOUSEWHEEL && uMsg != WM_MOUSEHWHEEL) { if (mb->is_pressed()) { if (++pressrc > 0) @@ -598,7 +613,16 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) video_mode.width = window_w; video_mode.height = window_h; } - //return 0; // Jump Back + if (wParam == SIZE_MAXIMIZED) { + maximized = true; + minimized = false; + } else if (wParam == SIZE_MINIMIZED) { + maximized = false; + minimized = true; + } else if (wParam == SIZE_RESTORED) { + maximized = false; + minimized = false; + } } break; case WM_ENTERSIZEMOVE: { @@ -670,7 +694,6 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) print_line("input lang change"); } break; -#if WINVER >= 0x0601 // for windows 7 case WM_TOUCH: { BOOL bHandled = FALSE; @@ -683,10 +706,10 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) //do something with each touch input entry if (ti.dwFlags & TOUCHEVENTF_MOVE) { - _drag_event(ti.x / 100, ti.y / 100, ti.dwID); + _drag_event(ti.x / 100.0f, ti.y / 100.0f, ti.dwID); } else if (ti.dwFlags & (TOUCHEVENTF_UP | TOUCHEVENTF_DOWN)) { - _touch_event(ti.dwFlags & TOUCHEVENTF_DOWN != 0, ti.x / 100, ti.y / 100, ti.dwID); + _touch_event(ti.dwFlags & TOUCHEVENTF_DOWN, ti.x / 100.0f, ti.y / 100.0f, ti.dwID); }; } bHandled = TRUE; @@ -704,7 +727,6 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) } break; -#endif case WM_DEVICECHANGE: { joypad->probe_joypads(); @@ -905,7 +927,7 @@ typedef enum _SHC_PROCESS_DPI_AWARENESS { SHC_PROCESS_PER_MONITOR_DPI_AWARE = 2 } SHC_PROCESS_DPI_AWARENESS; -void OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { +Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { main_loop = NULL; outside = true; @@ -951,7 +973,7 @@ void OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int if (!RegisterClassExW(&wc)) { MessageBox(NULL, "Failed To Register The Window Class.", "ERROR", MB_OK | MB_ICONEXCLAMATION); - return; // Return + return ERR_UNAVAILABLE; } pre_fs_valid = true; @@ -1021,7 +1043,7 @@ void OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int RECT rect; if (!GetClientRect(hWnd, &rect)) { MessageBoxW(NULL, L"Window Creation Error.", L"ERROR", MB_OK | MB_ICONEXCLAMATION); - return; // Return FALSE + return ERR_UNAVAILABLE; }; video_mode.width = rect.right; video_mode.height = rect.bottom; @@ -1039,17 +1061,30 @@ void OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int NULL, NULL, hInstance, NULL); if (!hWnd) { MessageBoxW(NULL, L"Window Creation Error.", L"ERROR", MB_OK | MB_ICONEXCLAMATION); - return; // Return FALSE + return ERR_UNAVAILABLE; } }; + if (video_mode.always_on_top) { + SetWindowPos(hWnd, video_mode.always_on_top ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + } + #if defined(OPENGL_ENABLED) - gl_context = memnew(ContextGL_Win(hWnd, true)); - gl_context->initialize(); + if (p_video_driver == VIDEO_DRIVER_GLES2) { + gl_context = memnew(ContextGL_Win(hWnd, false)); + gl_context->initialize(); + + RasterizerGLES2::register_config(); + RasterizerGLES2::make_current(); + } else { + gl_context = memnew(ContextGL_Win(hWnd, true)); + gl_context->initialize(); - RasterizerGLES3::register_config(); + RasterizerGLES3::register_config(); + RasterizerGLES3::make_current(); + } - RasterizerGLES3::make_current(); + gl_context->set_use_vsync(video_mode.use_vsync); #endif visual_server = memnew(VisualServerRaster); @@ -1058,18 +1093,6 @@ void OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int visual_server = memnew(VisualServerWrapMT(visual_server, get_render_thread_mode() == RENDER_SEPARATE_THREAD)); } - physics_server = memnew(PhysicsServerSW); - physics_server->init(); - - physics_2d_server = Physics2DServerWrapMT::init_server<Physics2DServerSW>(); - physics_2d_server->init(); - - if (!is_no_window_mode_enabled()) { - ShowWindow(hWnd, SW_SHOW); // Show The Window - SetForegroundWindow(hWnd); // Slightly Higher Priority - SetFocus(hWnd); // Sets Keyboard Focus To - } - /* DEVMODE dmScreenSettings; // Device Mode memset(&dmScreenSettings,0,sizeof(dmScreenSettings)); // Makes Sure Memory's Cleared @@ -1100,13 +1123,21 @@ void OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int tme.dwHoverTime = HOVER_DEFAULT; TrackMouseEvent(&tme); - //RegisterTouchWindow(hWnd, 0); // Windows 7 + RegisterTouchWindow(hWnd, 0); - _ensure_data_dir(); + _ensure_user_data_dir(); DragAcceptFiles(hWnd, true); move_timer_id = 1; + + if (!is_no_window_mode_enabled()) { + ShowWindow(hWnd, SW_SHOW); // Show The Window + SetForegroundWindow(hWnd); // Slightly Higher Priority + SetFocus(hWnd); // Sets Keyboard Focus To + } + + return OK; } void OS_Windows::set_clipboard(const String &p_text) { @@ -1208,6 +1239,7 @@ void OS_Windows::finalize() { memdelete(joypad); memdelete(input); + touch_state.clear(); visual_server->finish(); memdelete(visual_server); @@ -1225,12 +1257,6 @@ void OS_Windows::finalize() { memdelete(debugger_connection_console); } */ - - physics_server->finish(); - memdelete(physics_server); - - physics_2d_server->finish(); - memdelete(physics_2d_server); } void OS_Windows::finalize_core() { @@ -1238,7 +1264,7 @@ void OS_Windows::finalize_core() { memdelete(process_map); TCPServerWinsock::cleanup(); - StreamPeerWinsock::cleanup(); + StreamPeerTCPWinsock::cleanup(); } void OS_Windows::alert(const String &p_alert, const String &p_title) { @@ -1462,6 +1488,12 @@ Size2 OS_Windows::get_window_size() const { GetClientRect(hWnd, &r); return Vector2(r.right - r.left, r.bottom - r.top); } +Size2 OS_Windows::get_real_window_size() const { + + RECT r; + GetWindowRect(hWnd, &r); + return Vector2(r.right - r.left, r.bottom - r.top); +} void OS_Windows::set_window_size(const Size2 p_size) { video_mode.width = p_size.width; @@ -1583,7 +1615,20 @@ bool OS_Windows::is_window_maximized() const { return maximized; } -void OS_Windows::set_borderless_window(int p_borderless) { +void OS_Windows::set_window_always_on_top(bool p_enabled) { + if (video_mode.always_on_top == p_enabled) + return; + + video_mode.always_on_top = p_enabled; + + _update_window_style(); +} + +bool OS_Windows::is_window_always_on_top() const { + return video_mode.always_on_top; +} + +void OS_Windows::set_borderless_window(bool p_borderless) { if (video_mode.borderless_window == p_borderless) return; @@ -1607,6 +1652,8 @@ void OS_Windows::_update_window_style(bool repaint) { } } + SetWindowPos(hWnd, video_mode.always_on_top ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + if (repaint) { RECT rect; GetWindowRect(hWnd, &rect); @@ -1614,10 +1661,36 @@ void OS_Windows::_update_window_style(bool repaint) { } } -Error OS_Windows::open_dynamic_library(const String p_path, void *&p_library_handle) { - p_library_handle = (void *)LoadLibrary(p_path.utf8().get_data()); +Error OS_Windows::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) { + + String path = p_path; + + if (!FileAccess::exists(path)) { + //this code exists so gdnative can load .dll files from within the executable path + path = get_executable_path().get_base_dir().plus_file(p_path.get_file()); + } + + typedef DLL_DIRECTORY_COOKIE(WINAPI * PAddDllDirectory)(PCWSTR); + typedef BOOL(WINAPI * PRemoveDllDirectory)(DLL_DIRECTORY_COOKIE); + + PAddDllDirectory add_dll_directory = (PAddDllDirectory)GetProcAddress(GetModuleHandle("kernel32.dll"), "AddDllDirectory"); + PRemoveDllDirectory remove_dll_directory = (PRemoveDllDirectory)GetProcAddress(GetModuleHandle("kernel32.dll"), "RemoveDllDirectory"); + + bool has_dll_directory_api = ((add_dll_directory != NULL) && (remove_dll_directory != NULL)); + DLL_DIRECTORY_COOKIE cookie = NULL; + + if (p_also_set_library_path && has_dll_directory_api) { + cookie = add_dll_directory(path.get_base_dir().c_str()); + } + + p_library_handle = (void *)LoadLibraryExW(path.c_str(), NULL, (p_also_set_library_path && has_dll_directory_api) ? LOAD_LIBRARY_SEARCH_DEFAULT_DIRS : 0); + + if (cookie) { + remove_dll_directory(cookie); + } + if (!p_library_handle) { - ERR_EXPLAIN("Can't open dynamic library: " + p_path + ". Error: " + String::num(GetLastError())); + ERR_EXPLAIN("Can't open dynamic library: " + p_path + ". Error: " + format_error_message(GetLastError())); ERR_FAIL_V(ERR_CANT_OPEN); } return OK; @@ -1810,10 +1883,125 @@ void OS_Windows::set_cursor_shape(CursorShape p_shape) { IDC_HELP }; - SetCursor(LoadCursor(hInstance, win_cursors[p_shape])); + if (cursors[p_shape] != NULL) { + SetCursor(cursors[p_shape]); + } else { + SetCursor(LoadCursor(hInstance, win_cursors[p_shape])); + } cursor_shape = p_shape; } +void OS_Windows::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { + if (p_cursor.is_valid()) { + Ref<Texture> texture = p_cursor; + Ref<Image> image = texture->get_data(); + + UINT image_size = 32 * 32; + UINT size = sizeof(UINT) * image_size; + + ERR_FAIL_COND(texture->get_width() != 32 || texture->get_height() != 32); + + // Create the BITMAP with alpha channel + COLORREF *buffer = (COLORREF *)malloc(sizeof(COLORREF) * image_size); + + image->lock(); + for (UINT index = 0; index < image_size; index++) { + int column_index = floor(index / 32); + int row_index = index % 32; + + Color pcColor = image->get_pixel(row_index, column_index); + *(buffer + index) = image->get_pixel(row_index, column_index).to_argb32(); + } + image->unlock(); + + // Using 4 channels, so 4 * 8 bits + HBITMAP bitmap = CreateBitmap(32, 32, 1, 4 * 8, buffer); + COLORREF clrTransparent = -1; + + // Create the AND and XOR masks for the bitmap + HBITMAP hAndMask = NULL; + HBITMAP hXorMask = NULL; + + GetMaskBitmaps(bitmap, clrTransparent, hAndMask, hXorMask); + + if (NULL == hAndMask || NULL == hXorMask) { + return; + } + + // Finally, create the icon + ICONINFO iconinfo = { 0 }; + iconinfo.fIcon = FALSE; + iconinfo.xHotspot = p_hotspot.x; + iconinfo.yHotspot = p_hotspot.y; + iconinfo.hbmMask = hAndMask; + iconinfo.hbmColor = hXorMask; + + cursors[p_shape] = CreateIconIndirect(&iconinfo); + + if (p_shape == CURSOR_ARROW) { + SetCursor(cursors[p_shape]); + } + + if (hAndMask != NULL) { + DeleteObject(hAndMask); + } + + if (hXorMask != NULL) { + DeleteObject(hXorMask); + } + } +} + +void OS_Windows::GetMaskBitmaps(HBITMAP hSourceBitmap, COLORREF clrTransparent, OUT HBITMAP &hAndMaskBitmap, OUT HBITMAP &hXorMaskBitmap) { + + // Get the system display DC + HDC hDC = GetDC(NULL); + + // Create helper DC + HDC hMainDC = CreateCompatibleDC(hDC); + HDC hAndMaskDC = CreateCompatibleDC(hDC); + HDC hXorMaskDC = CreateCompatibleDC(hDC); + + // Get the dimensions of the source bitmap + BITMAP bm; + GetObject(hSourceBitmap, sizeof(BITMAP), &bm); + + // Create the mask bitmaps + hAndMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight); // color + hXorMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight); // color + + // Release the system display DC + ReleaseDC(NULL, hDC); + + // Select the bitmaps to helper DC + HBITMAP hOldMainBitmap = (HBITMAP)SelectObject(hMainDC, hSourceBitmap); + HBITMAP hOldAndMaskBitmap = (HBITMAP)SelectObject(hAndMaskDC, hAndMaskBitmap); + HBITMAP hOldXorMaskBitmap = (HBITMAP)SelectObject(hXorMaskDC, hXorMaskBitmap); + + // Assign the monochrome AND mask bitmap pixels so that a pixels of the source bitmap + // with 'clrTransparent' will be white pixels of the monochrome bitmap + SetBkColor(hMainDC, clrTransparent); + BitBlt(hAndMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hMainDC, 0, 0, SRCCOPY); + + // Assign the color XOR mask bitmap pixels so that a pixels of the source bitmap + // with 'clrTransparent' will be black and rest the pixels same as corresponding + // pixels of the source bitmap + SetBkColor(hXorMaskDC, RGB(0, 0, 0)); + SetTextColor(hXorMaskDC, RGB(255, 255, 255)); + BitBlt(hXorMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hAndMaskDC, 0, 0, SRCCOPY); + BitBlt(hXorMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hMainDC, 0, 0, SRCAND); + + // Deselect bitmaps from the helper DC + SelectObject(hMainDC, hOldMainBitmap); + SelectObject(hAndMaskDC, hOldAndMaskBitmap); + SelectObject(hXorMaskDC, hOldXorMaskBitmap); + + // Delete the helper DC + DeleteDC(hXorMaskDC); + DeleteDC(hAndMaskDC); + DeleteDC(hMainDC); +} + Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id, String *r_pipe, int *r_exitcode, bool read_stderr) { if (p_blocking && r_pipe) { @@ -1869,7 +2057,7 @@ Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments, modstr.resize(cmdline.size()); for (int i = 0; i < cmdline.size(); i++) modstr[i] = cmdline[i]; - int ret = CreateProcessW(NULL, modstr.ptr(), NULL, NULL, 0, NORMAL_PRIORITY_CLASS, NULL, NULL, si_w, &pi.pi); + int ret = CreateProcessW(NULL, modstr.ptrw(), NULL, NULL, 0, NORMAL_PRIORITY_CLASS, NULL, NULL, si_w, &pi.pi); ERR_FAIL_COND_V(ret == 0, ERR_CANT_FORK); if (p_blocking) { @@ -1980,7 +2168,7 @@ void OS_Windows::set_icon(const Ref<Image> &p_icon) { bool OS_Windows::has_environment(const String &p_var) const { - return getenv(p_var.utf8().get_data()) != NULL; + return _wgetenv(p_var.c_str()) != NULL; }; String OS_Windows::get_environment(const String &p_var) const { @@ -2045,6 +2233,36 @@ String OS_Windows::get_locale() const { return "en"; } +// We need this because GetSystemInfo() is unreliable on WOW64 +// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms724381(v=vs.85).aspx +// Taken from MSDN +typedef BOOL(WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL); +LPFN_ISWOW64PROCESS fnIsWow64Process; + +BOOL is_wow64() { + BOOL wow64 = FALSE; + + fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandle(TEXT("kernel32")), "IsWow64Process"); + + if (fnIsWow64Process) { + if (!fnIsWow64Process(GetCurrentProcess(), &wow64)) { + wow64 = FALSE; + } + } + + return wow64; +} + +int OS_Windows::get_processor_count() const { + SYSTEM_INFO sysinfo; + if (is_wow64()) + GetNativeSystemInfo(&sysinfo); + else + GetSystemInfo(&sysinfo); + + return sysinfo.dwNumberOfProcessors; +} + OS::LatinKeyboardVariant OS_Windows::get_latin_keyboard_variant() const { unsigned long azerty[] = { @@ -2122,6 +2340,10 @@ void OS_Windows::swap_buffers() { gl_context->swap_buffers(); } +void OS_Windows::force_process_input() { + process_events(); // get rid of pending events +} + void OS_Windows::run() { if (!main_loop) @@ -2149,6 +2371,43 @@ MainLoop *OS_Windows::get_main_loop() const { return main_loop; } +String OS_Windows::get_config_path() const { + + if (has_environment("XDG_CONFIG_HOME")) { // unlikely, but after all why not? + return get_environment("XDG_CONFIG_HOME"); + } else if (has_environment("APPDATA")) { + return get_environment("APPDATA"); + } else { + return "."; + } +} + +String OS_Windows::get_data_path() const { + + if (has_environment("XDG_DATA_HOME")) { + return get_environment("XDG_DATA_HOME"); + } else { + return get_config_path(); + } +} + +String OS_Windows::get_cache_path() const { + + if (has_environment("XDG_CACHE_HOME")) { + return get_environment("XDG_CACHE_HOME"); + } else if (has_environment("TEMP")) { + return get_environment("TEMP"); + } else { + return get_config_path(); + } +} + +// Get properly capitalized engine name for system paths +String OS_Windows::get_godot_dir_name() const { + + return String(VERSION_SHORT_NAME).capitalize(); +} + String OS_Windows::get_system_dir(SystemDir p_dir) const { int id; @@ -2185,24 +2444,44 @@ String OS_Windows::get_system_dir(SystemDir p_dir) const { ERR_FAIL_COND_V(res != S_OK, String()); return String(szPath); } -String OS_Windows::get_data_dir() const { - - String an = get_safe_application_name(); - if (an != "") { - if (has_environment("APPDATA")) { +String OS_Windows::get_user_data_dir() const { - 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 - return (OS::get_singleton()->get_environment("APPDATA") + "/Godot/app_userdata/" + an).replace("\\", "/"); + String appname = get_safe_dir_name(ProjectSettings::get_singleton()->get("application/config/name")); + if (appname != "") { + bool use_custom_dir = ProjectSettings::get_singleton()->get("application/config/use_custom_user_dir"); + if (use_custom_dir) { + String custom_dir = get_safe_dir_name(ProjectSettings::get_singleton()->get("application/config/custom_user_dir_name"), true); + if (custom_dir == "") { + custom_dir = appname; + } + return get_data_path().plus_file(custom_dir).replace("\\", "/"); + } else { + return get_data_path().plus_file(get_godot_dir_name()).plus_file("app_userdata").plus_file(appname).replace("\\", "/"); } } return ProjectSettings::get_singleton()->get_resource_path(); } +String OS_Windows::get_unique_id() const { + + HW_PROFILE_INFO HwProfInfo; + ERR_FAIL_COND_V(!GetCurrentHwProfile(&HwProfInfo), ""); + return String(HwProfInfo.szHwProfileGuid); +} + +void OS_Windows::set_ime_position(const Point2 &p_pos) { + + HIMC himc = ImmGetContext(hWnd); + COMPOSITIONFORM cps; + cps.dwStyle = CFS_FORCE_POSITION; + cps.ptCurrentPos.x = p_pos.x; + cps.ptCurrentPos.y = p_pos.y; + ImmSetCompositionWindow(himc, &cps); + ImmReleaseContext(hWnd, himc); +} + bool OS_Windows::is_joy_known(int p_device) { return input->is_joy_mapped(p_device); } @@ -2211,19 +2490,19 @@ String OS_Windows::get_joy_guid(int p_device) const { return input->get_joy_guid_remapped(p_device); } -void OS_Windows::set_use_vsync(bool p_enable) { +void OS_Windows::_set_use_vsync(bool p_enable) { if (gl_context) gl_context->set_use_vsync(p_enable); } - +/* bool OS_Windows::is_vsync_enabled() const { if (gl_context) return gl_context->is_using_vsync(); return true; -} +}*/ OS::PowerState OS_Windows::get_power_state() { return power_manager->get_power_state(); @@ -2307,7 +2586,9 @@ OS_Windows::OS_Windows(HINSTANCE _hInstance) { AudioDriverManager::add_driver(&driver_xaudio2); #endif - _set_logger(memnew(WindowsTerminalLogger)); + Vector<Logger *> loggers; + loggers.push_back(memnew(WindowsTerminalLogger)); + _set_logger(memnew(CompositeLogger(loggers))); } OS_Windows::~OS_Windows() { diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index c0b8dfc691..3d13627bfa 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,10 +27,11 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef OS_WINDOWS_H #define OS_WINDOWS_H - #include "context_gl_win.h" +#include "core/project_settings.h" #include "crash_handler_win.h" #include "drivers/rtaudio/audio_driver_rtaudio.h" #include "drivers/wasapi/audio_driver_wasapi.h" @@ -38,7 +39,6 @@ #include "os/os.h" #include "power_windows.h" #include "servers/audio_server.h" -#include "servers/physics/physics_server_sw.h" #include "servers/visual/rasterizer.h" #include "servers/visual_server.h" #ifdef XAUDIO2_ENABLED @@ -47,8 +47,6 @@ #include "drivers/unix/ip_unix.h" #include "key_mapping_win.h" #include "main/input_default.h" -#include "servers/physics_2d/physics_2d_server_sw.h" -#include "servers/physics_2d/physics_2d_server_wrap_mt.h" #include <fcntl.h> #include <io.h> @@ -90,8 +88,6 @@ class OS_Windows : public OS { ContextGL_Win *gl_context; #endif VisualServer *visual_server; - PhysicsServer *physics_server; - Physics2DServer *physics_2d_server; int pressrc; HDC hDC; // Private GDI Device Context HINSTANCE hInstance; // Holds The Instance Of The Application @@ -118,10 +114,12 @@ class OS_Windows : public OS { bool window_has_focus; uint32_t last_button_state; + HCURSOR cursors[CURSOR_MAX] = { NULL }; CursorShape cursor_shape; InputDefault *input; JoypadWindows *joypad; + Map<int, Vector2> touch_state; PowerWindows *power_manager; @@ -137,24 +135,15 @@ class OS_Windows : public OS { CrashHandler crash_handler; - void _drag_event(int p_x, int p_y, int idx); - void _touch_event(bool p_pressed, int p_x, int p_y, int idx); + void _drag_event(float p_x, float p_y, int idx); + void _touch_event(bool p_pressed, float p_x, float 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; - virtual const char *get_video_driver_name(int p_driver) const; - - virtual VideoMode get_default_video_mode() const; - - virtual int get_audio_driver_count() const; - virtual const char *get_audio_driver_name(int p_driver) const; - - virtual void initialize_logger(); virtual void initialize_core(); - virtual void initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver); + virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver); virtual void set_main_loop(MainLoop *p_main_loop); virtual void delete_main_loop(); @@ -206,6 +195,7 @@ public: virtual Point2 get_window_position() const; virtual void set_window_position(const Point2 &p_position); virtual Size2 get_window_size() const; + virtual Size2 get_real_window_size() const; virtual void set_window_size(const Size2 p_size); virtual void set_window_fullscreen(bool p_enabled); virtual bool is_window_fullscreen() const; @@ -215,12 +205,14 @@ public: virtual bool is_window_minimized() const; virtual void set_window_maximized(bool p_enabled); virtual bool is_window_maximized() const; + virtual void set_window_always_on_top(bool p_enabled); + virtual bool is_window_always_on_top() const; virtual void request_attention(); - virtual void set_borderless_window(int p_borderless); + virtual void set_borderless_window(bool p_borderless); virtual bool get_borderless_window(); - virtual Error open_dynamic_library(const String p_path, void *&p_library_handle); + virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false); 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, bool p_optional = false); @@ -251,17 +243,32 @@ public: virtual String get_clipboard() const; void set_cursor_shape(CursorShape p_shape); + virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); + void GetMaskBitmaps(HBITMAP hSourceBitmap, COLORREF clrTransparent, OUT HBITMAP &hAndMaskBitmap, OUT HBITMAP &hXorMaskBitmap); void set_icon(const Ref<Image> &p_icon); virtual String get_executable_path() const; virtual String get_locale() const; + + virtual int get_processor_count() const; + virtual LatinKeyboardVariant get_latin_keyboard_variant() const; virtual void enable_for_stealing_focus(ProcessID pid); virtual void move_window_to_foreground(); - virtual String get_data_dir() const; + + virtual String get_config_path() const; + virtual String get_data_path() const; + virtual String get_cache_path() const; + virtual String get_godot_dir_name() const; + virtual String get_system_dir(SystemDir p_dir) const; + virtual String get_user_data_dir() const; + + virtual String get_unique_id() const; + + virtual void set_ime_position(const Point2 &p_pos); virtual void release_rendering_thread(); virtual void make_rendering_thread(); @@ -276,8 +283,8 @@ public: virtual bool is_joy_known(int p_device); virtual String get_joy_guid(int p_device) const; - virtual void set_use_vsync(bool p_enable); - virtual bool is_vsync_enabled() const; + virtual void _set_use_vsync(bool p_enable); + //virtual bool is_vsync_enabled() const; virtual OS::PowerState get_power_state(); virtual int get_power_seconds_left(); @@ -288,6 +295,8 @@ public: void disable_crash_handler(); bool is_disable_crash_handler() const; + void force_process_input(); + virtual Error move_to_trash(const String &p_path); OS_Windows(HINSTANCE _hInstance); diff --git a/platform/windows/packet_peer_udp_winsock.cpp b/platform/windows/packet_peer_udp_winsock.cpp deleted file mode 100644 index d414ec891e..0000000000 --- a/platform/windows/packet_peer_udp_winsock.cpp +++ /dev/null @@ -1,293 +0,0 @@ -/*************************************************************************/ -/* packet_peer_udp_winsock.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ -#include "packet_peer_udp_winsock.h" - -#include <winsock2.h> -#include <ws2tcpip.h> - -#include "drivers/unix/socket_helpers.h" - -int PacketPeerUDPWinsock::get_available_packet_count() const { - - Error err = const_cast<PacketPeerUDPWinsock *>(this)->_poll(false); - if (err != OK) - return 0; - - return queue_count; -} - -Error PacketPeerUDPWinsock::get_packet(const uint8_t **r_buffer, int &r_buffer_size) const { - - Error err = const_cast<PacketPeerUDPWinsock *>(this)->_poll(false); - if (err != OK) - return err; - if (queue_count == 0) - return ERR_UNAVAILABLE; - - uint32_t size; - uint8_t type; - rb.read(&type, 1, true); - if (type == IP::TYPE_IPV4) { - uint8_t ip[4]; - rb.read(ip, 4, true); - packet_ip.set_ipv4(ip); - } else { - uint8_t ip[16]; - rb.read(ip, 16, true); - packet_ip.set_ipv6(ip); - }; - rb.read((uint8_t *)&packet_port, 4, true); - rb.read((uint8_t *)&size, 4, true); - rb.read(packet_buffer, size, true); - --queue_count; - *r_buffer = packet_buffer; - r_buffer_size = size; - return OK; -} -Error PacketPeerUDPWinsock::put_packet(const uint8_t *p_buffer, int p_buffer_size) { - - ERR_FAIL_COND_V(!peer_addr.is_valid(), ERR_UNCONFIGURED); - - if (sock_type == IP::TYPE_NONE) - sock_type = peer_addr.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; - - int sock = _get_socket(); - ERR_FAIL_COND_V(sock == -1, FAILED); - struct sockaddr_storage addr; - size_t addr_size = _set_sockaddr(&addr, peer_addr, peer_port, sock_type); - - _set_sock_blocking(blocking); - - errno = 0; - int err; - while ((err = sendto(sock, (const char *)p_buffer, p_buffer_size, 0, (struct sockaddr *)&addr, addr_size)) != p_buffer_size) { - - if (WSAGetLastError() != WSAEWOULDBLOCK) { - return FAILED; - } else if (!blocking) { - return ERR_UNAVAILABLE; - } - } - - return OK; -} - -int PacketPeerUDPWinsock::get_max_packet_size() const { - - return 512; // uhm maybe not -} - -void PacketPeerUDPWinsock::_set_sock_blocking(bool p_blocking) { - - if (sock_blocking == p_blocking) - return; - - sock_blocking = p_blocking; - unsigned long par = sock_blocking ? 0 : 1; - if (ioctlsocket(sockfd, FIONBIO, &par)) { - perror("setting non-block mode"); - //close(); - //return -1; - }; -} - -Error PacketPeerUDPWinsock::listen(int p_port, const IP_Address &p_bind_address, int p_recv_buffer_size) { - - ERR_FAIL_COND_V(sockfd != -1, ERR_ALREADY_IN_USE); - ERR_FAIL_COND_V(!p_bind_address.is_valid() && !p_bind_address.is_wildcard(), ERR_INVALID_PARAMETER); - - sock_type = IP::TYPE_ANY; - - if (p_bind_address.is_valid()) - sock_type = p_bind_address.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; - - int sock = _get_socket(); - if (sock == -1) - return ERR_CANT_CREATE; - - struct sockaddr_storage addr = { 0 }; - size_t addr_size = _set_listen_sockaddr(&addr, p_port, sock_type, IP_Address()); - - if (bind(sock, (struct sockaddr *)&addr, addr_size) == -1) { - close(); - return ERR_UNAVAILABLE; - } - - printf("UDP Connection listening on port %i\n", p_port); - rb.resize(nearest_shift(p_recv_buffer_size)); - return OK; -} - -void PacketPeerUDPWinsock::close() { - - if (sockfd != -1) - ::closesocket(sockfd); - sockfd = -1; - sock_type = IP::TYPE_NONE; - rb.resize(16); - queue_count = 0; -} - -Error PacketPeerUDPWinsock::wait() { - - return _poll(true); -} -Error PacketPeerUDPWinsock::_poll(bool p_wait) { - - if (sockfd == -1) { - return FAILED; - } - - _set_sock_blocking(p_wait); - - struct sockaddr_storage from = { 0 }; - int len = sizeof(struct sockaddr_storage); - int ret; - while ((ret = recvfrom(sockfd, (char *)recv_buffer, MIN((int)sizeof(recv_buffer), MAX(rb.space_left() - 24, 0)), 0, (struct sockaddr *)&from, &len)) > 0) { - - uint32_t port = 0; - - if (from.ss_family == AF_INET) { - uint8_t type = (uint8_t)IP::TYPE_IPV4; - rb.write(&type, 1); - struct sockaddr_in *sin_from = (struct sockaddr_in *)&from; - rb.write((uint8_t *)&sin_from->sin_addr, 4); - port = ntohs(sin_from->sin_port); - - } else if (from.ss_family == AF_INET6) { - - uint8_t type = (uint8_t)IP::TYPE_IPV6; - rb.write(&type, 1); - - struct sockaddr_in6 *s6_from = (struct sockaddr_in6 *)&from; - rb.write((uint8_t *)&s6_from->sin6_addr, 16); - - port = ntohs(s6_from->sin6_port); - - } else { - // WARN_PRINT("Ignoring packet with unknown address family"); - uint8_t type = (uint8_t)IP::TYPE_NONE; - rb.write(&type, 1); - }; - - rb.write((uint8_t *)&port, 4); - rb.write((uint8_t *)&ret, 4); - rb.write(recv_buffer, ret); - - len = sizeof(struct sockaddr_storage); - ++queue_count; - if (p_wait) - break; - }; - - if (ret == SOCKET_ERROR) { - int error = WSAGetLastError(); - - if (error == WSAEWOULDBLOCK) { - // Expected when doing non-blocking sockets, retry later. - } else if (error == WSAECONNRESET) { - // If the remote target does not accept messages, this error may occur, but is harmless. - // Once the remote target gets available, this message will disappear for new messages. - } else { - close(); - return FAILED; - } - } - - if (ret == 0) { - close(); - return FAILED; - }; - - return OK; -} - -bool PacketPeerUDPWinsock::is_listening() const { - - return sockfd != -1; -} - -IP_Address PacketPeerUDPWinsock::get_packet_address() const { - - return packet_ip; -} - -int PacketPeerUDPWinsock::get_packet_port() const { - - return packet_port; -} - -int PacketPeerUDPWinsock::_get_socket() { - - ERR_FAIL_COND_V(sock_type == IP::TYPE_NONE, -1); - - if (sockfd != -1) - return sockfd; - - sockfd = _socket_create(sock_type, SOCK_DGRAM, IPPROTO_UDP); - - if (sockfd != -1) - _set_sock_blocking(false); - - return sockfd; -} - -void PacketPeerUDPWinsock::set_dest_address(const IP_Address &p_address, int p_port) { - - peer_addr = p_address; - peer_port = p_port; -} - -void PacketPeerUDPWinsock::make_default() { - - PacketPeerUDP::_create = PacketPeerUDPWinsock::_create; -}; - -PacketPeerUDP *PacketPeerUDPWinsock::_create() { - - return memnew(PacketPeerUDPWinsock); -}; - -PacketPeerUDPWinsock::PacketPeerUDPWinsock() { - - blocking = true; - sock_blocking = true; - sockfd = -1; - packet_port = 0; - queue_count = 0; - peer_port = 0; - sock_type = IP::TYPE_NONE; - rb.resize(16); -} - -PacketPeerUDPWinsock::~PacketPeerUDPWinsock() { - - close(); -} diff --git a/platform/windows/platform_config.h b/platform/windows/platform_config.h index 832504b9c5..d100385e80 100644 --- a/platform/windows/platform_config.h +++ b/platform/windows/platform_config.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,8 +27,10 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include <malloc.h> //#else //#include <alloca.h> //#endif #define GLES3_INCLUDE_H "glad/glad.h" +#define GLES2_INCLUDE_H "glad/glad.h" diff --git a/platform/windows/power_windows.cpp b/platform/windows/power_windows.cpp index 8d86f160f1..5612906251 100644 --- a/platform/windows/power_windows.cpp +++ b/platform/windows/power_windows.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -121,8 +121,10 @@ int PowerWindows::get_power_percent_left() { } } -PowerWindows::PowerWindows() - : nsecs_left(-1), percent_left(-1), power_state(OS::POWERSTATE_UNKNOWN) { +PowerWindows::PowerWindows() : + nsecs_left(-1), + percent_left(-1), + power_state(OS::POWERSTATE_UNKNOWN) { } PowerWindows::~PowerWindows() { diff --git a/platform/windows/power_windows.h b/platform/windows/power_windows.h index 0745615195..1c1a8c0876 100644 --- a/platform/windows/power_windows.h +++ b/platform/windows/power_windows.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ diff --git a/platform/windows/stream_peer_winsock.cpp b/platform/windows/stream_peer_winsock.cpp deleted file mode 100644 index a9d9cb9373..0000000000 --- a/platform/windows/stream_peer_winsock.cpp +++ /dev/null @@ -1,371 +0,0 @@ -/*************************************************************************/ -/* stream_peer_winsock.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ -#ifdef WINDOWS_ENABLED - -#include "stream_peer_winsock.h" - -#include <winsock2.h> -#include <ws2tcpip.h> - -#include "drivers/unix/socket_helpers.h" - -int winsock_refcount = 0; - -StreamPeerTCP *StreamPeerWinsock::_create() { - - return memnew(StreamPeerWinsock); -}; - -void StreamPeerWinsock::make_default() { - - StreamPeerTCP::_create = StreamPeerWinsock::_create; - - if (winsock_refcount == 0) { - WSADATA data; - WSAStartup(MAKEWORD(2, 2), &data); - }; - ++winsock_refcount; -}; - -void StreamPeerWinsock::cleanup() { - - --winsock_refcount; - if (winsock_refcount == 0) { - - WSACleanup(); - }; -}; - -Error StreamPeerWinsock::_block(int p_sockfd, bool p_read, bool p_write) const { - - fd_set read, write; - FD_ZERO(&read); - FD_ZERO(&write); - - if (p_read) - FD_SET(p_sockfd, &read); - if (p_write) - FD_SET(p_sockfd, &write); - - int ret = select(p_sockfd + 1, &read, &write, NULL, NULL); // block forever - return ret < 0 ? FAILED : OK; -}; - -Error StreamPeerWinsock::_poll_connection() const { - - ERR_FAIL_COND_V(status != STATUS_CONNECTING || sockfd == INVALID_SOCKET, FAILED); - - struct sockaddr_storage their_addr; - size_t addr_size = _set_sockaddr(&their_addr, peer_host, peer_port, sock_type); - - if (::connect(sockfd, (struct sockaddr *)&their_addr, addr_size) == SOCKET_ERROR) { - - int err = WSAGetLastError(); - if (err == WSAEISCONN) { - status = STATUS_CONNECTED; - return OK; - }; - - if (err == WSAEINPROGRESS || err == WSAEALREADY) { - return OK; - } - - status = STATUS_ERROR; - return ERR_CONNECTION_ERROR; - } else { - - status = STATUS_CONNECTED; - return OK; - }; - - return OK; -}; - -Error StreamPeerWinsock::write(const uint8_t *p_data, int p_bytes, int &r_sent, bool p_block) { - - if (status == STATUS_NONE || status == STATUS_ERROR) { - - return FAILED; - }; - - if (status != STATUS_CONNECTED) { - - if (_poll_connection() != OK) { - - return FAILED; - }; - - if (status != STATUS_CONNECTED) { - r_sent = 0; - return OK; - }; - }; - - int data_to_send = p_bytes; - const uint8_t *offset = p_data; - if (sockfd == -1) return FAILED; - int total_sent = 0; - - while (data_to_send) { - - int sent_amount = send(sockfd, (const char *)offset, data_to_send, 0); - - if (sent_amount == -1) { - - if (WSAGetLastError() != WSAEWOULDBLOCK) { - - perror("shit?"); - disconnect_from_host(); - ERR_PRINT("Server disconnected!\n"); - return FAILED; - }; - - if (!p_block) { - r_sent = total_sent; - return OK; - }; - - _block(sockfd, false, true); - } else { - - data_to_send -= sent_amount; - offset += sent_amount; - total_sent += sent_amount; - }; - } - - r_sent = total_sent; - - return OK; -}; - -Error StreamPeerWinsock::read(uint8_t *p_buffer, int p_bytes, int &r_received, bool p_block) { - - if (!is_connected_to_host()) { - - return FAILED; - }; - - if (status != STATUS_CONNECTED) { - - if (_poll_connection() != OK) { - - return FAILED; - }; - - if (status != STATUS_CONNECTED) { - r_received = 0; - return OK; - }; - }; - - int to_read = p_bytes; - int total_read = 0; - - while (to_read) { - - int read = recv(sockfd, (char *)p_buffer + total_read, to_read, 0); - - if (read == -1) { - - if (WSAGetLastError() != WSAEWOULDBLOCK) { - - perror("shit?"); - disconnect_from_host(); - ERR_PRINT("Server disconnected!\n"); - return FAILED; - }; - - if (!p_block) { - - r_received = total_read; - return OK; - }; - _block(sockfd, true, false); - } else if (read == 0) { - disconnect_from_host(); - return ERR_FILE_EOF; - } else { - - to_read -= read; - total_read += read; - }; - }; - - r_received = total_read; - - return OK; -}; - -Error StreamPeerWinsock::put_data(const uint8_t *p_data, int p_bytes) { - - int total; - return write(p_data, p_bytes, total, true); -}; - -Error StreamPeerWinsock::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) { - - return write(p_data, p_bytes, r_sent, false); -}; - -Error StreamPeerWinsock::get_data(uint8_t *p_buffer, int p_bytes) { - - int total; - return read(p_buffer, p_bytes, total, true); -}; - -Error StreamPeerWinsock::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) { - - return read(p_buffer, p_bytes, r_received, false); -}; - -StreamPeerTCP::Status StreamPeerWinsock::get_status() const { - - if (status == STATUS_CONNECTING) { - _poll_connection(); - }; - - return status; -}; - -bool StreamPeerWinsock::is_connected_to_host() const { - - if (status == STATUS_NONE || status == STATUS_ERROR) { - - return false; - }; - if (status != STATUS_CONNECTED) { - return true; - }; - - return (sockfd != INVALID_SOCKET); -}; - -void StreamPeerWinsock::disconnect_from_host() { - - if (sockfd != INVALID_SOCKET) - closesocket(sockfd); - sockfd = INVALID_SOCKET; - sock_type = IP::TYPE_NONE; - - status = STATUS_NONE; - - peer_host = IP_Address(); - peer_port = 0; -}; - -void StreamPeerWinsock::set_socket(int p_sockfd, IP_Address p_host, int p_port, IP::Type p_sock_type) { - - sockfd = p_sockfd; - sock_type = p_sock_type; - status = STATUS_CONNECTING; - peer_host = p_host; - peer_port = p_port; -}; - -Error StreamPeerWinsock::connect_to_host(const IP_Address &p_host, uint16_t p_port) { - - ERR_FAIL_COND_V(!p_host.is_valid(), ERR_INVALID_PARAMETER); - - sock_type = p_host.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; - sockfd = _socket_create(sock_type, SOCK_STREAM, IPPROTO_TCP); - if (sockfd == INVALID_SOCKET) { - ERR_PRINT("Socket creation failed!"); - disconnect_from_host(); - //perror("socket"); - return FAILED; - }; - - unsigned long par = 1; - if (ioctlsocket(sockfd, FIONBIO, &par)) { - perror("setting non-block mode"); - disconnect_from_host(); - return FAILED; - }; - - struct sockaddr_storage their_addr; - size_t addr_size = _set_sockaddr(&their_addr, p_host, p_port, sock_type); - - if (::connect(sockfd, (struct sockaddr *)&their_addr, addr_size) == SOCKET_ERROR) { - - if (WSAGetLastError() != WSAEWOULDBLOCK) { - ERR_PRINT("Connection to remote host failed!"); - disconnect_from_host(); - return FAILED; - }; - status = STATUS_CONNECTING; - } else { - status = STATUS_CONNECTED; - }; - - peer_host = p_host; - peer_port = p_port; - - return OK; -}; - -void StreamPeerWinsock::set_nodelay(bool p_enabled) { - ERR_FAIL_COND(!is_connected_to_host()); - int flag = p_enabled ? 1 : 0; - setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int)); -} - -int StreamPeerWinsock::get_available_bytes() const { - - unsigned long len; - int ret = ioctlsocket(sockfd, FIONREAD, &len); - ERR_FAIL_COND_V(ret == -1, 0) - return len; -} - -IP_Address StreamPeerWinsock::get_connected_host() const { - - return peer_host; -}; - -uint16_t StreamPeerWinsock::get_connected_port() const { - - return peer_port; -}; - -StreamPeerWinsock::StreamPeerWinsock() { - - sock_type = IP::TYPE_NONE; - sockfd = INVALID_SOCKET; - status = STATUS_NONE; - peer_port = 0; -}; - -StreamPeerWinsock::~StreamPeerWinsock() { - - disconnect_from_host(); -}; - -#endif diff --git a/platform/windows/tcp_server_winsock.cpp b/platform/windows/tcp_server_winsock.cpp deleted file mode 100644 index de300befa7..0000000000 --- a/platform/windows/tcp_server_winsock.cpp +++ /dev/null @@ -1,183 +0,0 @@ -/*************************************************************************/ -/* tcp_server_winsock.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ -#include "tcp_server_winsock.h" - -#include "stream_peer_winsock.h" - -#include <winsock2.h> -#include <ws2tcpip.h> - -#include "drivers/unix/socket_helpers.h" - -extern int winsock_refcount; - -TCP_Server *TCPServerWinsock::_create() { - - return memnew(TCPServerWinsock); -}; - -void TCPServerWinsock::make_default() { - - TCP_Server::_create = TCPServerWinsock::_create; - - if (winsock_refcount == 0) { - WSADATA data; - WSAStartup(MAKEWORD(2, 2), &data); - }; - ++winsock_refcount; -}; - -void TCPServerWinsock::cleanup() { - - --winsock_refcount; - if (winsock_refcount == 0) { - - WSACleanup(); - }; -}; - -Error TCPServerWinsock::listen(uint16_t p_port, const IP_Address &p_bind_address) { - - ERR_FAIL_COND_V(listen_sockfd != -1, ERR_ALREADY_IN_USE); - ERR_FAIL_COND_V(!p_bind_address.is_valid() && !p_bind_address.is_wildcard(), ERR_INVALID_PARAMETER); - - int sockfd; - sock_type = IP::TYPE_ANY; - - // If the bind address is valid use its type as the socket type - if (p_bind_address.is_valid()) - sock_type = p_bind_address.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; - - sockfd = _socket_create(sock_type, SOCK_STREAM, IPPROTO_TCP); - ERR_FAIL_COND_V(sockfd == INVALID_SOCKET, FAILED); - - unsigned long par = 1; - if (ioctlsocket(sockfd, FIONBIO, &par)) { - perror("setting non-block mode"); - stop(); - return FAILED; - }; - - struct sockaddr_storage my_addr; - size_t addr_size = _set_listen_sockaddr(&my_addr, p_port, sock_type, p_bind_address); - - int reuse = 1; - if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) < 0) { - - printf("REUSEADDR failed!"); - } - - if (bind(sockfd, (struct sockaddr *)&my_addr, addr_size) != SOCKET_ERROR) { - - if (::listen(sockfd, SOMAXCONN) == SOCKET_ERROR) { - - closesocket(sockfd); - ERR_FAIL_V(FAILED); - }; - } else { - return ERR_ALREADY_IN_USE; - }; - - if (listen_sockfd != INVALID_SOCKET) { - - stop(); - }; - - listen_sockfd = sockfd; - - return OK; -}; - -bool TCPServerWinsock::is_connection_available() const { - - if (listen_sockfd == -1) { - return false; - }; - - timeval timeout; - timeout.tv_sec = 0; - timeout.tv_usec = 0; - - fd_set pfd; - FD_ZERO(&pfd); - FD_SET(listen_sockfd, &pfd); - - int ret = select(listen_sockfd + 1, &pfd, NULL, NULL, &timeout); - ERR_FAIL_COND_V(ret < 0, 0); - - if (ret && (FD_ISSET(listen_sockfd, &pfd))) { - - return true; - }; - - return false; -}; - -Ref<StreamPeerTCP> TCPServerWinsock::take_connection() { - - if (!is_connection_available()) { - return NULL; - }; - - struct sockaddr_storage their_addr; - int sin_size = sizeof(their_addr); - int fd = accept(listen_sockfd, (struct sockaddr *)&their_addr, &sin_size); - ERR_FAIL_COND_V(fd == INVALID_SOCKET, NULL); - - Ref<StreamPeerWinsock> conn = memnew(StreamPeerWinsock); - IP_Address ip; - int port; - _set_ip_addr_port(ip, port, &their_addr); - - conn->set_socket(fd, ip, port, sock_type); - - return conn; -}; - -void TCPServerWinsock::stop() { - - if (listen_sockfd != INVALID_SOCKET) { - closesocket(listen_sockfd); - }; - - listen_sockfd = -1; - sock_type = IP::TYPE_NONE; -}; - -TCPServerWinsock::TCPServerWinsock() { - - listen_sockfd = INVALID_SOCKET; - sock_type = IP::TYPE_NONE; -}; - -TCPServerWinsock::~TCPServerWinsock() { - - stop(); -}; diff --git a/platform/windows/windows_terminal_logger.cpp b/platform/windows/windows_terminal_logger.cpp index ef8140ffa7..12a4a1ae39 100644 --- a/platform/windows/windows_terminal_logger.cpp +++ b/platform/windows/windows_terminal_logger.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ diff --git a/platform/windows/windows_terminal_logger.h b/platform/windows/windows_terminal_logger.h index f6b1a68d18..1ad2bcb0fd 100644 --- a/platform/windows/windows_terminal_logger.h +++ b/platform/windows/windows_terminal_logger.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ diff --git a/platform/x11/SCsub b/platform/x11/SCsub index aabc49149f..d0f77892ef 100644 --- a/platform/x11/SCsub +++ b/platform/x11/SCsub @@ -4,9 +4,9 @@ import os Import('env') def make_debug(target, source, env): - os.system('objcopy --only-keep-debug %s %s.debug' % (target[0], target[0])) - os.system('strip --strip-debug --strip-unneeded %s' % (target[0])) - os.system('objcopy --add-gnu-debuglink=%s.debug %s' % (target[0], target[0])) + os.system('objcopy --only-keep-debug {0} {0}.debugsymbols'.format(target[0])) + os.system('strip --strip-debug --strip-unneeded {0}'.format(target[0])) + os.system('objcopy --add-gnu-debuglink={0}.debugsymbols {0}'.format(target[0])) common_x11 = [ "context_gl_x11.cpp", @@ -17,6 +17,7 @@ common_x11 = [ "power_x11.cpp", ] -binary = env.Program('#bin/godot', ['godot_x11.cpp'] + common_x11) -if env["debug_symbols"] == "full" or env["debug_symbols"] == "yes": - env.AddPostAction(binary, make_debug) +prog = env.add_program('#bin/godot', ['godot_x11.cpp'] + common_x11) + +if (env["debug_symbols"] == "full" or env["debug_symbols"] == "yes") and env["separate_debug_symbols"]: + env.AddPostAction(prog, make_debug) diff --git a/platform/x11/context_gl_x11.cpp b/platform/x11/context_gl_x11.cpp index 4f9d4a84b9..1a7cbc0d6d 100644 --- a/platform/x11/context_gl_x11.cpp +++ b/platform/x11/context_gl_x11.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "context_gl_x11.h" #ifdef X11_ENABLED @@ -145,20 +146,39 @@ Error ContextGL_X11::initialize() { int (*oldHandler)(Display *, XErrorEvent *) = XSetErrorHandler(&ctxErrorHandler); - if (!opengl_3_context) { - //oldstyle context: - p->glx_context = glXCreateContext(x11_display, vi, 0, GL_TRUE); - } else { - static int context_attribs[] = { - GLX_CONTEXT_MAJOR_VERSION_ARB, 3, - GLX_CONTEXT_MINOR_VERSION_ARB, 3, - GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB /*|GLX_CONTEXT_DEBUG_BIT_ARB*/, - None - }; - - p->glx_context = glXCreateContextAttribsARB(x11_display, fbc[0], NULL, true, context_attribs); - ERR_EXPLAIN("Could not obtain an OpenGL 3.3 context!"); - ERR_FAIL_COND_V(ctxErrorOccurred || !p->glx_context, ERR_UNCONFIGURED); + switch (context_type) { + case GLES_2_0_COMPATIBLE: + case OLDSTYLE: { + p->glx_context = glXCreateContext(x11_display, vi, 0, GL_TRUE); + } break; + /* + case ContextType::GLES_2_0_COMPATIBLE: { + + static int context_attribs[] = { + GLX_CONTEXT_MAJOR_VERSION_ARB, 3, + GLX_CONTEXT_MINOR_VERSION_ARB, 0, + None + }; + + p->glx_context = glXCreateContextAttribsARB(x11_display, fbc[0], NULL, true, context_attribs); + ERR_EXPLAIN("Could not obtain an OpenGL 3.0 context!"); + ERR_FAIL_COND_V(!p->glx_context, ERR_UNCONFIGURED); + } break; + */ + case GLES_3_0_COMPATIBLE: { + + static int context_attribs[] = { + GLX_CONTEXT_MAJOR_VERSION_ARB, 3, + GLX_CONTEXT_MINOR_VERSION_ARB, 3, + GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, + GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB /*|GLX_CONTEXT_DEBUG_BIT_ARB*/, + None + }; + + p->glx_context = glXCreateContextAttribsARB(x11_display, fbc[0], NULL, true, context_attribs); + ERR_EXPLAIN("Could not obtain an OpenGL 3.3 context!"); + ERR_FAIL_COND_V(ctxErrorOccurred || !p->glx_context, ERR_UNCONFIGURED); + } break; } XSync(x11_display, False); @@ -228,13 +248,13 @@ bool ContextGL_X11::is_using_vsync() const { return use_vsync; } -ContextGL_X11::ContextGL_X11(::Display *p_x11_display, ::Window &p_x11_window, const OS::VideoMode &p_default_video_mode, bool p_opengl_3_context) - : x11_window(p_x11_window) { +ContextGL_X11::ContextGL_X11(::Display *p_x11_display, ::Window &p_x11_window, const OS::VideoMode &p_default_video_mode, ContextType p_context_type) : + x11_window(p_x11_window) { default_video_mode = p_default_video_mode; x11_display = p_x11_display; - opengl_3_context = p_opengl_3_context; + context_type = p_context_type; double_buffer = false; direct_render = false; diff --git a/platform/x11/context_gl_x11.h b/platform/x11/context_gl_x11.h index c37bac5e9b..b54cc84fac 100644 --- a/platform/x11/context_gl_x11.h +++ b/platform/x11/context_gl_x11.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef CONTEXT_GL_X11_H #define CONTEXT_GL_X11_H @@ -45,6 +46,14 @@ struct ContextGL_X11_Private; class ContextGL_X11 : public ContextGL { +public: + enum ContextType { + OLDSTYLE, + GLES_2_0_COMPATIBLE, + GLES_3_0_COMPATIBLE + }; + +private: ContextGL_X11_Private *p; OS::VideoMode default_video_mode; //::Colormap x11_colormap; @@ -53,8 +62,8 @@ class ContextGL_X11 : public ContextGL { bool double_buffer; bool direct_render; int glx_minor, glx_major; - bool opengl_3_context; bool use_vsync; + ContextType context_type; public: virtual void release_current(); @@ -68,7 +77,7 @@ public: virtual void set_use_vsync(bool p_use); virtual bool is_using_vsync() const; - ContextGL_X11(::Display *p_x11_display, ::Window &p_x11_window, const OS::VideoMode &p_default_video_mode, bool p_opengl_3_context); + ContextGL_X11(::Display *p_x11_display, ::Window &p_x11_window, const OS::VideoMode &p_default_video_mode, ContextType p_context_type); ~ContextGL_X11(); }; diff --git a/platform/x11/crash_handler_x11.cpp b/platform/x11/crash_handler_x11.cpp index 3c54d5cbc2..d39fc33f81 100644 --- a/platform/x11/crash_handler_x11.cpp +++ b/platform/x11/crash_handler_x11.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,12 +27,15 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifdef DEBUG_ENABLED #define CRASH_HANDLER_ENABLED 1 #endif +#include "crash_handler_x11.h" #include "main/main.h" -#include "os_x11.h" +#include "os/os.h" +#include "project_settings.h" #ifdef CRASH_HANDLER_ENABLED #include <cxxabi.h> diff --git a/platform/x11/crash_handler_x11.h b/platform/x11/crash_handler_x11.h index e01334cbf2..49ca0df9a8 100644 --- a/platform/x11/crash_handler_x11.h +++ b/platform/x11/crash_handler_x11.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef CRASH_HANDLER_X11_H #define CRASH_HANDLER_X11_H diff --git a/platform/x11/detect.py b/platform/x11/detect.py index 56bc1d4c59..da2b0701b6 100644 --- a/platform/x11/detect.py +++ b/platform/x11/detect.py @@ -49,13 +49,14 @@ def get_opts(): return [ BoolVariable('use_llvm', 'Use the LLVM compiler', False), - BoolVariable('use_static_cpp', 'Link stdc++ statically', False), + BoolVariable('use_static_cpp', 'Link libgcc and libstdc++ statically for better portability', False), BoolVariable('use_sanitizer', 'Use LLVM compiler address sanitizer', False), BoolVariable('use_leak_sanitizer', 'Use LLVM compiler memory leaks sanitizer (implies use_sanitizer)', False), - BoolVariable('use_lto', 'Use link time optimization', False), BoolVariable('pulseaudio', 'Detect & use pulseaudio', True), BoolVariable('udev', 'Use udev for gamepad connection callbacks', False), EnumVariable('debug_symbols', 'Add debug symbols to release version', 'yes', ('yes', 'no', 'full')), + BoolVariable('separate_debug_symbols', 'Create a separate file with the debug symbols', False), + BoolVariable('touch', 'Enable touch events', True), ] @@ -64,7 +65,6 @@ def get_flags(): return [ ('builtin_freetype', False), ('builtin_libpng', False), - ('builtin_openssl', False), ('builtin_zlib', False), ] @@ -109,7 +109,7 @@ def configure(env): if ('clang++' not in env['CXX']): env["CC"] = "clang" env["CXX"] = "clang++" - env["LD"] = "clang++" + env["LINK"] = "clang++" env.Append(CPPFLAGS=['-DTYPED_METHOD_BIND']) env.extra_suffix = ".llvm" + env.extra_suffix @@ -142,14 +142,24 @@ def configure(env): env.ParseConfig('pkg-config xinerama --cflags --libs') env.ParseConfig('pkg-config xrandr --cflags --libs') + if (env['touch']): + x11_error = os.system("pkg-config xi --modversion > /dev/null ") + if (x11_error): + print("xi not found.. cannot build with touch. Aborting.") + sys.exit(255) + env.ParseConfig('pkg-config xi --cflags --libs') + env.Append(CPPFLAGS=['-DTOUCH_ENABLED']) + # FIXME: Check for existence of the libs before parsing their flags with pkg-config - if not env['builtin_openssl']: - env.ParseConfig('pkg-config openssl --cflags --libs') + if not env['builtin_mbedtls']: + # mbedTLS does not provide a pkgconfig config yet. See https://github.com/ARMmbed/mbedtls/issues/228 + env.Append(LIBS=['mbedtls', 'mbedcrypto', 'mbedx509']) if not env['builtin_libwebp']: env.ParseConfig('pkg-config libwebp --cflags --libs') + # freetype depends on libpng and zlib, so bundling one of them while keeping others # as shared libraries leads to weird issues if env['builtin_freetype'] or env['builtin_libpng'] or env['builtin_zlib']: @@ -163,6 +173,16 @@ def configure(env): if not env['builtin_libpng']: env.ParseConfig('pkg-config libpng --cflags --libs') + if not env['builtin_bullet']: + # We need at least version 2.88 + import subprocess + bullet_version = subprocess.check_output(['pkg-config', 'bullet', '--modversion']).strip() + if bullet_version < "2.88": + # Abort as system bullet was requested but too old + print("Bullet: System version {0} does not match minimal requirements ({1}). Aborting.".format(bullet_version, "2.88")) + sys.exit(255) + env.ParseConfig('pkg-config bullet --cflags --libs') + if not env['builtin_enet']: env.ParseConfig('pkg-config libenet --cflags --libs') @@ -237,7 +257,7 @@ def configure(env): env.ParseConfig('pkg-config zlib --cflags --libs') env.Append(CPPPATH=['#platform/x11']) - env.Append(CPPFLAGS=['-DX11_ENABLED', '-DUNIX_ENABLED', '-DOPENGL_ENABLED', '-DGLES2_ENABLED', '-DGLES_OVER_GL']) + env.Append(CPPFLAGS=['-DX11_ENABLED', '-DUNIX_ENABLED', '-DOPENGL_ENABLED', '-DGLES_ENABLED', '-DGLES_OVER_GL']) env.Append(LIBS=['GL', 'pthread']) if (platform.system() == "Linux"): @@ -255,5 +275,6 @@ def configure(env): env.Append(CPPFLAGS=['-m64']) env.Append(LINKFLAGS=['-m64', '-L/usr/lib/i686-linux-gnu']) + # Link those statically for portability if env['use_static_cpp']: - env.Append(LINKFLAGS=['-static-libstdc++']) + env.Append(LINKFLAGS=['-static-libgcc', '-static-libstdc++']) diff --git a/platform/x11/export/export.cpp b/platform/x11/export/export.cpp index fdb43c9ae0..904aceaca0 100644 --- a/platform/x11/export/export.cpp +++ b/platform/x11/export/export.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* 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" @@ -44,7 +45,8 @@ void register_x11_exporter() { logo->create_from_image(img); platform->set_logo(logo); platform->set_name("Linux/X11"); - platform->set_extension("bin"); + platform->set_extension("x86"); + platform->set_extension("x86_64", "binary_format/64_bits"); platform->set_release_32("linux_x11_32_release"); platform->set_debug_32("linux_x11_32_debug"); platform->set_release_64("linux_x11_64_release"); diff --git a/platform/x11/export/export.h b/platform/x11/export/export.h index b70633266f..ac44cef8f7 100644 --- a/platform/x11/export/export.h +++ b/platform/x11/export/export.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,4 +27,5 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + void register_x11_exporter(); diff --git a/platform/x11/godot_x11.cpp b/platform/x11/godot_x11.cpp index 2e43613e5d..3241cbcbf9 100644 --- a/platform/x11/godot_x11.cpp +++ b/platform/x11/godot_x11.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include <limits.h> #include <locale.h> #include <stdlib.h> diff --git a/platform/x11/joypad_linux.cpp b/platform/x11/joypad_linux.cpp index 428385f7cb..aff9a50bd6 100644 --- a/platform/x11/joypad_linux.cpp +++ b/platform/x11/joypad_linux.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ diff --git a/platform/x11/joypad_linux.h b/platform/x11/joypad_linux.h index b34d98b77a..1187acac23 100644 --- a/platform/x11/joypad_linux.h +++ b/platform/x11/joypad_linux.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ diff --git a/platform/x11/key_mapping_x11.cpp b/platform/x11/key_mapping_x11.cpp index 95ac2fe6b6..423e2737ee 100644 --- a/platform/x11/key_mapping_x11.cpp +++ b/platform/x11/key_mapping_x11.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "key_mapping_x11.h" /***** SCAN CODE CONVERSION ******/ diff --git a/platform/x11/key_mapping_x11.h b/platform/x11/key_mapping_x11.h index 7b11cb5ccc..62dfcf3a4d 100644 --- a/platform/x11/key_mapping_x11.h +++ b/platform/x11/key_mapping_x11.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef KEY_MAPPING_X11_H #define KEY_MAPPING_X11_H diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index 09193e0a2b..c06c7516d0 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,12 +27,14 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #include "os_x11.h" +#include "drivers/gles2/rasterizer_gles2.h" #include "drivers/gles3/rasterizer_gles3.h" #include "errno.h" #include "key_mapping_x11.h" +#include "os/dir_access.h" #include "print_string.h" -#include "servers/physics/physics_server_sw.h" #include "servers/visual/visual_server_raster.h" #include "servers/visual/visual_server_wrap_mt.h" @@ -75,29 +77,6 @@ #include <X11/XKBlib.h> -int OS_X11::get_video_driver_count() const { - return 1; -} - -const char *OS_X11::get_video_driver_name(int p_driver) const { - return "GLES3"; -} - -OS::VideoMode OS_X11::get_default_video_mode() const { - return OS::VideoMode(1024, 600, false); -} - -int OS_X11::get_audio_driver_count() const { - return AudioDriverManager::get_driver_count(); -} - -const char *OS_X11::get_audio_driver_name(int p_driver) const { - - AudioDriver *driver = AudioDriverManager::get_driver(p_driver); - ERR_FAIL_COND_V(!driver, ""); - return AudioDriverManager::get_driver(p_driver)->get_name(); -} - void OS_X11::initialize_core() { crash_handler.initialize(); @@ -105,7 +84,7 @@ void OS_X11::initialize_core() { OS_Unix::initialize_core(); } -void OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { +Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { long im_event_mask = 0; last_button_state = 0; @@ -128,22 +107,24 @@ void OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_au /** XLIB INITIALIZATION **/ x11_display = XOpenDisplay(NULL); + if (!x11_display) { + ERR_PRINT("X11 Display is not available"); + return ERR_UNAVAILABLE; + } + char *modifiers = NULL; Bool xkb_dar = False; - if (x11_display) { - XAutoRepeatOn(x11_display); - xkb_dar = XkbSetDetectableAutoRepeat(x11_display, True, NULL); + XAutoRepeatOn(x11_display); + xkb_dar = XkbSetDetectableAutoRepeat(x11_display, True, 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. + // Try to support IME if detectable auto-repeat is supported + if (xkb_dar == True) { #ifdef X_HAVE_UTF8_STRING - modifiers = XSetLocaleModifiers(""); + // Xutf8LookupString will be used later instead of XmbLookupString before + // the multibyte sequences can be converted to unicode string. + modifiers = XSetLocaleModifiers(""); #endif - } } if (modifiers == NULL) { @@ -183,6 +164,49 @@ void OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_au } } +#ifdef TOUCH_ENABLED + if (!XQueryExtension(x11_display, "XInputExtension", &touch.opcode, &event_base, &error_base)) { + fprintf(stderr, "XInput extension not available"); + } else { + // 2.2 is the first release with multitouch + int xi_major = 2; + int xi_minor = 2; + if (XIQueryVersion(x11_display, &xi_major, &xi_minor) != Success) { + fprintf(stderr, "XInput 2.2 not available (server supports %d.%d)\n", xi_major, xi_minor); + touch.opcode = 0; + } else { + int dev_count; + XIDeviceInfo *info = XIQueryDevice(x11_display, XIAllDevices, &dev_count); + + for (int i = 0; i < dev_count; i++) { + XIDeviceInfo *dev = &info[i]; + if (!dev->enabled) + continue; + if (!(dev->use == XIMasterPointer || dev->use == XIFloatingSlave)) + continue; + + bool direct_touch = false; + for (int j = 0; j < dev->num_classes; j++) { + if (dev->classes[j]->type == XITouchClass && ((XITouchClassInfo *)dev->classes[j])->mode == XIDirectTouch) { + direct_touch = true; + break; + } + } + if (direct_touch) { + touch.devices.push_back(dev->deviceid); + fprintf(stderr, "Using touch device: %s\n", dev->name); + } + } + + XIFreeDeviceInfo(info); + + if (is_stdout_verbose() && !touch.devices.size()) { + fprintf(stderr, "No touch devices found\n"); + } + } + } +#endif + xim = XOpenIM(x11_display, NULL, NULL, NULL); if (xim == NULL) { @@ -241,12 +265,27 @@ void OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_au //print_line("def videomode "+itos(current_videomode.width)+","+itos(current_videomode.height)); #if defined(OPENGL_ENABLED) - context_gl = memnew(ContextGL_X11(x11_display, x11_window, current_videomode, true)); + ContextGL_X11::ContextType opengl_api_type = ContextGL_X11::GLES_3_0_COMPATIBLE; + + if (p_video_driver == VIDEO_DRIVER_GLES2) { + opengl_api_type = ContextGL_X11::GLES_2_0_COMPATIBLE; + } + + context_gl = memnew(ContextGL_X11(x11_display, x11_window, current_videomode, opengl_api_type)); context_gl->initialize(); - RasterizerGLES3::register_config(); + switch (opengl_api_type) { + case ContextGL_X11::GLES_2_0_COMPATIBLE: { + RasterizerGLES2::register_config(); + RasterizerGLES2::make_current(); + } break; + case ContextGL_X11::GLES_3_0_COMPATIBLE: { + RasterizerGLES3::register_config(); + RasterizerGLES3::make_current(); + } break; + } - RasterizerGLES3::make_current(); + context_gl->set_use_vsync(current_videomode.use_vsync); #endif visual_server = memnew(VisualServerRaster); @@ -255,41 +294,13 @@ void OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_au visual_server = memnew(VisualServerWrapMT(visual_server, get_render_thread_mode() == RENDER_SEPARATE_THREAD)); } - - // borderless fullscreen window mode - if (current_videomode.fullscreen) { - // set bypass compositor hint - Atom bypass_compositor = XInternAtom(x11_display, "_NET_WM_BYPASS_COMPOSITOR", False); - unsigned long compositing_disable_on = 1; - XChangeProperty(x11_display, x11_window, bypass_compositor, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&compositing_disable_on, 1); - - // needed for lxde/openbox, possibly others - Hints hints; - Atom property; - 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); - XMapRaised(x11_display, x11_window); - XWindowAttributes xwa; - XGetWindowAttributes(x11_display, DefaultRootWindow(x11_display), &xwa); - XMoveResizeWindow(x11_display, x11_window, 0, 0, xwa.width, xwa.height); - - // code for netwm-compliants - XEvent xev; - Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False); - Atom fullscreen = XInternAtom(x11_display, "_NET_WM_STATE_FULLSCREEN", False); - - memset(&xev, 0, sizeof(xev)); - xev.type = ClientMessage; - xev.xclient.window = x11_window; - xev.xclient.message_type = wm_state; - xev.xclient.format = 32; - xev.xclient.data.l[0] = 1; - xev.xclient.data.l[1] = fullscreen; - xev.xclient.data.l[2] = 0; - - XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureNotifyMask, &xev); + if (current_videomode.maximized) { + current_videomode.maximized = false; + set_window_maximized(true); + // borderless fullscreen window mode + } else if (current_videomode.fullscreen) { + current_videomode.fullscreen = false; + set_window_fullscreen(true); } else if (current_videomode.borderless_window) { Hints hints; Atom property; @@ -318,10 +329,15 @@ void OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_au XFree(xsh); } + if (current_videomode.always_on_top) { + current_videomode.always_on_top = false; + set_window_always_on_top(true); + } + AudioDriverManager::initialize(p_audio_driver); - ERR_FAIL_COND(!visual_server); - ERR_FAIL_COND(x11_window == 0); + ERR_FAIL_COND_V(!visual_server, ERR_UNAVAILABLE); + ERR_FAIL_COND_V(x11_window == 0, ERR_UNAVAILABLE); XSetWindowAttributes new_attr; @@ -341,6 +357,34 @@ void OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_au XChangeWindowAttributes(x11_display, x11_window, CWEventMask, &new_attr); +#ifdef TOUCH_ENABLED + if (touch.devices.size()) { + + // Must be alive after this block + static unsigned char mask_data[XIMaskLen(XI_LASTEVENT)] = {}; + + touch.event_mask.deviceid = XIAllDevices; + touch.event_mask.mask_len = sizeof(mask_data); + touch.event_mask.mask = mask_data; + + XISetMask(touch.event_mask.mask, XI_TouchBegin); + XISetMask(touch.event_mask.mask, XI_TouchUpdate); + XISetMask(touch.event_mask.mask, XI_TouchEnd); + XISetMask(touch.event_mask.mask, XI_TouchOwnership); + + XISelectEvents(x11_display, x11_window, &touch.event_mask, 1); + + // Disabled by now since grabbing also blocks mouse events + // (they are received as extended events instead of standard events) + /*XIClearMask(touch.event_mask.mask, XI_TouchOwnership); + + // Grab touch devices to avoid OS gesture interference + for (int i = 0; i < touch.devices.size(); ++i) { + XIGrabDevice(x11_display, touch.devices[i], x11_window, CurrentTime, None, XIGrabModeAsync, XIGrabModeAsync, False, &touch.event_mask); + }*/ + } +#endif + /* set the titlebar name */ XStoreName(x11_display, x11_window, "Godot"); @@ -462,12 +506,6 @@ void OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_au requested = None; visual_server->init(); - // - physics_server = memnew(PhysicsServerSW); - physics_server->init(); - //physics_2d_server = memnew( Physics2DServerSW ); - physics_2d_server = Physics2DServerWrapMT::init_server<Physics2DServerSW>(); - physics_2d_server->init(); input = memnew(InputDefault); @@ -475,9 +513,19 @@ void OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_au #ifdef JOYDEV_ENABLED joypad = memnew(JoypadLinux(input)); #endif - _ensure_data_dir(); + _ensure_user_data_dir(); power_manager = memnew(PowerX11); + + XEvent xevent; + while (XPending(x11_display) > 0) { + XNextEvent(x11_display, &xevent); + if (xevent.type == ConfigureNotify) { + _window_changed(&xevent); + } + } + + return OK; } void OS_X11::xim_destroy_callback(::XIM im, ::XPointer client_data, @@ -502,13 +550,28 @@ void OS_X11::set_ime_position(const Point2 &p_pos) { XFree(preedit_attr); } +String OS_X11::get_unique_id() const { + + static String machine_id; + if (machine_id.empty()) { + if (FileAccess *f = FileAccess::open("/etc/machine-id", FileAccess::READ)) { + while (machine_id.empty() && !f->eof_reached()) { + machine_id = f->get_line().strip_edges(); + } + f->close(); + memdelete(f); + } + } + return machine_id; +} + void OS_X11::finalize() { if (main_loop) memdelete(main_loop); main_loop = NULL; -/* + /* if (debugger_connection_console) { memdelete(debugger_connection_console); } @@ -517,18 +580,16 @@ void OS_X11::finalize() { #ifdef JOYDEV_ENABLED memdelete(joypad); #endif +#ifdef TOUCH_ENABLED + touch.devices.clear(); + touch.state.clear(); +#endif memdelete(input); visual_server->finish(); memdelete(visual_server); //memdelete(rasterizer); - physics_server->finish(); - memdelete(physics_server); - - physics_2d_server->finish(); - memdelete(physics_2d_server); - memdelete(power_manager); if (xrandr_handle) @@ -665,6 +726,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 && !get_borderless_window()) { + // remove decorations if the window is not already borderless + 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); + } + if (p_enabled && !is_window_resizable()) { // Set the window as resizable to prevent window managers to ignore the fullscreen state flag. XSizeHints *xsh; @@ -675,7 +746,7 @@ void OS_X11::set_wm_fullscreen(bool p_enabled) { XFree(xsh); } - // Using EWMH -- Extened Window Manager Hints + // Using EWMH -- Extended Window Manager Hints XEvent xev; Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False); Atom wm_fullscreen = XInternAtom(x11_display, "_NET_WM_STATE_FULLSCREEN", False); @@ -725,6 +796,22 @@ void OS_X11::set_wm_fullscreen(bool p_enabled) { } } +void OS_X11::set_wm_above(bool p_enabled) { + Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False); + Atom wm_above = XInternAtom(x11_display, "_NET_WM_STATE_ABOVE", False); + + XClientMessageEvent xev; + memset(&xev, 0, sizeof(xev)); + xev.type = ClientMessage; + xev.window = x11_window; + xev.message_type = wm_state; + xev.format = 32; + xev.data.l[0] = p_enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; + xev.data.l[1] = wm_above; + xev.data.l[3] = 1; + XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent *)&xev); +} + int OS_X11::get_screen_count() const { // Using Xinerama Extension int event_base, error_base; @@ -876,6 +963,26 @@ Size2 OS_X11::get_window_size() const { return Size2i(current_videomode.width, current_videomode.height); } +Size2 OS_X11::get_real_window_size() const { + XWindowAttributes xwa; + XSync(x11_display, False); + XGetWindowAttributes(x11_display, x11_window, &xwa); + int w = xwa.width; + int h = xwa.height; + Atom prop = XInternAtom(x11_display, "_NET_FRAME_EXTENTS", True); + Atom type; + int format; + unsigned long len; + unsigned long remaining; + unsigned char *data = NULL; + if (XGetWindowProperty(x11_display, x11_window, prop, 0, 4, False, AnyPropertyType, &type, &format, &len, &remaining, &data) == Success) { + long *extents = (long *)data; + w += extents[0] + extents[1]; // left, right + h += extents[2] + extents[3]; // top, bottom + } + return Size2(w, h); +} + void OS_X11::set_window_size(const Size2 p_size) { // If window resizable is disabled we need to update the attributes first if (is_window_resizable() == false) { @@ -899,7 +1006,19 @@ void OS_X11::set_window_size(const Size2 p_size) { } void OS_X11::set_window_fullscreen(bool p_enabled) { + if (current_videomode.fullscreen == p_enabled) + return; + + if (p_enabled && current_videomode.always_on_top) { + // Fullscreen + Always-on-top requires a maximized window on some window managers (Metacity) + set_window_maximized(true); + } set_wm_fullscreen(p_enabled); + if (!p_enabled && !current_videomode.always_on_top) { + // Restore + set_window_maximized(false); + } + current_videomode.fullscreen = p_enabled; } @@ -988,6 +1107,9 @@ bool OS_X11::is_window_minimized() const { } void OS_X11::set_window_maximized(bool p_enabled) { + if (is_window_maximized() == p_enabled) + return; + // Using EWMH -- Extended Window Manager Hints XEvent xev; Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False); @@ -1005,9 +1127,59 @@ void OS_X11::set_window_maximized(bool p_enabled) { XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); + if (is_window_maximize_allowed()) { + while (p_enabled && !is_window_maximized()) { + // Wait for effective resizing (so the GLX context is too). + } + } + maximized = p_enabled; } +bool OS_X11::is_window_maximize_allowed() { + Atom property = XInternAtom(x11_display, "_NET_WM_ALLOWED_ACTIONS", False); + Atom type; + int format; + unsigned long len; + unsigned long remaining; + unsigned char *data = NULL; + + int result = XGetWindowProperty( + x11_display, + x11_window, + property, + 0, + 1024, + False, + XA_ATOM, + &type, + &format, + &len, + &remaining, + &data); + + if (result == Success) { + Atom *atoms = (Atom *)data; + Atom wm_act_max_horz = XInternAtom(x11_display, "_NET_WM_ACTION_MAXIMIZE_HORZ", False); + Atom wm_act_max_vert = XInternAtom(x11_display, "_NET_WM_ACTION_MAXIMIZE_VERT", False); + bool found_wm_act_max_horz = false; + bool found_wm_act_max_vert = false; + + for (unsigned int i = 0; i < len; i++) { + if (atoms[i] == wm_act_max_horz) + found_wm_act_max_horz = true; + if (atoms[i] == wm_act_max_vert) + found_wm_act_max_vert = true; + + if (found_wm_act_max_horz || found_wm_act_max_vert) + return true; + } + XFree(atoms); + } + + return false; +} + bool OS_X11::is_window_maximized() const { // Using EWMH -- Extended Window Manager Hints Atom property = XInternAtom(x11_display, "_NET_WM_STATE", False); @@ -1016,6 +1188,7 @@ bool OS_X11::is_window_maximized() const { unsigned long len; unsigned long remaining; unsigned char *data = NULL; + bool retval = false; int result = XGetWindowProperty( x11_display, @@ -1044,16 +1217,39 @@ bool OS_X11::is_window_maximized() const { if (atoms[i] == wm_max_vert) found_wm_max_vert = true; - if (found_wm_max_horz && found_wm_max_vert) - return true; + if (found_wm_max_horz && found_wm_max_vert) { + retval = true; + break; + } } - XFree(atoms); } - return false; + XFree(data); + return retval; +} + +void OS_X11::set_window_always_on_top(bool p_enabled) { + if (is_window_always_on_top() == p_enabled) + return; + + if (p_enabled && current_videomode.fullscreen) { + // Fullscreen + Always-on-top requires a maximized window on some window managers (Metacity) + set_window_maximized(true); + } + set_wm_above(p_enabled); + if (!p_enabled && !current_videomode.fullscreen) { + // Restore + set_window_maximized(false); + } + + current_videomode.always_on_top = p_enabled; } -void OS_X11::set_borderless_window(int p_borderless) { +bool OS_X11::is_window_always_on_top() const { + return current_videomode.always_on_top; +} + +void OS_X11::set_borderless_window(bool p_borderless) { if (current_videomode.borderless_window == p_borderless) return; @@ -1434,6 +1630,20 @@ static Atom pick_target_from_atoms(Display *p_disp, Atom p_t1, Atom p_t2, Atom p return None; } +void OS_X11::_window_changed(XEvent *event) { + + if (xic) { + // Not portable. + set_ime_position(Point2(0, 1)); + } + if ((event->xconfigure.width == current_videomode.width) && + (event->xconfigure.height == current_videomode.height)) + return; + + current_videomode.width = event->xconfigure.width; + current_videomode.height = event->xconfigure.height; +} + void OS_X11::process_xevents() { //printf("checking events %i\n", XPending(x11_display)); @@ -1451,6 +1661,69 @@ void OS_X11::process_xevents() { continue; } +#ifdef TOUCH_ENABLED + if (XGetEventData(x11_display, &event.xcookie)) { + + if (event.xcookie.type == GenericEvent && event.xcookie.extension == touch.opcode) { + + XIDeviceEvent *event_data = (XIDeviceEvent *)event.xcookie.data; + int index = event_data->detail; + Vector2 pos = Vector2(event_data->event_x, event_data->event_y); + + switch (event_data->evtype) { + + case XI_TouchBegin: // Fall-through + // Disabled hand-in-hand with the grabbing + //XIAllowTouchEvents(x11_display, event_data->deviceid, event_data->detail, x11_window, XIAcceptTouch); + + case XI_TouchEnd: { + + bool is_begin = event_data->evtype == XI_TouchBegin; + + Ref<InputEventScreenTouch> st; + st.instance(); + st->set_index(index); + st->set_position(pos); + st->set_pressed(is_begin); + + if (is_begin) { + if (touch.state.has(index)) // Defensive + break; + touch.state[index] = pos; + input->parse_input_event(st); + } else { + if (!touch.state.has(index)) // Defensive + break; + touch.state.erase(index); + input->parse_input_event(st); + } + } break; + + case XI_TouchUpdate: { + + Map<int, Vector2>::Element *curr_pos_elem = touch.state.find(index); + if (!curr_pos_elem) { // Defensive + break; + } + + if (curr_pos_elem->value() != pos) { + + Ref<InputEventScreenDrag> sd; + sd.instance(); + sd->set_index(index); + sd->set_position(pos); + sd->set_relative(pos - curr_pos_elem->value()); + input->parse_input_event(sd); + + curr_pos_elem->value() = pos; + } + } break; + } + } + } + XFreeEventData(x11_display, &event.xcookie); +#endif + switch (event.type) { case Expose: Main::force_redraw(); @@ -1493,6 +1766,12 @@ void OS_X11::process_xevents() { ButtonPressMask | ButtonReleaseMask | PointerMotionMask, GrabModeAsync, GrabModeAsync, x11_window, None, CurrentTime); } +#ifdef TOUCH_ENABLED + // Grab touch devices to avoid OS gesture interference + /*for (int i = 0; i < touch.devices.size(); ++i) { + XIGrabDevice(x11_display, touch.devices[i], x11_window, CurrentTime, None, XIGrabModeAsync, XIGrabModeAsync, False, &touch.event_mask); + }*/ +#endif if (xic) { XSetICFocus(xic); } @@ -1509,24 +1788,30 @@ void OS_X11::process_xevents() { } XUngrabPointer(x11_display, CurrentTime); } +#ifdef TOUCH_ENABLED + // Ungrab touch devices so input works as usual while we are unfocused + /*for (int i = 0; i < touch.devices.size(); ++i) { + XIUngrabDevice(x11_display, touch.devices[i], CurrentTime); + }*/ + + // Release every pointer to avoid sticky points + for (Map<int, Vector2>::Element *E = touch.state.front(); E; E = E->next()) { + + Ref<InputEventScreenTouch> st; + st.instance(); + st->set_index(E->key()); + st->set_position(E->get()); + input->parse_input_event(st); + } + touch.state.clear(); +#endif 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) && - (event.xconfigure.height == current_videomode.height)) - break; - - current_videomode.width = event.xconfigure.width; - current_videomode.height = event.xconfigure.height; + _window_changed(&event); break; case ButtonPress: case ButtonRelease: { @@ -1672,8 +1957,12 @@ void OS_X11::process_xevents() { e = event; req = &(e.xselectionrequest); - if (req->target == XA_STRING || req->target == XInternAtom(x11_display, "COMPOUND_TEXT", 0) || - req->target == XInternAtom(x11_display, "UTF8_STRING", 0)) { + if (req->target == XInternAtom(x11_display, "UTF8_STRING", 0) || + req->target == XInternAtom(x11_display, "COMPOUND_TEXT", 0) || + req->target == XInternAtom(x11_display, "TEXT", 0) || + req->target == XA_STRING || + req->target == XInternAtom(x11_display, "text/plain;charset=utf-8", 0) || + req->target == XInternAtom(x11_display, "text/plain", 0)) { CharString clip = OS::get_clipboard().utf8(); XChangeProperty(x11_display, req->requestor, @@ -1686,26 +1975,40 @@ void OS_X11::process_xevents() { respond.xselection.property = req->property; } else if (req->target == XInternAtom(x11_display, "TARGETS", 0)) { - Atom data[2]; - data[0] = XInternAtom(x11_display, "UTF8_STRING", 0); - data[1] = XA_STRING; - XChangeProperty(x11_display, req->requestor, req->property, req->target, - 8, PropModeReplace, (unsigned char *)&data, - sizeof(data)); + Atom data[7]; + data[0] = XInternAtom(x11_display, "TARGETS", 0); + data[1] = XInternAtom(x11_display, "UTF8_STRING", 0); + data[2] = XInternAtom(x11_display, "COMPOUND_TEXT", 0); + data[3] = XInternAtom(x11_display, "TEXT", 0); + data[4] = XA_STRING; + data[5] = XInternAtom(x11_display, "text/plain;charset=utf-8", 0); + data[6] = XInternAtom(x11_display, "text/plain", 0); + + XChangeProperty(x11_display, + req->requestor, + req->property, + XA_ATOM, + 32, + PropModeReplace, + (unsigned char *)&data, + sizeof(data) / sizeof(data[0])); respond.xselection.property = req->property; } else { - printf("No String %x\n", - (int)req->target); + char *targetname = XGetAtomName(x11_display, req->target); + printf("No Target '%s'\n", targetname); + if (targetname) + XFree(targetname); respond.xselection.property = None; } + respond.xselection.type = SelectionNotify; respond.xselection.display = req->display; respond.xselection.requestor = req->requestor; respond.xselection.selection = req->selection; respond.xselection.target = req->target; respond.xselection.time = req->time; - XSendEvent(x11_display, req->requestor, 0, 0, &respond); + XSendEvent(x11_display, req->requestor, True, NoEventMask, &respond); XFlush(x11_display); } break; @@ -1958,6 +2261,39 @@ bool OS_X11::_check_internal_feature_support(const String &p_feature) { return p_feature == "pc" || p_feature == "s3tc"; } +String OS_X11::get_config_path() const { + + if (has_environment("XDG_CONFIG_HOME")) { + return get_environment("XDG_CONFIG_HOME"); + } else if (has_environment("HOME")) { + return get_environment("HOME").plus_file(".config"); + } else { + return "."; + } +} + +String OS_X11::get_data_path() const { + + if (has_environment("XDG_DATA_HOME")) { + return get_environment("XDG_DATA_HOME"); + } else if (has_environment("HOME")) { + return get_environment("HOME").plus_file(".local/share"); + } else { + return get_config_path(); + } +} + +String OS_X11::get_cache_path() const { + + if (has_environment("XDG_CACHE_HOME")) { + return get_environment("XDG_CACHE_HOME"); + } else if (has_environment("HOME")) { + return get_environment("HOME").plus_file(".cache"); + } else { + return get_config_path(); + } +} + String OS_X11::get_system_dir(SystemDir p_dir) const { String xdgparam; @@ -2034,6 +2370,48 @@ void OS_X11::set_cursor_shape(CursorShape p_shape) { current_cursor = p_shape; } +void OS_X11::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { + if (p_cursor.is_valid()) { + Ref<Texture> texture = p_cursor; + Ref<Image> image = texture->get_data(); + + ERR_FAIL_COND(texture->get_width() != 32 || texture->get_height() != 32); + + // Create the cursor structure + XcursorImage *cursor_image = XcursorImageCreate(texture->get_width(), texture->get_height()); + XcursorUInt image_size = 32 * 32; + XcursorDim size = sizeof(XcursorPixel) * image_size; + + cursor_image->version = 1; + cursor_image->size = size; + cursor_image->xhot = p_hotspot.x; + cursor_image->yhot = p_hotspot.y; + + // allocate memory to contain the whole file + cursor_image->pixels = (XcursorPixel *)malloc(size); + + image->lock(); + + for (XcursorPixel index = 0; index < image_size; index++) { + int column_index = floor(index / 32); + int row_index = index % 32; + + *(cursor_image->pixels + index) = image->get_pixel(row_index, column_index).to_argb32(); + } + + image->unlock(); + + ERR_FAIL_COND(cursor_image->pixels == NULL); + + // Save it for a further usage + cursors[p_shape] = XcursorImageLoadCursor(x11_display, cursor_image); + + if (p_shape == CURSOR_ARROW) { + XDefineCursor(x11_display, x11_window, cursors[p_shape]); + } + } +} + void OS_X11::release_rendering_thread() { context_gl->release_current(); @@ -2097,6 +2475,13 @@ void OS_X11::set_icon(const Ref<Image> &p_icon) { XFlush(x11_display); } +void OS_X11::force_process_input() { + process_xevents(); // get rid of pending events +#ifdef JOYDEV_ENABLED + joypad->process_joypads(); +#endif +} + void OS_X11::run() { force_quit = false; @@ -2132,11 +2517,11 @@ String OS_X11::get_joy_guid(int p_device) const { return input->get_joy_guid_remapped(p_device); } -void OS_X11::set_use_vsync(bool p_enable) { +void OS_X11::_set_use_vsync(bool p_enable) { if (context_gl) return context_gl->set_use_vsync(p_enable); } - +/* bool OS_X11::is_vsync_enabled() const { if (context_gl) @@ -2144,7 +2529,7 @@ bool OS_X11::is_vsync_enabled() const { return true; } - +*/ void OS_X11::set_context(int p_context) { XClassHint *classHint = XAllocClassHint(); @@ -2209,48 +2594,66 @@ static String get_mountpoint(const String &p_path) { } Error OS_X11::move_to_trash(const String &p_path) { - String trashcan = ""; + String trash_can = ""; String mnt = get_mountpoint(p_path); + // If there is a directory "[Mountpoint]/.Trash-[UID]/files", use it as the trash can. if (mnt != "") { String path(mnt + "/.Trash-" + itos(getuid()) + "/files"); struct stat s; if (!stat(path.utf8().get_data(), &s)) { - trashcan = path; + trash_can = path; } } - if (trashcan == "") { + // Otherwise, if ${XDG_DATA_HOME} is defined, use "${XDG_DATA_HOME}/Trash/files" as the trash can. + if (trash_can == "") { char *dhome = getenv("XDG_DATA_HOME"); if (dhome) { - trashcan = String(dhome) + "/Trash/files"; + trash_can = String(dhome) + "/Trash/files"; } } - if (trashcan == "") { + // Otherwise, if ${HOME} is defined, use "${HOME}/.local/share/Trash/files" as the trash can. + if (trash_can == "") { char *home = getenv("HOME"); if (home) { - trashcan = String(home) + "/.local/share/Trash/files"; + trash_can = String(home) + "/.local/share/Trash/files"; } } - if (trashcan == "") { - ERR_PRINTS("move_to_trash: Could not determine trashcan location"); + // Issue an error if none of the previous locations is appropriate for the trash can. + if (trash_can == "") { + ERR_PRINTS("move_to_trash: Could not determine the trash can location"); return FAILED; } - List<String> args; - args.push_back("-p"); - args.push_back(trashcan); - Error err = execute("mkdir", args, true); - if (err == OK) { - List<String> args2; - args2.push_back(p_path); - args2.push_back(trashcan); - err = execute("mv", args2, true); + // Create needed directories for decided trash can location. + DirAccess *dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + Error err = dir_access->make_dir_recursive(trash_can); + memdelete(dir_access); + + // Issue an error if trash can is not created proprely. + if (err != OK) { + ERR_PRINTS("move_to_trash: Could not create the trash can \"" + trash_can + "\""); + return err; + } + + // The trash can is successfully created, now move the given resource to it. + // Do not use DirAccess:rename() because it can't move files across multiple mountpoints. + List<String> mv_args; + mv_args.push_back(p_path); + mv_args.push_back(trash_can); + int retval; + err = execute("mv", mv_args, true, NULL, NULL, &retval); + + // Issue an error if "mv" failed to move the given resource to the trash can. + if (err != OK || retval != 0) { + ERR_PRINTS("move_to_trash: Could not move the resource \"" + p_path + "\" to the trash can \"" + trash_can + "\""); + return FAILED; } - return err; + return OK; } OS::LatinKeyboardVariant OS_X11::get_latin_keyboard_variant() const { diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h index b71b456d49..610dba0716 100644 --- a/platform/x11/os_x11.h +++ b/platform/x11/os_x11.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifndef OS_X11_H #define OS_X11_H @@ -42,15 +43,15 @@ #include "main/input_default.h" #include "power_x11.h" #include "servers/audio_server.h" -#include "servers/physics_2d/physics_2d_server_sw.h" -#include "servers/physics_2d/physics_2d_server_wrap_mt.h" -#include "servers/physics_server.h" #include "servers/visual/rasterizer.h" #include <X11/Xcursor/Xcursor.h> #include <X11/Xlib.h> #include <X11/extensions/Xrandr.h> #include <X11/keysym.h> +#ifdef TOUCH_ENABLED +#include <X11/extensions/XInput2.h> +#endif // Hints for X11 fullscreen typedef struct { @@ -120,11 +121,17 @@ class OS_X11 : public OS_Unix { Point2i last_click_pos; uint64_t last_click_ms; uint32_t last_button_state; +#ifdef TOUCH_ENABLED + struct { + int opcode; + Vector<int> devices; + XIEventMask event_mask; + Map<int, Vector2> state; + } touch; +#endif - PhysicsServer *physics_server; unsigned int get_mouse_button_state(unsigned int p_x11_state); void get_key_modifier_state(unsigned int p_x11_state, Ref<InputEventWithModifiers> state); - Physics2DServer *physics_2d_server; MouseMode mouse_mode; Point2i center; @@ -171,6 +178,7 @@ class OS_X11 : public OS_Unix { bool maximized; //void set_wm_border(bool p_enabled); void set_wm_fullscreen(bool p_enabled); + void set_wm_above(bool p_enabled); typedef xrr_monitor_info *(*xrr_get_monitors_t)(Display *dpy, Window window, Bool get_active, int *nmonitors); typedef void (*xrr_free_monitors_t)(xrr_monitor_info *monitors); @@ -180,23 +188,21 @@ class OS_X11 : public OS_Unix { Bool xrandr_ext_ok; protected: - virtual int get_video_driver_count() const; - virtual const char *get_video_driver_name(int p_driver) const; - virtual VideoMode get_default_video_mode() const; - - virtual int get_audio_driver_count() const; - virtual const char *get_audio_driver_name(int p_driver) const; - virtual void initialize_core(); - virtual void initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver); + virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver); virtual void finalize(); virtual void set_main_loop(MainLoop *p_main_loop); + void _window_changed(XEvent *event); + + bool is_window_maximize_allowed(); + public: virtual String get_name(); virtual void set_cursor_shape(CursorShape p_shape); + virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); void set_mouse_mode(MouseMode p_mode); MouseMode get_mouse_mode() const; @@ -219,6 +225,10 @@ public: virtual void make_rendering_thread(); virtual void swap_buffers(); + virtual String get_config_path() const; + virtual String get_data_path() const; + virtual String get_cache_path() const; + virtual String get_system_dir(SystemDir p_dir) const; virtual Error shell_open(String p_uri); @@ -236,6 +246,7 @@ public: virtual Point2 get_window_position() const; virtual void set_window_position(const Point2 &p_position); virtual Size2 get_window_size() const; + virtual Size2 get_real_window_size() const; virtual void set_window_size(const Size2 p_size); virtual void set_window_fullscreen(bool p_enabled); virtual bool is_window_fullscreen() const; @@ -245,12 +256,16 @@ public: virtual bool is_window_minimized() const; virtual void set_window_maximized(bool p_enabled); virtual bool is_window_maximized() const; + virtual void set_window_always_on_top(bool p_enabled); + virtual bool is_window_always_on_top() const; virtual void request_attention(); - virtual void set_borderless_window(int p_borderless); + virtual void set_borderless_window(bool p_borderless); virtual bool get_borderless_window(); virtual void set_ime_position(const Point2 &p_pos); + virtual String get_unique_id() const; + virtual void move_window_to_foreground(); virtual void alert(const String &p_alert, const String &p_title = "ALERT!"); @@ -259,8 +274,8 @@ public: virtual void set_context(int p_context); - virtual void set_use_vsync(bool p_enable); - virtual bool is_vsync_enabled() const; + virtual void _set_use_vsync(bool p_enable); + //virtual bool is_vsync_enabled() const; virtual OS::PowerState get_power_state(); virtual int get_power_seconds_left(); @@ -268,6 +283,7 @@ public: virtual bool _check_internal_feature_support(const String &p_feature); + virtual void force_process_input(); void run(); void disable_crash_handler(); diff --git a/platform/x11/platform_config.h b/platform/x11/platform_config.h index 85af12372f..b757be49c3 100644 --- a/platform/x11/platform_config.h +++ b/platform/x11/platform_config.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -27,6 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ + #ifdef __linux__ #include <alloca.h> #endif @@ -36,3 +37,4 @@ #endif #define GLES3_INCLUDE_H "glad/glad.h" +#define GLES2_INCLUDE_H "glad/glad.h" diff --git a/platform/x11/power_x11.cpp b/platform/x11/power_x11.cpp index 76ff7f91fb..4ac331d212 100644 --- a/platform/x11/power_x11.cpp +++ b/platform/x11/power_x11.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ @@ -542,8 +542,10 @@ bool PowerX11::UpdatePowerInfo() { return false; } -PowerX11::PowerX11() - : nsecs_left(-1), percent_left(-1), power_state(OS::POWERSTATE_UNKNOWN) { +PowerX11::PowerX11() : + nsecs_left(-1), + percent_left(-1), + power_state(OS::POWERSTATE_UNKNOWN) { } PowerX11::~PowerX11() { diff --git a/platform/x11/power_x11.h b/platform/x11/power_x11.h index 7fc258bc0d..4077887998 100644 --- a/platform/x11/power_x11.h +++ b/platform/x11/power_x11.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 */ |