summaryrefslogtreecommitdiff
path: root/platform
diff options
context:
space:
mode:
Diffstat (limited to 'platform')
-rw-r--r--platform/SCsub30
-rw-r--r--platform/android/SCsub12
-rw-r--r--platform/android/build.gradle.template7
-rw-r--r--platform/android/detect.py49
-rw-r--r--platform/android/export/export.cpp16
-rw-r--r--platform/android/globals/global_defaults.cpp8
-rw-r--r--platform/android/godot_android.cpp28
-rw-r--r--platform/android/java/gradlew.bat180
-rw-r--r--platform/android/java/src/org/godotengine/godot/Godot.java23
-rw-r--r--platform/android/java/src/org/godotengine/godot/GodotLib.java1
-rw-r--r--platform/android/java/src/org/godotengine/godot/GodotView.java8
-rw-r--r--platform/android/java/src/org/godotengine/godot/input/GodotTextInputWrapper.java73
-rw-r--r--platform/android/java_glue.cpp79
-rw-r--r--platform/android/java_glue.h1
-rw-r--r--platform/android/os_android.cpp57
-rw-r--r--platform/android/os_android.h17
-rw-r--r--platform/android/platform_config.h1
-rw-r--r--platform/haiku/context_gl_haiku.cpp2
-rw-r--r--platform/haiku/context_gl_haiku.h2
-rw-r--r--platform/haiku/detect.py2
-rw-r--r--platform/haiku/os_haiku.cpp60
-rw-r--r--platform/haiku/os_haiku.h11
-rw-r--r--platform/iphone/SCsub12
-rw-r--r--platform/iphone/app_delegate.mm20
-rw-r--r--platform/iphone/detect.py50
-rw-r--r--platform/iphone/export/export.cpp473
-rw-r--r--platform/iphone/game_center.h9
-rw-r--r--platform/iphone/game_center.mm86
-rw-r--r--platform/iphone/globals/global_defaults.cpp7
-rw-r--r--platform/iphone/godot_iphone.cpp7
-rw-r--r--platform/iphone/in_app_store.mm3
-rw-r--r--platform/iphone/os_iphone.cpp120
-rw-r--r--platform/iphone/os_iphone.h19
-rw-r--r--platform/iphone/platform_config.h2
-rw-r--r--platform/javascript/SCsub34
-rw-r--r--platform/javascript/api/api.cpp73
-rw-r--r--platform/javascript/api/api.h31
-rw-r--r--platform/javascript/api/javascript_eval.h (renamed from platform/javascript/javascript_eval.h)3
-rw-r--r--platform/javascript/audio_driver_javascript.cpp79
-rw-r--r--platform/javascript/audio_driver_javascript.h15
-rw-r--r--platform/javascript/audio_server_javascript.cpp853
-rw-r--r--platform/javascript/audio_server_javascript.h229
-rw-r--r--platform/javascript/detect.py25
-rw-r--r--platform/javascript/engine.js219
-rw-r--r--platform/javascript/export/export.cpp134
-rw-r--r--platform/javascript/http_client.h.inc48
-rw-r--r--platform/javascript/http_client_javascript.cpp282
-rw-r--r--platform/javascript/http_request.h74
-rw-r--r--platform/javascript/http_request.js145
-rw-r--r--platform/javascript/javascript_eval.cpp99
-rw-r--r--platform/javascript/javascript_main.cpp1
-rw-r--r--platform/javascript/os_javascript.cpp69
-rw-r--r--platform/javascript/os_javascript.h21
-rw-r--r--platform/javascript/pre.js (renamed from platform/javascript/pre_wasm.js)1
-rw-r--r--platform/javascript/pre_asmjs.js3
-rw-r--r--platform/osx/crash_handler_osx.mm1
-rw-r--r--platform/osx/detect.py13
-rw-r--r--platform/osx/export/export.cpp459
-rw-r--r--platform/osx/os_osx.h29
-rw-r--r--platform/osx/os_osx.mm346
-rw-r--r--platform/register_platform_apis.h36
-rw-r--r--platform/server/detect.py3
-rw-r--r--platform/server/os_server.cpp18
-rw-r--r--platform/server/os_server.h6
-rw-r--r--platform/uwp/SCsub1
-rw-r--r--platform/uwp/app.h1
-rw-r--r--platform/uwp/detect.py4
-rw-r--r--platform/uwp/export/export.cpp6
-rw-r--r--platform/uwp/gl_context_egl.cpp39
-rw-r--r--platform/uwp/os_uwp.cpp38
-rw-r--r--platform/uwp/os_uwp.h11
-rw-r--r--platform/windows/context_gl_win.cpp19
-rw-r--r--platform/windows/context_gl_win.h13
-rw-r--r--platform/windows/crash_handler_win.cpp1
-rw-r--r--platform/windows/detect.py7
-rw-r--r--platform/windows/godot_res.rc8
-rw-r--r--platform/windows/key_mapping_win.cpp2
-rw-r--r--platform/windows/os_windows.cpp90
-rw-r--r--platform/windows/os_windows.h20
-rw-r--r--platform/x11/context_gl_x11.cpp2
-rw-r--r--platform/x11/context_gl_x11.h2
-rw-r--r--platform/x11/crash_handler_x11.cpp1
-rw-r--r--platform/x11/detect.py10
-rw-r--r--platform/x11/os_x11.cpp188
-rw-r--r--platform/x11/os_x11.h17
85 files changed, 2714 insertions, 2590 deletions
diff --git a/platform/SCsub b/platform/SCsub
new file mode 100644
index 0000000000..4ef23ab053
--- /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'
+f = open_utf8('register_platform_apis.gen.cpp', 'w')
+f.write(reg_apis_inc)
+f.write(reg_apis)
+f.write(unreg_apis)
+f.close()
+platform_sources.append('register_platform_apis.gen.cpp')
+
+env.Prepend(LIBS=env.Library('platform', platform_sources))
+
+Export('env')
diff --git a/platform/android/SCsub b/platform/android/SCsub
index e9a370869f..7fa0262359 100644
--- a/platform/android/SCsub
+++ b/platform/android/SCsub
@@ -46,8 +46,18 @@ gradle_baseout = open_utf8(abspath + "/java/build.gradle", "w")
gradle_text = gradle_basein.read()
-
+gradle_maven_flat_text = ""
+if len(env.android_flat_dirs) > 0:
+ gradle_maven_flat_text += "flatDir {\n"
+ gradle_maven_flat_text += "\tdirs "
+ for x in env.android_flat_dirs:
+ gradle_maven_flat_text += "'" + x + "',"
+
+ gradle_maven_flat_text = gradle_maven_flat_text[:-1]
+ gradle_maven_flat_text += "\n\t}\n"
+
gradle_maven_repos_text = ""
+gradle_maven_repos_text += gradle_maven_flat_text
if len(env.android_maven_repos) > 0:
gradle_maven_repos_text += ""
diff --git a/platform/android/build.gradle.template b/platform/android/build.gradle.template
index 1df56ce621..11c49fbb50 100644
--- a/platform/android/build.gradle.template
+++ b/platform/android/build.gradle.template
@@ -1,6 +1,7 @@
buildscript {
repositories {
jcenter()
+ $$GRADLE_REPOSITORY_URLS$$
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
@@ -30,7 +31,7 @@ android {
disable 'MissingTranslation'
}
- compileSdkVersion 23
+ compileSdkVersion 24
buildToolsVersion "26.0.1"
useLibrary 'org.apache.http.legacy'
@@ -54,11 +55,11 @@ android {
]
res.srcDirs = [
'res'
- $$GRADLE_RES_DIRS$$
+ $$GRADLE_RES_DIRS$$
]
aidl.srcDirs = [
'aidl'
- $$GRADLE_AIDL_DIRS$$
+ $$GRADLE_AIDL_DIRS$$
]
assets.srcDirs = [
'assets'
diff --git a/platform/android/detect.py b/platform/android/detect.py
index 13fc4ee810..bc67f6e6dc 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():
@@ -25,7 +26,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)
]
@@ -172,20 +173,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(int(env['ndk_platform'].split("-")[1]))])
+ 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 +244,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())
@@ -248,3 +268,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 = map(lambda x: string.strip(x), 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/export/export.cpp b/platform/android/export/export.cpp
index 9fe1f291d6..e1ff12c5ac 100644
--- a/platform/android/export/export.cpp
+++ b/platform/android/export/export.cpp
@@ -267,7 +267,6 @@ class EditorExportAndroid : public EditorExportPlatform {
if (different) {
- print_line("DIFFERENT!");
Vector<Device> ndevices;
for (int i = 0; i < ldevices.size(); i++) {
@@ -371,7 +370,7 @@ class EditorExportAndroid : public EditorExportPlatform {
}
if (aname == "") {
- aname = _MKSTR(VERSION_NAME);
+ aname = VERSION_NAME;
}
return aname;
@@ -946,16 +945,17 @@ public:
public:
virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) {
- int api = p_preset->get("graphics/api");
+ // Reenable when a GLES 2.0 backend is readded
+ /*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");
}
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"), ""));
@@ -1067,7 +1067,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();
@@ -1292,7 +1292,7 @@ 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");
diff --git a/platform/android/globals/global_defaults.cpp b/platform/android/globals/global_defaults.cpp
index c73b578154..0e1c17e9c8 100644
--- a/platform/android/globals/global_defaults.cpp
+++ b/platform/android/globals/global_defaults.cpp
@@ -31,12 +31,4 @@
#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/godot_android.cpp b/platform/android/godot_android.cpp
index 8235683496..f9bcbadc24 100644
--- a/platform/android/godot_android.cpp
+++ b/platform/android/godot_android.cpp
@@ -29,23 +29,23 @@
/*************************************************************************/
#ifdef ANDROID_NATIVE_ACTIVITY
-#include <errno.h>
-#include <jni.h>
-
-#include <EGL/egl.h>
-#include <GLES2/gl2.h>
-
+#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__))
@@ -669,6 +669,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 +702,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 +741,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 +842,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) {
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/Godot.java b/platform/android/java/src/org/godotengine/godot/Godot.java
index 053dfa631a..41dcba5c2c 100644
--- a/platform/android/java/src/org/godotengine/godot/Godot.java
+++ b/platform/android/java/src/org/godotengine/godot/Godot.java
@@ -191,6 +191,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
protected void onMainPause() {}
protected void onMainResume() {}
protected void onMainDestroy() {}
+ protected boolean onMainBackPressed() { return false; }
protected void onGLDrawFrame(GL10 gl) {}
protected void onGLSurfaceChanged(GL10 gl, int width, int height) {} // singletons will always miss first onGLSurfaceChanged call
@@ -218,6 +219,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
private SensorManager mSensorManager;
private Sensor mAccelerometer;
+ private Sensor mGravity;
private Sensor mMagnetometer;
private Sensor mGyroscope;
@@ -434,6 +436,8 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
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);
@@ -666,6 +670,7 @@ 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);
@@ -733,13 +738,16 @@ 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);
}
}
});
@@ -767,9 +775,16 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
*/
@Override public void onBackPressed() {
+ boolean shouldQuit = true;
+
+ for(int i=0;i<singleton_count;i++) {
+ if (singletons[i].onMainBackPressed()) {
+ shouldQuit = false;
+ }
+ }
System.out.printf("** BACK REQUEST!\n");
- if (mView != null) {
+ if (shouldQuit && mView != null) {
mView.queueEvent(new Runnable() {
@Override
public void run() {
diff --git a/platform/android/java/src/org/godotengine/godot/GodotLib.java b/platform/android/java/src/org/godotengine/godot/GodotLib.java
index e0ed4cd38c..6b84ad6555 100644
--- a/platform/android/java/src/org/godotengine/godot/GodotLib.java
+++ b/platform/android/java/src/org/godotengine/godot/GodotLib.java
@@ -53,6 +53,7 @@ public class GodotLib {
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);
diff --git a/platform/android/java/src/org/godotengine/godot/GodotView.java b/platform/android/java/src/org/godotengine/godot/GodotView.java
index 3c2ad7cc59..b807b952d4 100644
--- a/platform/android/java/src/org/godotengine/godot/GodotView.java
+++ b/platform/android/java/src/org/godotengine/godot/GodotView.java
@@ -285,13 +285,7 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener {
@Override public boolean onKeyDown(final int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
- queueEvent(new Runnable() {
- @Override
- public void run() {
- GodotLib.back();
- }
- });
-
+ activity.onBackPressed();
// press 'back' button should not terminate program
//normal handle 'back' event in game logic
return true;
diff --git a/platform/android/java/src/org/godotengine/godot/input/GodotTextInputWrapper.java b/platform/android/java/src/org/godotengine/godot/input/GodotTextInputWrapper.java
index 04669a3b0c..ac424ab9f8 100644
--- a/platform/android/java/src/org/godotengine/godot/input/GodotTextInputWrapper.java
+++ b/platform/android/java/src/org/godotengine/godot/input/GodotTextInputWrapper.java
@@ -88,79 +88,48 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
public void beforeTextChanged(final CharSequence pCharSequence, final int start, final int count, final int after) {
//Log.d(TAG, "beforeTextChanged(" + pCharSequence + ")start: " + start + ",count: " + count + ",after: " + after);
- for (int i=0;i<count;i++){
- mView.queueEvent(new Runnable() {
- @Override
- public void run() {
+ mView.queueEvent(new Runnable() {
+ @Override
+ public void run() {
+ for (int i = 0; i < count; ++i) {
GodotLib.key(KeyEvent.KEYCODE_DEL, 0, true);
GodotLib.key(KeyEvent.KEYCODE_DEL, 0, false);
}
- });
- }
+ }
+ });
}
@Override
public void onTextChanged(final CharSequence pCharSequence, final int start, final int before, final int count) {
//Log.d(TAG, "onTextChanged(" + pCharSequence + ")start: " + start + ",count: " + count + ",before: " + before);
- for (int i=start;i<start+count;i++){
- final int ch = pCharSequence.charAt(i);
- mView.queueEvent(new Runnable() {
- @Override
- public void run() {
+ mView.queueEvent(new Runnable() {
+ @Override
+ public void run() {
+ for (int i = start; i < start + count; ++i) {
+ final int ch = pCharSequence.charAt(i);
GodotLib.key(0, ch, true);
GodotLib.key(0, ch, false);
}
- });
- }
-
+ }
+ });
}
@Override
public boolean onEditorAction(final TextView pTextView, final int pActionID, final KeyEvent pKeyEvent) {
if (this.mEdit == pTextView && this.isFullScreenEdit()) {
- // user press the action button, delete all old text and insert new text
- for (int i = this.mOriginText.length(); i > 0; i--) {
- mView.queueEvent(new Runnable() {
- @Override
- public void run() {
- GodotLib.key(KeyEvent.KEYCODE_DEL, 0, true);
- GodotLib.key(KeyEvent.KEYCODE_DEL, 0, false);
- }
- });
+ final String characters = pKeyEvent.getCharacters();
- /*
- if (BuildConfig.DEBUG) {
- Log.d(TAG, "deleteBackward");
- }
- */
- }
- String text = pTextView.getText().toString();
-
- /* If user input nothing, translate "\n" to engine. */
- if (text.compareTo("") == 0) {
- text = "\n";
- }
-
- if ('\n' != text.charAt(text.length() - 1)) {
- text += '\n';
- }
-
- for(int i = 0; i < text.length(); i++) {
- final int ch = text.codePointAt(i);
- mView.queueEvent(new Runnable() {
- @Override
- public void run() {
+ mView.queueEvent(new Runnable() {
+ @Override
+ public void run() {
+ for (int i = 0; i < characters.length(); i++) {
+ final int ch = characters.codePointAt(i);
GodotLib.key(0, ch, true);
GodotLib.key(0, ch, false);
}
- });
- }
- /*
- if (BuildConfig.DEBUG) {
- Log.d(TAG, "insertText(" + insertText + ")");
- }
- */
+ }
+ });
}
if (pActionID == EditorInfo.IME_ACTION_DONE) {
diff --git a/platform/android/java_glue.cpp b/platform/android/java_glue.cpp
index 6819a7e20f..40dfe6d909 100644
--- a/platform/android/java_glue.cpp
+++ b/platform/android/java_glue.cpp
@@ -34,6 +34,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"
@@ -602,30 +603,18 @@ struct TST {
TST tst;
-struct JAndroidPointerEvent {
-
- Vector<OS_Android::TouchPos> points;
- int pointer;
- int what;
-};
-
-static List<JAndroidPointerEvent> pointer_events;
-static List<Ref<InputEvent> > key_events;
-static List<OS_Android::JoypadEvent> joy_events;
static bool initialized = false;
static int step = 0;
static bool resized = false;
static bool resized_reload = false;
-static bool go_back_request = false;
static Size2 new_size;
static Vector3 accelerometer;
+static Vector3 gravity;
static Vector3 magnetometer;
static Vector3 gyroscope;
static HashMap<String, JNISingleton *> jni_singletons;
static jobject godot_io;
-static Vector<int> joy_device_ids;
-
typedef void (*GFXInitFunc)(void *ud, bool gl2);
static jmethodID _on_video_init = 0;
@@ -658,7 +647,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);
@@ -836,7 +825,7 @@ 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];
@@ -965,7 +954,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jo
__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();
}
@@ -998,7 +987,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *en
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jobject obj) {
- go_back_request = true;
+ os_android->main_loop_request_go_back();
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jobject obj) {
@@ -1023,38 +1012,10 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, job
//__android_log_print(ANDROID_LOG_INFO,"godot","**STEP EVENT! - %p-%i\n",env,Thread::get_caller_id());
- while (pointer_events.size()) {
-
- JAndroidPointerEvent jpe = pointer_events.front()->get();
- os_android->process_touch(jpe.what, jpe.pointer, jpe.points);
-
- pointer_events.pop_front();
- }
-
- while (key_events.size()) {
-
- Ref<InputEvent> event = key_events.front()->get();
- os_android->process_event(event);
-
- key_events.pop_front();
- };
-
- while (joy_events.size()) {
-
- OS_Android::JoypadEvent event = joy_events.front()->get();
- os_android->process_joy_event(event);
-
- joy_events.pop_front();
- }
-
- if (go_back_request) {
-
- os_android->main_loop_request_go_back();
- go_back_request = false;
- }
-
os_android->process_accelerometer(accelerometer);
+ os_android->process_gravity(gravity);
+
os_android->process_magnetometer(magnetometer);
os_android->process_gyroscope(gyroscope);
@@ -1083,12 +1044,8 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch(JNIEnv *env, jo
points.push_back(tp);
}
- JAndroidPointerEvent jpe;
- jpe.pointer = pointer;
- jpe.points = points;
- jpe.what = ev;
+ os_android->process_touch(ev, pointer, points);
- pointer_events.push_back(jpe);
/*
if (os_android)
os_android->process_touch(ev,pointer,points);
@@ -1358,7 +1315,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env
jevent.index = p_button;
jevent.pressed = p_pressed;
- joy_events.push_back(jevent);
+ os_android->process_joy_event(jevent);
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jobject obj, jint p_device, jint p_axis, jfloat p_value) {
@@ -1369,7 +1326,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env,
jevent.index = p_axis;
jevent.value = p_value;
- joy_events.push_back(jevent);
+ os_android->process_joy_event(jevent);
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, jobject obj, jint p_device, jint p_hat_x, jint p_hat_y) {
@@ -1390,7 +1347,8 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, j
hat |= InputDefault::HAT_MASK_DOWN;
}
jevent.hat = hat;
- joy_events.push_back(jevent);
+
+ os_android->process_joy_event(jevent);
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyconnectionchanged(JNIEnv *env, jobject obj, jint p_device, jboolean p_connected, jstring p_name) {
@@ -1403,6 +1361,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyconnectionchanged(
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jobject obj, jint p_scancode, jint p_unicode_char, jboolean p_pressed) {
Ref<InputEventKey> ievent;
+ ievent.instance();
int val = p_unicode_char;
int scancode = android_get_keysym(p_scancode);
ievent->set_scancode(scancode);
@@ -1421,16 +1380,20 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jobj
ievent->set_unicode(KEY_ENTER);
} else if (p_scancode == 4) {
- go_back_request = true;
+ os_android->main_loop_request_go_back();
}
- key_events.push_back(ievent);
+ os_android->process_event(ievent);
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_accelerometer(JNIEnv *env, jobject obj, jfloat x, jfloat y, jfloat z) {
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);
}
@@ -1464,7 +1427,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);
}
diff --git a/platform/android/java_glue.h b/platform/android/java_glue.h
index 0aa2489813..4790c65617 100644
--- a/platform/android/java_glue.h
+++ b/platform/android/java_glue.h
@@ -50,6 +50,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 473a093077..b575f15559 100644
--- a/platform/android/os_android.cpp
+++ b/platform/android/os_android.cpp
@@ -65,12 +65,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,13 +114,6 @@ void OS_Android::initialize_core() {
#endif
}
-void OS_Android::initialize_logger() {
- Vector<Logger *> loggers;
- loggers.push_back(memnew(AndroidLogger));
- 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);
@@ -155,11 +142,6 @@ 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");
@@ -499,6 +481,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);
@@ -616,13 +603,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];
@@ -645,7 +632,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) {
@@ -710,10 +696,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;
@@ -729,7 +732,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;
@@ -750,7 +753,9 @@ 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)));
}
OS_Android::~OS_Android() {
diff --git a/platform/android/os_android.h b/platform/android/os_android.h
index 0c78c198a8..3b7f55096e 100644
--- a/platform/android/os_android.h
+++ b/platform/android/os_android.h
@@ -38,9 +38,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 +48,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 +103,6 @@ private:
bool use_16bits_fbo;
VisualServer *visual_server;
- PhysicsServer *physics_server;
- Physics2DServer *physics_2d_server;
mutable String data_dir_cache;
@@ -121,7 +116,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,12 +141,9 @@ 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);
@@ -215,7 +207,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 +218,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 +236,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..b1f51c9256 100644
--- a/platform/android/platform_config.h
+++ b/platform/android/platform_config.h
@@ -28,3 +28,4 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include <alloca.h>
+#include <malloc.h>
diff --git a/platform/haiku/context_gl_haiku.cpp b/platform/haiku/context_gl_haiku.cpp
index 2b943df5ba..80d0bd78d5 100644
--- a/platform/haiku/context_gl_haiku.cpp
+++ b/platform/haiku/context_gl_haiku.cpp
@@ -29,7 +29,7 @@
/*************************************************************************/
#include "context_gl_haiku.h"
-#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED)
+#if defined(OPENGL_ENABLED)
ContextGL_Haiku::ContextGL_Haiku(HaikuDirectWindow *p_window) {
window = p_window;
diff --git a/platform/haiku/context_gl_haiku.h b/platform/haiku/context_gl_haiku.h
index 40daf43ab9..a9a13a2b7f 100644
--- a/platform/haiku/context_gl_haiku.h
+++ b/platform/haiku/context_gl_haiku.h
@@ -30,7 +30,7 @@
#ifndef CONTEXT_GL_HAIKU_H
#define CONTEXT_GL_HAIKU_H
-#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED)
+#if defined(OPENGL_ENABLED)
#include "drivers/gl_context/context_gl.h"
diff --git a/platform/haiku/detect.py b/platform/haiku/detect.py
index 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/os_haiku.cpp b/platform/haiku/os_haiku.cpp
index 9f2f88bb4e..ef5a065107 100644
--- a/platform/haiku/os_haiku.cpp
+++ b/platform/haiku/os_haiku.cpp
@@ -76,11 +76,7 @@ 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) {
@@ -105,12 +101,14 @@ void OS_Haiku::initialize(const VideoMode &p_desired, int p_video_driver, int p_
window->SetFlags(flags);
}
-#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED)
+#if defined(OPENGL_ENABLED)
context_gl = memnew(ContextGL_Haiku(window));
context_gl->initialize();
context_gl->make_current();
- rasterizer = memnew(RasterizerGLES2);
+ /* Port to GLES 3 rasterizer */
+ //rasterizer = memnew(RasterizerGLES2);
+
#endif
visual_server = memnew(VisualServerRaster(rasterizer));
@@ -130,13 +128,6 @@ 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);
@@ -153,15 +144,9 @@ 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) || defined(LEGACYGL_ENABLED)
+#if defined(OPENGL_ENABLED)
memdelete(context_gl);
#endif
}
@@ -331,3 +316,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 d2fafb9129..4ee54fb48d 100644
--- a/platform/haiku/os_haiku.h
+++ b/platform/haiku/os_haiku.h
@@ -38,8 +38,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,15 +50,13 @@ private:
Rasterizer *rasterizer;
VisualServer *visual_server;
VideoMode current_video_mode;
- PhysicsServer *physics_server;
- Physics2DServer *physics_2d_server;
PowerHaiku *power_manager;
#ifdef MEDIA_KIT_ENABLED
AudioDriverMediaKit driver_media_kit;
#endif
-#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED)
+#if defined(OPENGL_ENABLED)
ContextGL_Haiku *context_gl;
#endif
@@ -69,7 +65,6 @@ 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 void finalize();
@@ -122,6 +117,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/iphone/SCsub b/platform/iphone/SCsub
index 61798c5f87..550dfdd7d6 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.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.mm b/platform/iphone/app_delegate.mm
index da6ceacdac..8f2893e69e 100644
--- a/platform/iphone/app_delegate.mm
+++ b/platform/iphone/app_delegate.mm
@@ -81,7 +81,7 @@ void _set_keep_screen_on(bool p_enabled) {
extern int gargc;
extern char **gargv;
-extern int iphone_main(int, int, int, char **);
+extern int iphone_main(int, int, int, char **, String);
extern void iphone_finish();
CMMotionManager *motionManager;
@@ -393,15 +393,6 @@ static int frame_count = 0;
};
++frame_count;
- NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
- NSUserDomainMask, YES);
- NSString *documentsDirectory = [paths objectAtIndex:0];
- // NSString *documentsDirectory = [[[NSFileManager defaultManager]
- // URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask]
- // lastObject];
- OSIPhone::get_singleton()->set_data_dir(
- String::utf8([documentsDirectory UTF8String]));
-
NSString *locale_code = [[NSLocale currentLocale] localeIdentifier];
OSIPhone::get_singleton()->set_locale(
String::utf8([locale_code UTF8String]));
@@ -604,7 +595,11 @@ static int frame_count = 0;
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
- int err = iphone_main(backingWidth, backingHeight, gargc, gargv);
+ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
+ NSUserDomainMask, YES);
+ NSString *documentsDirectory = [paths objectAtIndex:0];
+
+ int err = iphone_main(backingWidth, backingHeight, gargc, gargv, String::utf8([documentsDirectory UTF8String]));
if (err != 0) {
// bail, things did not go very well for us, should probably output a message on screen with our error code...
exit(0);
@@ -643,6 +638,9 @@ static int frame_count = 0;
mainViewController = view_controller;
+ // prevent to stop music in another background app
+ [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:nil];
+
#ifdef MODULE_GAME_ANALYTICS_ENABLED
printf("********************* didFinishLaunchingWithOptions\n");
if (!ProjectSettings::get_singleton()->has("mobileapptracker/advertiser_id")) {
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 c91781ce1d..6aa1ed9f8d 100644
--- a/platform/iphone/export/export.cpp
+++ b/platform/iphone/export/export.cpp
@@ -56,11 +56,48 @@ 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);
@@ -96,6 +133,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 +168,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 +192,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 +208,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 +235,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 +264,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 {
@@ -397,12 +467,212 @@ Error EditorExportPlatformIOS::_codesign(String p_file, void *p_userdata) {
codesign_args.push_back("-s");
codesign_args.push_back(data->preset->get(data->debug ? "application/code_sign_identity_debug" : "application/code_sign_identity_release"));
codesign_args.push_back(p_file);
- return OS::get_singleton()->execute("/usr/bin/codesign", codesign_args, true);
+ return OS::get_singleton()->execute("codesign", codesign_args, true);
}
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 +697,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;
- 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");
+ ep.step("Extracting and configuring Xcode project", 1);
- print_line("binary: " + binary_to_use);
+ String library_to_use = "libgodot.iphone." + String(p_debug ? "debug" : "release") + ".fat.a";
+
+ 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 +742,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;
@@ -496,15 +802,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 +866,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,19 +889,30 @@ 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("Making .xcarchive", 2);
+ ep.step("Code-signing dylibs", 2);
+ 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);
+ memdelete(dylibs_dir);
+ ERR_FAIL_COND_V(err, err);
+
+ ep.step("Making .xcarchive", 3);
String archive_path = p_path.get_basename() + ".xcarchive";
List<String> archive_args;
archive_args.push_back("-project");
@@ -608,15 +928,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
archive_args.push_back("archive");
archive_args.push_back("-archivePath");
archive_args.push_back(archive_path);
- err = OS::get_singleton()->execute("/usr/bin/xcodebuild", archive_args, true);
- ERR_FAIL_COND_V(err, err);
-
- ep.step("Code-signing dylibs", 3);
- DirAccess *dylibs_dir = DirAccess::open(archive_path + "/Products/Applications/" + binary_name + ".app/dylibs");
- ERR_FAIL_COND_V(!dylibs_dir, ERR_CANT_OPEN);
- CodesignData codesign_data(p_preset, p_debug);
- err = _walk_dir_recursive(dylibs_dir, _codesign, &codesign_data);
- memdelete(dylibs_dir);
+ err = OS::get_singleton()->execute("xcodebuild", archive_args, true);
ERR_FAIL_COND_V(err, err);
ep.step("Making .ipa", 4);
@@ -625,13 +937,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("/usr/bin/xcodebuild", export_args, true);
+ 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;
diff --git a/platform/iphone/game_center.h b/platform/iphone/game_center.h
index c0a7830fe9..21f40fa362 100644
--- a/platform/iphone/game_center.h
+++ b/platform/iphone/game_center.h
@@ -43,11 +43,13 @@ class GameCenter : public Object {
List<Variant> pending_events;
- bool connected;
+ bool authenticated;
+
+ void return_connect_error(const char *p_error_description);
public:
- Error connect();
- bool is_connected();
+ void connect();
+ bool is_authenticated();
Error post_score(Variant p_score);
Error award_achievement(Variant p_params);
@@ -55,6 +57,7 @@ public:
void request_achievements();
void request_achievement_descriptions();
Error show_game_center(Variant p_params);
+ Error request_identity_verification_signature();
void game_center_closed();
diff --git a/platform/iphone/game_center.mm b/platform/iphone/game_center.mm
index 821ef2a3ab..d2104ae765 100644
--- a/platform/iphone/game_center.mm
+++ b/platform/iphone/game_center.mm
@@ -49,8 +49,7 @@ extern "C" {
GameCenter *GameCenter::instance = NULL;
void GameCenter::_bind_methods() {
- ClassDB::bind_method(D_METHOD("connect"), &GameCenter::connect);
- ClassDB::bind_method(D_METHOD("is_connected"), &GameCenter::is_connected);
+ ClassDB::bind_method(D_METHOD("is_authenticated"), &GameCenter::is_authenticated);
ClassDB::bind_method(D_METHOD("post_score"), &GameCenter::post_score);
ClassDB::bind_method(D_METHOD("award_achievement"), &GameCenter::award_achievement);
@@ -58,24 +57,41 @@ void GameCenter::_bind_methods() {
ClassDB::bind_method(D_METHOD("request_achievements"), &GameCenter::request_achievements);
ClassDB::bind_method(D_METHOD("request_achievement_descriptions"), &GameCenter::request_achievement_descriptions);
ClassDB::bind_method(D_METHOD("show_game_center"), &GameCenter::show_game_center);
+ ClassDB::bind_method(D_METHOD("request_identity_verification_signature"), &GameCenter::request_identity_verification_signature);
ClassDB::bind_method(D_METHOD("get_pending_event_count"), &GameCenter::get_pending_event_count);
ClassDB::bind_method(D_METHOD("pop_pending_event"), &GameCenter::pop_pending_event);
};
-Error GameCenter::connect() {
+void GameCenter::return_connect_error(const char *p_error_description) {
+ authenticated = false;
+ Dictionary ret;
+ ret["type"] = "authentication";
+ ret["result"] = "error";
+ ret["error_code"] = 0;
+ ret["error_description"] = p_error_description;
+ pending_events.push_back(ret);
+}
+
+void GameCenter::connect() {
//if this class isn't available, game center isn't implemented
if ((NSClassFromString(@"GKLocalPlayer")) == nil) {
- GameCenter::get_singleton()->connected = false;
- return ERR_UNAVAILABLE;
+ return_connect_error("GameCenter not available");
+ return;
}
GKLocalPlayer *player = [GKLocalPlayer localPlayer];
- ERR_FAIL_COND_V(![player respondsToSelector:@selector(authenticateHandler)], ERR_UNAVAILABLE);
+ if (![player respondsToSelector:@selector(authenticateHandler)]) {
+ return_connect_error("GameCenter doesn't respond to 'authenticateHandler'");
+ return;
+ }
ViewController *root_controller = (ViewController *)((AppDelegate *)[[UIApplication sharedApplication] delegate]).window.rootViewController;
- ERR_FAIL_COND_V(!root_controller, FAILED);
+ if (!root_controller) {
+ return_connect_error("Window doesn't have root ViewController");
+ return;
+ }
// This handler is called several times. First when the view needs to be shown, then again
// after the view is cancelled or the user logs in. Or if the user's already logged in, it's
@@ -89,24 +105,22 @@ Error GameCenter::connect() {
ret["type"] = "authentication";
if (player.isAuthenticated) {
ret["result"] = "ok";
- ret["player_id"] = player.playerID;
- GameCenter::get_singleton()->connected = true;
+ ret["player_id"] = [player.playerID UTF8String];
+ GameCenter::get_singleton()->authenticated = true;
} else {
ret["result"] = "error";
- ret["error_code"] = error.code;
+ ret["error_code"] = (int64_t)error.code;
ret["error_description"] = [error.localizedDescription UTF8String];
- GameCenter::get_singleton()->connected = false;
+ GameCenter::get_singleton()->authenticated = false;
};
pending_events.push_back(ret);
};
});
-
- return OK;
};
-bool GameCenter::is_connected() {
- return connected;
+bool GameCenter::is_authenticated() {
+ return authenticated;
};
Error GameCenter::post_score(Variant p_score) {
@@ -117,7 +131,7 @@ Error GameCenter::post_score(Variant p_score) {
String category = params["category"];
NSString *cat_str = [[[NSString alloc] initWithUTF8String:category.utf8().get_data()] autorelease];
- GKScore *reporter = [[[GKScore alloc] initWithCategory:cat_str] autorelease];
+ GKScore *reporter = [[[GKScore alloc] initWithLeaderboardIdentifier:cat_str] autorelease];
reporter.value = score;
ERR_FAIL_COND_V([GKScore respondsToSelector:@selector(reportScores)], ERR_UNAVAILABLE);
@@ -131,7 +145,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];
};
@@ -169,7 +183,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);
@@ -227,7 +241,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);
@@ -259,7 +273,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);
@@ -275,7 +289,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);
@@ -326,6 +340,34 @@ Error GameCenter::show_game_center(Variant p_params) {
return OK;
};
+Error GameCenter::request_identity_verification_signature() {
+
+ ERR_FAIL_COND_V(!is_authenticated(), ERR_UNAUTHORIZED);
+
+ GKLocalPlayer *player = [GKLocalPlayer localPlayer];
+ [player generateIdentityVerificationSignatureWithCompletionHandler:^(NSURL *publicKeyUrl, NSData *signature, NSData *salt, uint64_t timestamp, NSError *error) {
+
+ Dictionary ret;
+ ret["type"] = "identity_verification_signature";
+ if (error == nil) {
+ ret["result"] = "ok";
+ ret["public_key_url"] = [publicKeyUrl.absoluteString UTF8String];
+ ret["signature"] = [[signature base64EncodedStringWithOptions:0] UTF8String];
+ ret["salt"] = [[salt base64EncodedStringWithOptions:0] UTF8String];
+ ret["timestamp"] = timestamp;
+ ret["player_id"] = [player.playerID UTF8String];
+ } else {
+ ret["result"] = "error";
+ ret["error_code"] = (int64_t)error.code;
+ ret["error_description"] = [error.localizedDescription UTF8String];
+ };
+
+ pending_events.push_back(ret);
+ }];
+
+ return OK;
+};
+
void GameCenter::game_center_closed() {
Dictionary ret;
@@ -354,7 +396,7 @@ GameCenter *GameCenter::get_singleton() {
GameCenter::GameCenter() {
ERR_FAIL_COND(instance != NULL);
instance = this;
- connected = false;
+ authenticated = false;
};
GameCenter::~GameCenter(){};
diff --git a/platform/iphone/globals/global_defaults.cpp b/platform/iphone/globals/global_defaults.cpp
index 4bdc716d6e..b81e6def3b 100644
--- a/platform/iphone/globals/global_defaults.cpp
+++ b/platform/iphone/globals/global_defaults.cpp
@@ -31,11 +31,4 @@
#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/godot_iphone.cpp b/platform/iphone/godot_iphone.cpp
index 8c6d6d8da4..7d21d35e18 100644
--- a/platform/iphone/godot_iphone.cpp
+++ b/platform/iphone/godot_iphone.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "main/main.h"
#include "os_iphone.h"
+#include "ustring.h"
#include <stdio.h>
#include <string.h>
@@ -41,9 +42,9 @@ int add_path(int p_argc, char **p_args);
int add_cmdline(int p_argc, char **p_args);
};
-int iphone_main(int, int, int, char **);
+int iphone_main(int, int, int, char **, String);
-int iphone_main(int width, int height, int argc, char **argv) {
+int iphone_main(int width, int height, int argc, char **argv, String data_dir) {
int len = strlen(argv[0]);
@@ -63,7 +64,7 @@ int iphone_main(int width, int height, int argc, char **argv) {
char cwd[512];
getcwd(cwd, sizeof(cwd));
printf("cwd %s\n", cwd);
- os = new OSIPhone(width, height);
+ os = new OSIPhone(width, height, data_dir);
char *fargv[64];
for (int i = 0; i < argc; i++) {
diff --git a/platform/iphone/in_app_store.mm b/platform/iphone/in_app_store.mm
index 9efd4b9891..25f4e1e166 100644
--- a/platform/iphone/in_app_store.mm
+++ b/platform/iphone/in_app_store.mm
@@ -92,6 +92,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 +106,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;
diff --git a/platform/iphone/os_iphone.cpp b/platform/iphone/os_iphone.cpp
index 219e93facf..fbe3bd310d 100644
--- a/platform/iphone/os_iphone.cpp
+++ b/platform/iphone/os_iphone.cpp
@@ -46,6 +46,7 @@
#include "sem_iphone.h"
#include "ios.h"
+#include <dlfcn.h>
int OSIPhone::get_video_driver_count() const {
@@ -54,7 +55,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 +63,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;
@@ -97,14 +93,9 @@ void OSIPhone::initialize_core() {
OS_Unix::initialize_core();
SemaphoreIphone::make_default();
-};
-void OSIPhone::initialize_logger() {
- Vector<Logger *> loggers;
- loggers.push_back(memnew(SyslogLogger));
- loggers.push_back(memnew(RotatedFileLogger("user://logs/log.txt")));
- _set_logger(memnew(CompositeLogger(loggers)));
-}
+ set_data_dir(data_dir);
+};
void OSIPhone::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) {
@@ -134,40 +125,33 @@ void OSIPhone::initialize(const VideoMode &p_desired, int p_video_driver, int p_
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));
+ Engine::get_singleton()->add_singleton(Engine::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));
+ 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)));
};
MainLoop *OSIPhone::get_main_loop() const {
@@ -380,12 +364,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);
};
@@ -416,6 +394,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) {
+ if (p_path.length() == 0) {
+ p_library_handle = RTLD_SELF;
+ return OK;
+ }
+ return OS_Unix::open_dynamic_library(p_path, p_library_handle);
+}
+
+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;
@@ -484,7 +493,7 @@ void OSIPhone::set_cursor_shape(CursorShape p_shape){
};
-String OSIPhone::get_data_dir() const {
+String OSIPhone::get_user_data_dir() const {
return data_dir;
};
@@ -523,7 +532,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;
@@ -535,7 +544,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);
@@ -572,7 +581,36 @@ bool OSIPhone::_check_internal_feature_support(const String &p_feature) {
return p_feature == "mobile" || p_feature == "etc" || p_feature == "pvrtc" || p_feature == "etc2";
}
-OSIPhone::OSIPhone(int width, int height) {
+// 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;
@@ -586,7 +624,17 @@ OSIPhone::OSIPhone(int width, int height) {
event_count = 0;
virtual_keyboard_height = 0;
- _set_logger(memnew(SyslogLogger));
+ // can't call set_data_dir from here, since it requires DirAccess
+ // which is initialized in initialize_core
+ data_dir = p_data_dir;
+
+ 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)));
};
OSIPhone::~OSIPhone() {
diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h
index cf2396e87b..1ef673765a 100644
--- a/platform/iphone/os_iphone.h
+++ b/platform/iphone/os_iphone.h
@@ -41,9 +41,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 +60,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,9 +86,6 @@ 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);
@@ -160,6 +155,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);
+ 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;
@@ -185,7 +184,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;
@@ -201,7 +200,7 @@ public:
virtual void native_video_stop();
virtual bool _check_internal_feature_support(const String &p_feature);
- OSIPhone(int width, int height);
+ OSIPhone(int width, int height, String p_data_dir);
~OSIPhone();
};
diff --git a/platform/iphone/platform_config.h b/platform/iphone/platform_config.h
index 54de66082e..7ff6e7a9a9 100644
--- a/platform/iphone/platform_config.h
+++ b/platform/iphone/platform_config.h
@@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include <alloca.h>
-// #define GLES2_INCLUDE_H <ES2/gl.h>
+
#define GLES3_INCLUDE_H <ES3/gl.h>
#define PLATFORM_REFCOUNT
diff --git a/platform/javascript/SCsub b/platform/javascript/SCsub
index f01d9367d2..8d505a5829 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,23 @@ 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.Program(['#bin/godot', target_dir.File('godot' + env['PROGSUFFIX'] + '.wasm')], javascript_objects, PROGSUFFIX=env['PROGSUFFIX'] + '.js');
-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))
+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)
+prejs = env.File('pre.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])
+env.Depends(build, [prejs, postjs])
+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')], build + ['#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/javascript/api/api.cpp b/platform/javascript/api/api.cpp
new file mode 100644
index 0000000000..f2b2ca40bf
--- /dev/null
+++ b/platform/javascript/api/api.cpp
@@ -0,0 +1,73 @@
+/*************************************************************************/
+/* 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) */
+/* */
+/* 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 "api.h"
+#include "engine.h"
+#include "javascript_eval.h"
+
+static JavaScript *javascript_eval;
+
+void register_javascript_api() {
+
+ ClassDB::register_virtual_class<JavaScript>();
+ javascript_eval = memnew(JavaScript);
+ Engine::get_singleton()->add_singleton(Engine::Singleton("JavaScript", javascript_eval));
+}
+
+void unregister_javascript_api() {
+
+ memdelete(javascript_eval);
+}
+
+JavaScript *JavaScript::singleton = NULL;
+
+JavaScript *JavaScript::get_singleton() {
+
+ return singleton;
+}
+
+JavaScript::JavaScript() {
+
+ ERR_FAIL_COND(singleton != NULL);
+ singleton = this;
+}
+
+JavaScript::~JavaScript() {}
+
+void JavaScript::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("eval", "code", "use_global_execution_context"), &JavaScript::eval, DEFVAL(false));
+}
+
+#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..53cd9239fc
--- /dev/null
+++ b/platform/javascript/api/api.h
@@ -0,0 +1,31 @@
+/*************************************************************************/
+/* api.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. */
+/*************************************************************************/
+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..4d0b0b21ff 100644
--- a/platform/javascript/javascript_eval.h
+++ b/platform/javascript/api/javascript_eval.h
@@ -27,8 +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 +50,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 4c0e5fd966..9633472cd2 100644
--- a/platform/javascript/audio_driver_javascript.cpp
+++ b/platform/javascript/audio_driver_javascript.cpp
@@ -29,31 +29,86 @@
/*************************************************************************/
#include "audio_driver_javascript.h"
-#include <string.h>
+#include <emscripten.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" 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) {
+
+ int todo = p_frames;
+ int offset = 0;
+
+ while (todo) {
+
+ int tomix = MIN(todo, INTERNAL_BUFFER_SIZE);
+
+ audio_server_process(p_frames, stream_buffer);
+ for (int i = 0; i < tomix * internal_buffer_channels; i++) {
+ internal_buffer[i] = float(stream_buffer[i] >> 16) / 32768.0;
+ }
+
+ /* clang-format off */
+ EM_ASM_ARGS({
+ var data = HEAPF32.subarray($0 / 4, $0 / 4 + $2 * 2);
+
+ for (var channel = 0; channel < _as_output_buffer.numberOfChannels; channel++) {
+ var outputData = _as_output_buffer.getChannelData(channel);
+ // Loop through samples
+ for (var sample = 0; sample < $2; sample++) {
+ // make output equal to the same as the input
+ outputData[sample + $1] = data[sample * 2 + channel];
+ }
+ }
+ }, internal_buffer, offset, tomix);
+ /* clang-format on */
+
+ todo -= tomix;
+ offset += tomix;
+ }
+}
+
Error AudioDriverJavaScript::init() {
return OK;
}
void AudioDriverJavaScript::start() {
+
+ internal_buffer = 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 */
+ 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
+ _as_output_buffer = audioProcessingEvent.outputBuffer;
+ jsAudioDriverMixFunction([_as_output_buffer.getChannelData(0).length]);
+ };
+ return _as_audioctx.sampleRate;
+ }, INTERNAL_BUFFER_SIZE, internal_buffer_channels);
+ /* clang-format on */
}
int AudioDriverJavaScript::get_mix_rate() const {
- return 44100;
+ return mix_rate;
}
AudioDriver::SpeakerMode AudioDriverJavaScript::get_speaker_mode() const {
@@ -63,7 +118,7 @@ AudioDriver::SpeakerMode AudioDriverJavaScript::get_speaker_mode() const {
void AudioDriverJavaScript::lock() {
- /*
+ /*no locking, as threads are not supported
if (active && mutex)
mutex->lock();
*/
@@ -71,7 +126,7 @@ void AudioDriverJavaScript::lock() {
void AudioDriverJavaScript::unlock() {
- /*
+ /*no locking, as threads are not supported
if (active && mutex)
mutex->unlock();
*/
@@ -81,4 +136,8 @@ void AudioDriverJavaScript::finish() {
}
AudioDriverJavaScript::AudioDriverJavaScript() {
+
+ 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 c5cebe800f..b265c4e030 100644
--- a/platform/javascript/audio_driver_javascript.h
+++ b/platform/javascript/audio_driver_javascript.h
@@ -32,10 +32,21 @@
#include "servers/audio_server.h"
-#include "os/mutex.h"
-
class AudioDriverJavaScript : public AudioDriver {
+
+ enum {
+ INTERNAL_BUFFER_SIZE = 4096,
+ };
+
+ int mix_rate;
+ float *internal_buffer;
+ int internal_buffer_channels;
+ int32_t *stream_buffer;
+
public:
+ void mix_to_js(int p_frames);
+ static AudioDriverJavaScript *singleton_js;
+
virtual const char *get_name() const;
virtual Error init();
diff --git a/platform/javascript/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 cc29ad8956..8472c3ccab 100644
--- a/platform/javascript/detect.py
+++ b/platform/javascript/detect.py
@@ -13,13 +13,12 @@ def get_name():
def can_build():
- return ("EMSCRIPTEN_ROOT" in os.environ)
+ return ("EMSCRIPTEN_ROOT" in os.environ or "EMSCRIPTEN" in os.environ)
def get_opts():
from SCons.Variables import BoolVariable
return [
- BoolVariable('wasm', 'Compile to WebAssembly', False),
BoolVariable('javascript_eval', 'Enable JavaScript eval interface', True),
]
@@ -66,7 +65,10 @@ def configure(env):
## Compiler configuration
env['ENV'] = os.environ
- env.PrependENVPath('PATH', os.environ['EMSCRIPTEN_ROOT'])
+ if ("EMSCRIPTEN_ROOT" in os.environ):
+ env.PrependENVPath('PATH', os.environ['EMSCRIPTEN_ROOT'])
+ elif ("EMSCRIPTEN" in os.environ):
+ env.PrependENVPath('PATH', os.environ['EMSCRIPTEN'])
env['CC'] = 'emcc'
env['CXX'] = 'em++'
env['LINK'] = 'emcc'
@@ -100,20 +102,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/engine.js b/platform/javascript/engine.js
index 99d1c20bbd..dc4bdc7efb 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 {
@@ -147,15 +160,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 +194,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 +221,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 +269,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 +287,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 +306,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 +321,7 @@
this.abort();
return;
} else {
- loadXHR(resolve, reject, file);
+ setTimeout(loadXHR.bind(null, resolve, reject, file, tracker), 1000);
}
}
@@ -348,12 +348,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..05b0fb3fbc 100644
--- a/platform/javascript/export/export.cpp
+++ b/platform/javascript/export/export.cpp
@@ -30,13 +30,12 @@
#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,18 +46,11 @@ 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;
@@ -90,17 +82,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 +113,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::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";
@@ -166,17 +141,10 @@ 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;
}
@@ -187,9 +155,11 @@ String EditorExportPlatformJavaScript::get_binary_extension() const {
}
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,17 +167,10 @@ 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)) {
@@ -222,14 +185,6 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese
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);
@@ -240,13 +195,17 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese
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;
@@ -262,20 +221,18 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese
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);
@@ -288,9 +245,50 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese
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.ptr(), 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 + "\nUsing 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 +317,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/http_client.h.inc b/platform/javascript/http_client.h.inc
new file mode 100644
index 0000000000..9e4edf7848
--- /dev/null
+++ b/platform/javascript/http_client.h.inc
@@ -0,0 +1,48 @@
+/*************************************************************************/
+/* http_client.h.inc */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+// 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;
diff --git a/platform/javascript/http_client_javascript.cpp b/platform/javascript/http_client_javascript.cpp
new file mode 100644
index 0000000000..0b105dcb40
--- /dev/null
+++ b/platform/javascript/http_client_javascript.cpp
@@ -0,0 +1,282 @@
+/*************************************************************************/
+/* http_client_javascript.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "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");
+ }
+
+ host = p_host;
+ if (host.begins_with("http://")) {
+ host.replace_first("http://", "");
+ } else if (host.begins_with("https://")) {
+ host.replace_first("https://", "");
+ }
+
+ status = host.is_valid_ip_address() ? STATUS_CONNECTING : STATUS_RESOLVING;
+ port = p_port;
+ use_tls = p_ssl;
+ 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_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);
+
+ static const char *_methods[HTTPClient::METHOD_MAX] = {
+ "GET",
+ "HEAD",
+ "POST",
+ "PUT",
+ "DELETE",
+ "OPTIONS",
+ "TRACE",
+ "CONNECT"
+ };
+
+ 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.size())
+ 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);
+ polled_response_code = 0;
+ polled_response_header = String();
+ 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:
+ polled_response_code = godot_xhr_get_status(xhr_id);
+ int response_length = godot_xhr_get_response_length(xhr_id);
+ if (response_length == 0) {
+ godot_xhr_ready_state_t ready_state = godot_xhr_get_ready_state(xhr_id);
+ if (ready_state == XHR_READY_STATE_HEADERS_RECEIVED || ready_state == XHR_READY_STATE_LOADING) {
+ return OK;
+ } else {
+ 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);
+ PoolByteArray::Write write = bytes.write();
+ godot_xhr_get_response_headers(xhr_id, reinterpret_cast<char *>(write.ptr()), len);
+ 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(response_length);
+ write = polled_response.write();
+ godot_xhr_get_response(xhr_id, write.ptr(), response_length);
+ 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;
+}
+
+HTTPClient::~HTTPClient() {
+
+ godot_xhr_free(xhr_id);
+}
diff --git a/platform/javascript/http_request.h b/platform/javascript/http_request.h
new file mode 100644
index 0000000000..06d9239004
--- /dev/null
+++ b/platform/javascript/http_request.h
@@ -0,0 +1,74 @@
+/*************************************************************************/
+/* http_request.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef HTTP_REQUEST_H
+#define HTTP_REQUEST_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "stddef.h"
+
+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;
+
+extern int godot_xhr_new();
+extern void godot_xhr_reset(int p_xhr_id);
+extern bool godot_xhr_free(int p_xhr_id);
+
+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);
+
+extern void godot_xhr_set_request_header(int p_xhr_id, const char *p_header, const char *p_value);
+
+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);
+
+/* 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);
+
+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);
+
+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..f30240b41b
--- /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-2017 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 74f8d80a76..a755dcb5c4 100644
--- a/platform/javascript/javascript_eval.cpp
+++ b/platform/javascript/javascript_eval.cpp
@@ -29,34 +29,44 @@
/*************************************************************************/
#ifdef JAVASCRIPT_EVAL_ENABLED
-#include "javascript_eval.h"
+#include "api/javascript_eval.h"
#include "emscripten.h"
-JavaScript *JavaScript::singleton = NULL;
+extern "C" EMSCRIPTEN_KEEPALIVE uint8_t *resize_poolbytearray_and_open_write(PoolByteArray *p_arr, PoolByteArray::Write *r_write, int p_len) {
-JavaScript *JavaScript::get_singleton() {
-
- return singleton;
+ p_arr->resize(p_len);
+ *r_write = p_arr->write();
+ return r_write->ptr();
}
Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) {
union {
- int i;
+ bool b;
double d;
char *s;
} js_data[4];
+
+ PoolByteArray arr;
+ PoolByteArray::Write arr_write;
+
/* clang-format off */
Variant::Type return_type = static_cast<Variant::Type>(EM_ASM_INT({
+ const CODE = $0;
+ const USE_GLOBAL_EXEC_CONTEXT = $1;
+ const PTR = $2;
+ const ELEM_LEN = $3;
+ const BYTEARRAY_PTR = $4;
+ const BYTEARRAY_WRITE_PTR = $5;
var eval_ret;
try {
- if ($3) { // p_use_global_exec_context
+ if (USE_GLOBAL_EXEC_CONTEXT) {
// indirect eval call grants global execution context
var global_eval = eval;
- eval_ret = global_eval(UTF8ToString($2));
+ eval_ret = global_eval(UTF8ToString(CODE));
} else {
- eval_ret = eval(UTF8ToString($2));
+ eval_ret = eval(UTF8ToString(CODE));
}
} catch (e) {
Module.printErr(e);
@@ -66,16 +76,11 @@ Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) {
switch (typeof eval_ret) {
case 'boolean':
- // bitwise op yields 32-bit int
- setValue($0, eval_ret|0, 'i32');
+ setValue(PTR, eval_ret, 'i32');
return 1; // BOOL
case 'number':
- if ((eval_ret|0)===eval_ret) {
- setValue($0, eval_ret|0, 'i32');
- return 2; // INT
- }
- setValue($0, eval_ret, 'double');
+ setValue(PTR, eval_ret, 'double');
return 3; // REAL
case 'string':
@@ -85,7 +90,7 @@ Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) {
if (array_ptr===0) {
throw new Error('String allocation failed (probably out of memory)');
}
- setValue($0, array_ptr|0 , '*');
+ setValue(PTR, array_ptr , '*');
stringToUTF8(eval_ret, array_ptr, array_len);
return 4; // STRING
} catch (e) {
@@ -102,41 +107,50 @@ Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) {
break;
}
- else if (typeof eval_ret.x==='number' && typeof eval_ret.y==='number') {
- setValue($0, eval_ret.x, 'double');
- setValue($0+$1, eval_ret.y, 'double');
+ if (ArrayBuffer.isView(eval_ret) && !(eval_ret instanceof Uint8Array)) {
+ eval_ret = new Uint8Array(eval_ret.buffer);
+ }
+ else if (eval_ret instanceof ArrayBuffer) {
+ eval_ret = new Uint8Array(eval_ret);
+ }
+ if (eval_ret instanceof Uint8Array) {
+ var bytes_ptr = ccall('resize_poolbytearray_and_open_write', 'number', ['number', 'number' ,'number'], [BYTEARRAY_PTR, BYTEARRAY_WRITE_PTR, eval_ret.length]);
+ HEAPU8.set(eval_ret, bytes_ptr);
+ return 20; // POOL_BYTE_ARRAY
+ }
+
+ if (typeof eval_ret.x==='number' && typeof eval_ret.y==='number') {
+ setValue(PTR, eval_ret.x, 'double');
+ setValue(PTR + ELEM_LEN, eval_ret.y, 'double');
if (typeof eval_ret.z==='number') {
- setValue($0+$1*2, eval_ret.z, 'double');
+ setValue(PTR + ELEM_LEN*2, eval_ret.z, 'double');
return 7; // VECTOR3
}
else if (typeof eval_ret.width==='number' && typeof eval_ret.height==='number') {
- setValue($0+$1*2, eval_ret.width, 'double');
- setValue($0+$1*3, eval_ret.height, 'double');
+ setValue(PTR + ELEM_LEN*2, eval_ret.width, 'double');
+ setValue(PTR + ELEM_LEN*3, eval_ret.height, 'double');
return 6; // RECT2
}
return 5; // VECTOR2
}
- else if (typeof eval_ret.r==='number' && typeof eval_ret.g==='number' && typeof eval_ret.b==='number') {
- // assume 8-bit rgb components since we're on the web
- setValue($0, eval_ret.r, 'double');
- setValue($0+$1, eval_ret.g, 'double');
- setValue($0+$1*2, eval_ret.b, 'double');
- setValue($0+$1*3, typeof eval_ret.a==='number' ? eval_ret.a : 1, 'double');
+ if (typeof eval_ret.r === 'number' && typeof eval_ret.g === 'number' && typeof eval_ret.b === 'number') {
+ setValue(PTR, eval_ret.r, 'double');
+ setValue(PTR + ELEM_LEN, eval_ret.g, 'double');
+ setValue(PTR + ELEM_LEN*2, eval_ret.b, 'double');
+ setValue(PTR + ELEM_LEN*3, typeof eval_ret.a === 'number' ? eval_ret.a : 1, 'double');
return 14; // COLOR
}
break;
}
return 0; // NIL
- }, js_data, sizeof *js_data, p_code.utf8().get_data(), p_use_global_exec_context));
+ }, p_code.utf8().get_data(), p_use_global_exec_context, js_data, sizeof *js_data, &arr, &arr_write));
/* clang-format on */
switch (return_type) {
case Variant::BOOL:
- return !!js_data->i;
- case Variant::INT:
- return js_data->i;
+ return js_data->b;
case Variant::REAL:
return js_data->d;
case Variant::STRING: {
@@ -153,23 +167,12 @@ Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) {
case Variant::RECT2:
return Rect2(js_data[0].d, js_data[1].d, js_data[2].d, js_data[3].d);
case Variant::COLOR:
- return Color(js_data[0].d / 255., js_data[1].d / 255., js_data[2].d / 255., js_data[3].d);
+ return Color(js_data[0].d, js_data[1].d, js_data[2].d, js_data[3].d);
+ case Variant::POOL_BYTE_ARRAY:
+ arr_write = PoolByteArray::Write();
+ return arr;
}
return Variant();
}
-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..5c5d608524 100644
--- a/platform/javascript/javascript_main.cpp
+++ b/platform/javascript/javascript_main.cpp
@@ -61,7 +61,6 @@ 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) {
diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp
index f6446e77da..d5c675d9e0 100644
--- a/platform/javascript/os_javascript.cpp
+++ b/platform/javascript/os_javascript.cpp
@@ -29,8 +29,8 @@
/*************************************************************************/
#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/gles3/rasterizer_gles3.h"
#include "drivers/unix/dir_access_unix.h"
@@ -64,11 +64,6 @@ 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();
-}
-
int OS_JavaScript::get_audio_driver_count() const {
return 1;
@@ -85,10 +80,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);
@@ -171,14 +162,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;
@@ -442,25 +434,23 @@ void OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, i
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 */
+ bool resize_canvas_on_start = EM_ASM_INT_V(
+ return Module.resizeCanvasOnStart;
+ );
+ /* clang-format on */
+ if (resize_canvas_on_start) {
+ set_window_size(Size2(video_mode.width, video_mode.height));
+ } else {
+ Size2 canvas_size = get_window_size();
+ video_mode.width = canvas_size.width;
+ video_mode.height = canvas_size.height;
+ }
- // 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);
@@ -480,11 +470,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,11 +506,6 @@ 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();
}
@@ -898,14 +878,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 {
@@ -1003,7 +982,7 @@ 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;
@@ -1011,7 +990,7 @@ OS_JavaScript::OS_JavaScript(const char *p_execpath, GetDataDirFunc p_get_data_d
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;
diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h
index 1c939d3fd5..a95b069d03 100644
--- a/platform/javascript/os_javascript.h
+++ b/platform/javascript/os_javascript.h
@@ -31,21 +31,17 @@
#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,8 +50,6 @@ 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;
@@ -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,12 +78,9 @@ 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);
@@ -153,7 +140,7 @@ public:
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 +159,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/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/crash_handler_osx.mm b/platform/osx/crash_handler_osx.mm
index 2ed88db309..5635fe0187 100644
--- a/platform/osx/crash_handler_osx.mm
+++ b/platform/osx/crash_handler_osx.mm
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "main/main.h"
#include "os_osx.h"
+#include "project_settings.h"
#include <string.h>
#include <unistd.h>
diff --git a/platform/osx/detect.py b/platform/osx/detect.py
index 31032659b6..ff7cf2ad2f 100644
--- a/platform/osx/detect.py
+++ b/platform/osx/detect.py
@@ -84,8 +84,15 @@ def configure(env):
else: # 64-bit, default
basecmd = root + "/target/bin/x86_64-apple-" + env["osxcross_sdk"] + "-"
- env['CC'] = basecmd + "cc"
- env['CXX'] = basecmd + "c++"
+ 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"
@@ -103,7 +110,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/export/export.cpp b/platform/osx/export/export.cpp
index 0fd21d62ee..689b79b826 100644
--- a/platform/osx/export/export.cpp
+++ b/platform/osx/export/export.cpp
@@ -53,9 +53,16 @@ class EditorExportPlatformOSX : public EditorExportPlatform {
void _fix_plist(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &plist, const String &p_binary);
void _make_icon(const Ref<Image> &p_icon, Vector<uint8_t> &p_data);
-#ifdef OSX_ENABLED
+
Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path);
Error _create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name);
+
+#ifdef OSX_ENABLED
+ bool use_codesign() const { return true; }
+ bool use_dmg() const { return true; }
+#else
+ bool use_codesign() const { return false; }
+ bool use_dmg() const { return false; }
#endif
protected:
@@ -67,11 +74,7 @@ public:
virtual String get_os_name() const { return "OSX"; }
virtual Ref<Texture> get_logo() const { return logo; }
-#ifdef OSX_ENABLED
- virtual String get_binary_extension() const { return "dmg"; }
-#else
- virtual String get_binary_extension() const { return "zip"; }
-#endif
+ virtual String get_binary_extension() const { return use_dmg() ? "dmg" : "zip"; }
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0);
virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const;
@@ -157,7 +160,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);
@@ -220,7 +223,6 @@ void EditorExportPlatformOSX::_fix_plist(const Ref<EditorExportPreset> &p_preset
}
}
-#ifdef OSX_ENABLED
/**
If we're running the OSX version of the Godot editor we'll:
- export our application bundle to a temporary folder
@@ -230,6 +232,7 @@ void EditorExportPlatformOSX::_fix_plist(const Ref<EditorExportPreset> &p_preset
Error EditorExportPlatformOSX::_code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path) {
List<String> args;
+
if (p_preset->get("codesign/entitlements") != "") {
/* this should point to our entitlements.plist file that sandboxes our application, I don't know if this should also be placed in our app bundle */
args.push_back("-entitlements");
@@ -239,14 +242,25 @@ Error EditorExportPlatformOSX::_code_sign(const Ref<EditorExportPreset> &p_prese
args.push_back(p_preset->get("codesign/identity"));
args.push_back("-v"); /* provide some more feedback */
args.push_back(p_path);
- Error err = OS::get_singleton()->execute("/usr/bin/codesign", args, true);
- ERR_FAIL_COND_V(err, err);
+
+ String str;
+ Error err = OS::get_singleton()->execute("codesign", args, true, NULL, &str, NULL, true);
+ ERR_FAIL_COND_V(err != OK, err);
+
+ print_line("codesign: " + str);
+ if (str.find("no identity found") != -1) {
+ EditorNode::add_io_error("codesign: no identity found");
+ return FAILED;
+ }
return OK;
}
Error EditorExportPlatformOSX::_create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name) {
List<String> args;
+
+ OS::get_singleton()->move_to_trash(p_dmg_path);
+
args.push_back("create");
args.push_back(p_dmg_path);
args.push_back("-volname");
@@ -255,13 +269,26 @@ Error EditorExportPlatformOSX::_create_dmg(const String &p_dmg_path, const Strin
args.push_back("HFS+");
args.push_back("-srcfolder");
args.push_back(p_app_path_name);
- Error err = OS::get_singleton()->execute("/usr/bin/hdiutil", args, true);
- ERR_FAIL_COND_V(err, err);
+
+ String str;
+ Error err = OS::get_singleton()->execute("hdiutil", args, true, NULL, &str, NULL, true);
+ ERR_FAIL_COND_V(err != OK, err);
+
+ print_line("hdiutil returned: " + str);
+ if (str.find("create failed") != -1) {
+ if (str.find("File exists") != -1) {
+ EditorNode::add_io_error("hdiutil: create failed - file exists");
+ } else {
+ EditorNode::add_io_error("hdiutil: create failed");
+ }
+ return FAILED;
+ }
return OK;
}
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;
@@ -309,28 +336,45 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
else
pkg_name = "Unnamed";
- // We're on OSX so we can export to DMG, but first we create our application bundle
- String tmp_app_path_name = p_path.get_base_dir() + "/" + pkg_name + ".app";
- print_line("Exporting to " + tmp_app_path_name);
- DirAccess *tmp_app_path = DirAccess::create_for_path(tmp_app_path_name);
- ERR_FAIL_COND_V(!tmp_app_path, ERR_CANT_CREATE)
+ Error err = OK;
+ String tmp_app_path_name = "";
+ zlib_filefunc_def io2 = io;
+ FileAccess *dst_f = NULL;
+ io2.opaque = &dst_f;
+ zipFile dst_pkg_zip = NULL;
+
+ if (use_dmg()) {
+ // We're on OSX so we can export to DMG, but first we create our application bundle
+ tmp_app_path_name = EditorSettings::get_singleton()->get_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) {
+ err = ERR_CANT_CREATE;
+ }
- ///@TODO We should delete the existing application bundle especially if we attempt to code sign it, but what is a safe way to do this? Maybe call system function so it moves to trash?
- // tmp_app_path->erase_contents_recursive();
+ // Create our folder structure or rely on unzip?
+ if (err == OK) {
+ print_line("Creating " + tmp_app_path_name + "/Contents/MacOS");
+ err = tmp_app_path->make_dir_recursive(tmp_app_path_name + "/Contents/MacOS");
+ }
- // Create our folder structure or rely on unzip?
- print_line("Creating " + tmp_app_path_name + "/Contents/MacOS");
- Error dir_err = tmp_app_path->make_dir_recursive(tmp_app_path_name + "/Contents/MacOS");
- ERR_FAIL_COND_V(dir_err, ERR_CANT_CREATE)
- print_line("Creating " + tmp_app_path_name + "/Contents/Resources");
- dir_err = tmp_app_path->make_dir_recursive(tmp_app_path_name + "/Contents/Resources");
- ERR_FAIL_COND_V(dir_err, ERR_CANT_CREATE)
+ if (err == OK) {
+ print_line("Creating " + tmp_app_path_name + "/Contents/Resources");
+ err = tmp_app_path->make_dir_recursive(tmp_app_path_name + "/Contents/Resources");
+ }
+ } else {
+ // Open our destination zip file
+ dst_pkg_zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, NULL, &io2);
+ if (!dst_pkg_zip) {
+ err = ERR_CANT_CREATE;
+ }
+ }
- /* Now process our template */
+ // Now process our template
bool found_binary = false;
int total_size = 0;
- while (ret == UNZ_OK) {
+ while (ret == UNZ_OK && err == OK) {
bool is_execute = false;
//get filename
@@ -392,287 +436,152 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
print_line("ADDING: " + file + " size: " + itos(data.size()));
total_size += data.size();
- /* write it into our application bundle */
- file = tmp_app_path_name + "/" + file;
-
- /* write the file, need to add chmod */
- FileAccess *f = FileAccess::open(file, FileAccess::WRITE);
- ERR_FAIL_COND_V(!f, ERR_CANT_CREATE)
- f->store_buffer(data.ptr(), data.size());
- f->close();
- memdelete(f);
-
- if (is_execute) {
- // we need execute rights on this file
- chmod(file.utf8().get_data(), 0755);
+ if (use_dmg()) {
+ // write it into our application bundle
+ file = tmp_app_path_name + "/" + file;
+
+ // write the file, need to add chmod
+ FileAccess *f = FileAccess::open(file, FileAccess::WRITE);
+ if (f) {
+ f->store_buffer(data.ptr(), data.size());
+ f->close();
+ if (is_execute) {
+ // Chmod with 0755 if the file is executable
+ f->_chmod(file, 0755);
+ }
+ memdelete(f);
+ } else {
+ err = ERR_CANT_CREATE;
+ }
} else {
- // seems to already be set correctly
- // chmod(file.utf8().get_data(), 0644);
+ // add it to our zip file
+ file = pkg_name + ".app/" + file;
+
+ zip_fileinfo fi;
+ fi.tmz_date.tm_hour = info.tmu_date.tm_hour;
+ fi.tmz_date.tm_min = info.tmu_date.tm_min;
+ fi.tmz_date.tm_sec = info.tmu_date.tm_sec;
+ fi.tmz_date.tm_mon = info.tmu_date.tm_mon;
+ fi.tmz_date.tm_mday = info.tmu_date.tm_mday;
+ fi.tmz_date.tm_year = info.tmu_date.tm_year;
+ fi.dosDate = info.dosDate;
+ fi.internal_fa = info.internal_fa;
+ fi.external_fa = info.external_fa;
+
+ int zerr = zipOpenNewFileInZip(dst_pkg_zip,
+ file.utf8().get_data(),
+ &fi,
+ NULL,
+ 0,
+ NULL,
+ 0,
+ NULL,
+ Z_DEFLATED,
+ Z_DEFAULT_COMPRESSION);
+
+ print_line("OPEN ERR: " + itos(zerr));
+ zerr = zipWriteInFileInZip(dst_pkg_zip, data.ptr(), data.size());
+ print_line("WRITE ERR: " + itos(zerr));
+ zipCloseFileInZip(dst_pkg_zip);
}
}
ret = unzGoToNextFile(src_pkg_zip);
}
- /* we're done with our source zip */
+ // we're done with our source zip
unzClose(src_pkg_zip);
if (!found_binary) {
ERR_PRINTS("Requested template binary '" + binary_to_use + "' not found. It might be missing from your template archive.");
- unzClose(src_pkg_zip);
- return ERR_FILE_NOT_FOUND;
- }
-
- ep.step("Making PKG", 1);
-
- String pack_path = tmp_app_path_name + "/Contents/Resources/" + pkg_name + ".pck";
- Error err = save_pack(p_preset, pack_path);
- // chmod(pack_path.utf8().get_data(), 0644);
-
- if (err) {
- return err;
+ err = ERR_FILE_NOT_FOUND;
}
- /* see if we can code sign our new package */
- if (p_preset->get("codesign/identity") != "") {
- ep.step("Code signing bundle", 2);
+ if (err == OK) {
+ ep.step("Making PKG", 1);
- /* the order in which we code sign is important, this is a bit of a shame or we could do this in our loop that extracts the files from our ZIP */
+ if (use_dmg()) {
+ String pack_path = tmp_app_path_name + "/Contents/Resources/" + pkg_name + ".pck";
+ err = save_pack(p_preset, pack_path);
- // start with our application
- err = _code_sign(p_preset, tmp_app_path_name + "/Contents/MacOS/" + pkg_name);
- ERR_FAIL_COND_V(err, err);
+ // see if we can code sign our new package
+ String identity = p_preset->get("codesign/identity");
+ if (err == OK && identity != "") {
+ ep.step("Code signing bundle", 2);
- ///@TODO we should check the contents of /Contents/Frameworks for frameworks to sign
+ // the order in which we code sign is important, this is a bit of a shame or we could do this in our loop that extracts the files from our ZIP
- // we should probably loop through all resources and sign them?
- err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Resources/icon.icns");
- ERR_FAIL_COND_V(err, err);
- err = _code_sign(p_preset, pack_path);
- ERR_FAIL_COND_V(err, err);
- err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Info.plist");
- ERR_FAIL_COND_V(err, err);
- }
+ // start with our application
+ err = _code_sign(p_preset, tmp_app_path_name + "/Contents/MacOS/" + pkg_name);
- /* and finally create a DMG */
- ep.step("Making DMG", 3);
- err = _create_dmg(p_path, pkg_name, tmp_app_path_name);
- ERR_FAIL_COND_V(err, err);
-
- return OK;
-}
-
-#else
-
-/**
- When exporting for OSX from any other platform we don't have access to code signing or creating DMGs so we'll wrap the bundle into a zip file.
-
- Should probably find a nicer way to have just one export method instead of duplicating the method like this but I would the code got very
- messy with switches inside of it.
-**/
-Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
-
- String src_pkg_name;
-
- EditorProgress ep("export", "Exporting for OSX", 104);
-
- if (p_debug)
- src_pkg_name = p_preset->get("custom_package/debug");
- else
- src_pkg_name = p_preset->get("custom_package/release");
-
- if (src_pkg_name == "") {
- String err;
- src_pkg_name = find_export_template("osx.zip", &err);
- if (src_pkg_name == "") {
- EditorNode::add_io_error(err);
- return ERR_FILE_NOT_FOUND;
- }
- }
-
- FileAccess *src_f = NULL;
- zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
-
- ep.step("Creating app", 0);
-
- unzFile src_pkg_zip = unzOpen2(src_pkg_name.utf8().get_data(), &io);
- if (!src_pkg_zip) {
-
- EditorNode::add_io_error("Could not find template app to export:\n" + src_pkg_name);
- return ERR_FILE_NOT_FOUND;
- }
-
- ERR_FAIL_COND_V(!src_pkg_zip, ERR_CANT_OPEN);
- int ret = unzGoToFirstFile(src_pkg_zip);
-
- String binary_to_use = "godot_osx_" + String(p_debug ? "debug" : "release") + ".";
- int bits_mode = p_preset->get("application/bits_mode");
- binary_to_use += String(bits_mode == 0 ? "fat" : bits_mode == 1 ? "64" : "32");
-
- print_line("binary: " + binary_to_use);
- String pkg_name;
- if (p_preset->get("application/name") != "")
- pkg_name = p_preset->get("application/name"); // app_name
- else if (String(ProjectSettings::get_singleton()->get("application/config/name")) != "")
- pkg_name = String(ProjectSettings::get_singleton()->get("application/config/name"));
- else
- pkg_name = "Unnamed";
-
- /* Open our destination zip file */
- zlib_filefunc_def io2 = io;
- FileAccess *dst_f = NULL;
- io2.opaque = &dst_f;
- zipFile dst_pkg_zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, NULL, &io2);
-
- bool found_binary = false;
-
- while (ret == UNZ_OK) {
-
- //get filename
- unz_file_info info;
- char fname[16384];
- ret = unzGetCurrentFileInfo(src_pkg_zip, &info, fname, 16384, NULL, 0, NULL, 0);
-
- String file = fname;
-
- print_line("READ: " + file);
- Vector<uint8_t> data;
- data.resize(info.uncompressed_size);
-
- //read
- unzOpenCurrentFile(src_pkg_zip);
- unzReadCurrentFile(src_pkg_zip, data.ptr(), data.size());
- unzCloseCurrentFile(src_pkg_zip);
-
- //write
+ ///@TODO we should check the contents of /Contents/Frameworks for frameworks to sign
+ }
- file = file.replace_first("osx_template.app/", "");
+ if (err == OK && identity != "") {
+ // we should probably loop through all resources and sign them?
+ err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Resources/icon.icns");
+ }
- if (file == "Contents/Info.plist") {
- print_line("parse plist");
- _fix_plist(p_preset, data, pkg_name);
- }
+ if (err == OK && identity != "") {
+ err = _code_sign(p_preset, pack_path);
+ }
- if (file.begins_with("Contents/MacOS/godot_")) {
- if (file != "Contents/MacOS/" + binary_to_use) {
- ret = unzGoToNextFile(src_pkg_zip);
- continue; //ignore!
+ if (err == OK && identity != "") {
+ err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Info.plist");
}
- found_binary = true;
- file = "Contents/MacOS/" + pkg_name;
- }
- if (file == "Contents/Resources/icon.icns") {
- //see if there is an icon
- String iconpath;
- if (p_preset->get("application/icon") != "")
- iconpath = p_preset->get("application/icon");
- else
- iconpath = ProjectSettings::get_singleton()->get("application/config/icon");
- print_line("icon? " + iconpath);
- if (iconpath != "") {
- Ref<Image> icon;
- icon.instance();
- icon->load(iconpath);
- if (!icon->empty()) {
- print_line("loaded?");
- _make_icon(icon, data);
- }
+ // and finally create a DMG
+ if (err == OK) {
+ ep.step("Making DMG", 3);
+ err = _create_dmg(p_path, pkg_name, tmp_app_path_name);
}
- //bleh?
- }
- if (data.size() > 0) {
- print_line("ADDING: " + file + " size: " + itos(data.size()));
+ // Clean up temporary .app dir
+ OS::get_singleton()->move_to_trash(tmp_app_path_name);
+ } else {
- /* add it to our zip file */
- file = pkg_name + ".app/" + file;
-
- zip_fileinfo fi;
- fi.tmz_date.tm_hour = info.tmu_date.tm_hour;
- fi.tmz_date.tm_min = info.tmu_date.tm_min;
- fi.tmz_date.tm_sec = info.tmu_date.tm_sec;
- fi.tmz_date.tm_mon = info.tmu_date.tm_mon;
- fi.tmz_date.tm_mday = info.tmu_date.tm_mday;
- fi.tmz_date.tm_year = info.tmu_date.tm_year;
- fi.dosDate = info.dosDate;
- fi.internal_fa = info.internal_fa;
- fi.external_fa = info.external_fa;
-
- int err = zipOpenNewFileInZip(dst_pkg_zip,
- file.utf8().get_data(),
- &fi,
- NULL,
- 0,
- NULL,
- 0,
- NULL,
- Z_DEFLATED,
- Z_DEFAULT_COMPRESSION);
-
- print_line("OPEN ERR: " + itos(err));
- err = zipWriteInFileInZip(dst_pkg_zip, data.ptr(), data.size());
- print_line("WRITE ERR: " + itos(err));
- zipCloseFileInZip(dst_pkg_zip);
+ String pack_path = EditorSettings::get_singleton()->get_cache_dir().plus_file(pkg_name + ".pck");
+ Error err = save_pack(p_preset, pack_path);
+
+ if (err == OK) {
+ zipOpenNewFileInZip(dst_pkg_zip,
+ (pkg_name + ".app/Contents/Resources/" + pkg_name + ".pck").utf8().get_data(),
+ NULL,
+ NULL,
+ 0,
+ NULL,
+ 0,
+ NULL,
+ Z_DEFLATED,
+ Z_DEFAULT_COMPRESSION);
+
+ FileAccess *pf = FileAccess::open(pack_path, FileAccess::READ);
+ if (pf) {
+ const int BSIZE = 16384;
+ uint8_t buf[BSIZE];
+
+ while (true) {
+
+ int r = pf->get_buffer(buf, BSIZE);
+ if (r <= 0)
+ break;
+ zipWriteInFileInZip(dst_pkg_zip, buf, r);
+ }
+ zipCloseFileInZip(dst_pkg_zip);
+ memdelete(pf);
+ } else {
+ err = ERR_CANT_OPEN;
+ }
+ }
}
-
- ret = unzGoToNextFile(src_pkg_zip);
}
- if (!found_binary) {
- ERR_PRINTS("Requested template binary '" + binary_to_use + "' not found. It might be missing from your template archive.");
+ if (dst_pkg_zip) {
zipClose(dst_pkg_zip, NULL);
- unzClose(src_pkg_zip);
- return ERR_FILE_NOT_FOUND;
}
- ep.step("Making PKG", 1);
-
- String pack_path = EditorSettings::get_singleton()->get_settings_path() + "/tmp/" + pkg_name + ".pck";
- Error err = save_pack(p_preset, pack_path);
-
- if (err) {
- zipClose(dst_pkg_zip, NULL);
- unzClose(src_pkg_zip);
- return err;
- }
-
- {
- //write datapack
-
- zipOpenNewFileInZip(dst_pkg_zip,
- (pkg_name + ".app/Contents/Resources/" + pkg_name + ".pck").utf8().get_data(),
- NULL,
- NULL,
- 0,
- NULL,
- 0,
- NULL,
- Z_DEFLATED,
- Z_DEFAULT_COMPRESSION);
-
- FileAccess *pf = FileAccess::open(pack_path, FileAccess::READ);
- ERR_FAIL_COND_V(!pf, ERR_CANT_OPEN);
- const int BSIZE = 16384;
- uint8_t buf[BSIZE];
-
- while (true) {
-
- int r = pf->get_buffer(buf, BSIZE);
- if (r <= 0)
- break;
- zipWriteInFileInZip(dst_pkg_zip, buf, r);
- }
- zipCloseFileInZip(dst_pkg_zip);
- memdelete(pf);
- }
-
- zipClose(dst_pkg_zip, NULL);
- unzClose(src_pkg_zip);
-
return OK;
}
-#endif
bool EditorExportPlatformOSX::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h
index eb8c0566b4..9a740a7bea 100644
--- a/platform/osx/os_osx.h
+++ b/platform/osx/os_osx.h
@@ -38,9 +38,6 @@
#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"
@@ -62,9 +59,6 @@ public:
List<String> args;
MainLoop *main_loop;
- PhysicsServer *physics_server;
- Physics2DServer *physics_2d_server;
-
IP_Unix *ip_unix;
AudioDriverCoreAudio audio_driver;
@@ -112,22 +106,21 @@ public:
CrashHandler crash_handler;
float _mouse_scale(float p_scale) {
- if (display_scale > 1.0)
+ if (_display_scale() > 1.0)
return p_scale;
else
return 1.0;
}
- void _update_window();
+ float _display_scale() const;
+ float _display_scale(id screen) const;
- float display_scale;
+ void _update_window();
protected:
virtual int get_video_driver_count() const;
virtual const char *get_video_driver_name(int p_driver) const;
- virtual VideoMode get_default_video_mode() const;
- virtual void initialize_logger();
virtual void initialize_core();
virtual void initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver);
virtual void finalize();
@@ -160,6 +153,11 @@ public:
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;
@@ -218,6 +216,9 @@ public:
virtual bool _check_internal_feature_support(const String &p_feature);
+ virtual void set_use_vsync(bool p_enable);
+ virtual bool is_vsync_enabled() const;
+
void run();
void set_mouse_mode(MouseMode p_mode);
@@ -229,6 +230,12 @@ public:
virtual Error move_to_trash(const String &p_path);
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 8323aa84a8..781e8de1ab 100644
--- a/platform/osx/os_osx.mm
+++ b/platform/osx/os_osx.mm
@@ -35,8 +35,8 @@
#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 <Carbon/Carbon.h>
#import <Cocoa/Cocoa.h>
@@ -85,6 +85,15 @@ 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
@@ -180,13 +189,13 @@ static bool mouse_down_control = false;
if (newBackingScaleFactor != oldBackingScaleFactor) {
//Set new display scale and window size
- OS_OSX::singleton->display_scale = newBackingScaleFactor;
+ float newDisplayScale = OS_OSX::singleton->is_hidpi_allowed() ? newBackingScaleFactor : 1.0;
const NSRect contentRect = [OS_OSX::singleton->window_view frame];
const NSRect fbRect = contentRect; //convertRectToBacking(contentRect);
- OS_OSX::singleton->window_size.width = fbRect.size.width * OS_OSX::singleton->display_scale;
- OS_OSX::singleton->window_size.height = fbRect.size.height * OS_OSX::singleton->display_scale;
+ OS_OSX::singleton->window_size.width = fbRect.size.width * newDisplayScale;
+ OS_OSX::singleton->window_size.height = fbRect.size.height * newDisplayScale;
//Update context
if (OS_OSX::singleton->main_loop) {
@@ -206,8 +215,9 @@ static bool mouse_down_control = false;
const NSRect contentRect = [OS_OSX::singleton->window_view frame];
const NSRect fbRect = contentRect; //convertRectToBacking(contentRect);
- OS_OSX::singleton->window_size.width = fbRect.size.width * OS_OSX::singleton->display_scale;
- OS_OSX::singleton->window_size.height = fbRect.size.height * OS_OSX::singleton->display_scale;
+ float displayScale = OS_OSX::singleton->_display_scale();
+ OS_OSX::singleton->window_size.width = fbRect.size.width * displayScale;
+ OS_OSX::singleton->window_size.height = fbRect.size.height * displayScale;
if (OS_OSX::singleton->main_loop) {
Main::force_redraw();
@@ -352,7 +362,8 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange {
const NSRect contentRect = [OS_OSX::singleton->window_view frame];
- NSRect pointInWindowRect = NSMakeRect(OS_OSX::singleton->im_position.x / OS_OSX::singleton->display_scale, contentRect.size.height - (OS_OSX::singleton->im_position.y / OS_OSX::singleton->display_scale) - 1, 0, 0);
+ float displayScale = OS_OSX::singleton->_display_scale();
+ NSRect pointInWindowRect = NSMakeRect(OS_OSX::singleton->im_position.x / displayScale, contentRect.size.height - (OS_OSX::singleton->im_position.y / displayScale) - 1, 0, 0);
NSPoint pointOnScreen = [[OS_OSX::singleton->window_view window] convertRectToScreen:pointInWindowRect].origin;
return NSMakeRect(pointOnScreen.x, pointOnScreen.y, 0, 0);
@@ -506,12 +517,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]);
@@ -573,6 +581,15 @@ static void _mouseDownEvent(NSEvent *event, int index, int mask, bool pressed) {
OS_OSX::singleton->input->set_mouse_in_window(true);
}
+- (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 {
// nothing left to do here
}
@@ -836,6 +853,18 @@ 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;
@@ -854,11 +883,16 @@ inline void sendScrollEvent(int button, double factor, int modifierFlags) {
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 (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]);
+ }
}
}
@@ -895,17 +929,7 @@ int OS_OSX::get_video_driver_count() const {
const char *OS_OSX::get_video_driver_name(int p_driver) const {
- return "GLES2";
-}
-
-OS::VideoMode OS_OSX::get_default_video_mode() const {
-
- VideoMode vm;
- vm.width = 1024;
- vm.height = 600;
- vm.fullscreen = false;
- vm.resizable = true;
- return vm;
+ return "GLES3";
}
void OS_OSX::initialize_core() {
@@ -922,10 +946,15 @@ 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;
}
+static bool displays_arrangement_dirty = true;
+static void displays_arrangement_changed(CGDirectDisplayID display_id, CGDisplayChangeSummaryFlags flags, void *user_info) {
+ displays_arrangement_dirty = true;
+}
+
void OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) {
/*** OSX INITIALIZATION ***/
@@ -933,21 +962,16 @@ void OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_au
/*** 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);
- if (is_hidpi_allowed() && [[NSScreen mainScreen] respondsToSelector:@selector(backingScaleFactor)]) {
- for (NSScreen *screen in [NSScreen screens]) {
- float s = [screen backingScaleFactor];
- if (s > display_scale) {
- display_scale = s;
- }
- }
- }
+ // Register to be notified on displays arrangement changes
+ CGDisplayRegisterReconfigurationCallback(displays_arrangement_changed, NULL);
window_delegate = [[GodotWindowDelegate alloc] init];
@@ -972,10 +996,19 @@ void OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_au
window_view = [[GodotContentView alloc] init];
- window_size.width = p_desired.width * display_scale;
- window_size.height = p_desired.height * display_scale;
+ float displayScale = 1.0;
+ if (is_hidpi_allowed()) {
+ // note that mainScreen is not screen #0 but the one with the keyboard focus.
+ NSScreen *screen = [NSScreen mainScreen];
+ if ([screen respondsToSelector:@selector(backingScaleFactor)]) {
+ displayScale = fmax(displayScale, [screen backingScaleFactor]);
+ }
+ }
+
+ window_size.width = p_desired.width * displayScale;
+ window_size.height = p_desired.height * displayScale;
- if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6 && display_scale > 1) {
+ if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6 && displayScale > 1.0) {
[window_view setWantsBestResolutionOpenGLSurface:YES];
//if (current_videomode.resizable)
[window_object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
@@ -1066,8 +1099,6 @@ 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;
@@ -1077,32 +1108,21 @@ void OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_au
RasterizerGLES3::register_config();
RasterizerGLES3::make_current();
- //rasterizer = instance_RasterizerGLES2();
- //visual_server = memnew( VisualServerRaster(rasterizer) );
-
visual_server = memnew(VisualServerRaster);
if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) {
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());
}
@@ -1110,6 +1130,8 @@ void OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_au
void OS_OSX::finalize() {
CFNotificationCenterRemoveObserver(CFNotificationCenterGetDistributedCenter(), NULL, kTISNotifySelectedKeyboardInputSourceChanged, NULL);
+ CGDisplayRemoveReconfigurationCallback(displays_arrangement_changed, NULL);
+
delete_main_loop();
memdelete(joypad_osx);
@@ -1118,12 +1140,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) {
@@ -1201,13 +1217,6 @@ public:
typedef UnixTerminalLogger OSXTerminalLogger;
#endif
-void OS_OSX::initialize_logger() {
- Vector<Logger *> loggers;
- loggers.push_back(memnew(OSXTerminalLogger));
- 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];
@@ -1274,7 +1283,8 @@ void OS_OSX::warp_mouse_position(const Point2 &p_to) {
//local point in window coords
const NSRect contentRect = [window_view frame];
- NSRect pointInWindowRect = NSMakeRect(p_to.x / display_scale, contentRect.size.height - (p_to.y / display_scale) - 1, 0, 0);
+ float displayScale = _display_scale();
+ NSRect pointInWindowRect = NSMakeRect(p_to.x / displayScale, contentRect.size.height - (p_to.y / displayScale) - 1, 0, 0);
NSPoint pointOnScreen = [[window_view window] convertRectToScreen:pointInWindowRect].origin;
//point in scren coords
@@ -1348,6 +1358,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;
@@ -1475,17 +1522,43 @@ 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;
+}
+
int OS_OSX::get_current_screen() const {
- Vector2 wpos = get_window_position();
-
- int count = get_screen_count();
- for (int i = 0; i < count; i++) {
- Point2 pos = get_screen_position(i);
- Size2 size = get_screen_size(i);
- if ((wpos.x >= pos.x && wpos.x < pos.x + size.width) && (wpos.y >= pos.y && wpos.y < pos.y + size.height))
- return i;
+ if (window_object) {
+ return get_screen_index([window_object screen]);
+ } else {
+ return get_screen_index([NSScreen mainScreen]);
}
- return 0;
};
void OS_OSX::set_current_screen(int p_screen) {
@@ -1493,26 +1566,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 = 1.0;
-
- if (display_scale > 1.0 && [[screenArray objectAtIndex:p_screen] respondsToSelector:@selector(backingScaleFactor)]) {
- displayScale = [[screenArray objectAtIndex:p_screen] backingScaleFactor];
- }
-
+ 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();
@@ -1520,12 +1597,7 @@ int OS_OSX::get_screen_dpi(int p_screen) const {
NSArray *screenArray = [NSScreen screens];
if (p_screen < [screenArray count]) {
- float displayScale = 1.0;
-
- if (display_scale > 1.0 && [[screenArray objectAtIndex:p_screen] respondsToSelector:@selector(backingScaleFactor)]) {
- displayScale = [[screenArray objectAtIndex:p_screen] backingScaleFactor];
- }
-
+ float displayScale = _display_scale([screenArray objectAtIndex:p_screen]);
NSDictionary *description = [[screenArray objectAtIndex:p_screen] deviceDescription];
NSSize displayPixelSize = [[description objectForKey:NSDeviceSize] sizeValue];
CGSize displayPhysicalSize = CGDisplayScreenSize(
@@ -1544,12 +1616,7 @@ Size2 OS_OSX::get_screen_size(int p_screen) const {
NSArray *screenArray = [NSScreen screens];
if (p_screen < [screenArray count]) {
- float displayScale = 1.0;
-
- if (display_scale > 1.0 && [[screenArray objectAtIndex:p_screen] respondsToSelector:@selector(backingScaleFactor)]) {
- displayScale = [[screenArray objectAtIndex:p_screen] backingScaleFactor];
- }
-
+ float displayScale = _display_scale([screenArray objectAtIndex:p_screen]);
// Note: Use frame to get the whole screen size
NSRect nsrect = [[screenArray objectAtIndex:p_screen] frame];
return Size2(nsrect.size.width, nsrect.size.height) * displayScale;
@@ -1583,27 +1650,66 @@ void OS_OSX::_update_window() {
}
}
-Point2 OS_OSX::get_window_position() const {
+float OS_OSX::_display_scale() const {
+ if (window_object) {
+ return _display_scale([window_object screen]);
+ } else {
+ return _display_scale([NSScreen mainScreen]);
+ }
+}
- Size2 wp([window_object frame].origin.x, [window_object frame].origin.y);
- wp *= display_scale;
- return wp;
+float OS_OSX::_display_scale(id screen) const {
+ if (is_hidpi_allowed()) {
+ if ([screen respondsToSelector:@selector(backingScaleFactor)]) {
+ return fmax(1.0, [screen backingScaleFactor]);
+ }
+ } else {
+ return 1.0;
+ }
+}
+
+Point2 OS_OSX::get_native_window_position() const {
+
+ NSRect nsrect = [window_object frame];
+ Point2 pos;
+ float display_scale = _display_scale();
+
+ // 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 / display_scale;
- // For OS X the y starts at the bottom
- pos.y = (scr.height - p_position.y) / display_scale;
+ pos.x = p_position.x / 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;
@@ -1825,6 +1931,8 @@ OS::LatinKeyboardVariant OS_OSX::get_latin_keyboard_variant() const {
layout = LATIN_KEYBOARD_DVORAK;
} else if ([test isEqualToString:@"xvlcwk"]) {
layout = LATIN_KEYBOARD_NEO;
+ } else if ([test isEqualToString:@"qwfpgj"]) {
+ layout = LATIN_KEYBOARD_COLEMAK;
}
[test release];
@@ -1947,6 +2055,23 @@ Error OS_OSX::move_to_trash(const String &p_path) {
return OK;
}
+void OS_OSX::set_use_vsync(bool p_enable) {
+ CGLContextObj ctx = CGLGetCurrentContext();
+ if (ctx) {
+ GLint swapInterval = p_enable ? 1 : 0;
+ CGLSetParameter(ctx, kCGLCPSwapInterval, &swapInterval);
+ }
+}
+
+bool OS_OSX::is_vsync_enabled() const {
+ GLint swapInterval = 0;
+ CGLContextObj ctx = CGLGetCurrentContext();
+ if (ctx) {
+ CGLGetParameter(ctx, kCGLCPSwapInterval, &swapInterval);
+ }
+ return swapInterval ? true : false;
+}
+
OS_OSX *OS_OSX::singleton = NULL;
OS_OSX::OS_OSX() {
@@ -2039,9 +2164,10 @@ OS_OSX::OS_OSX() {
minimized = false;
window_size = Vector2(1024, 600);
zoomed = false;
- display_scale = 1.0;
- _set_logger(memnew(OSXTerminalLogger));
+ Vector<Logger *> loggers;
+ loggers.push_back(memnew(OSXTerminalLogger));
+ _set_logger(memnew(CompositeLogger(loggers)));
}
bool OS_OSX::_check_internal_feature_support(const String &p_feature) {
diff --git a/platform/register_platform_apis.h b/platform/register_platform_apis.h
new file mode 100644
index 0000000000..37f98f6cd3
--- /dev/null
+++ b/platform/register_platform_apis.h
@@ -0,0 +1,36 @@
+/*************************************************************************/
+/* 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) */
+/* */
+/* 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 REGISTER_APIS_H
+#define REGISTER_APIS_H
+
+void register_platform_apis();
+void unregister_platform_apis();
+
+#endif
diff --git a/platform/server/detect.py b/platform/server/detect.py
index 04b38f280d..ffec2af933 100644
--- a/platform/server/detect.py
+++ b/platform/server/detect.py
@@ -12,6 +12,9 @@ def get_name():
def can_build():
+ # Doesn't build against Godot 3.0 for now, disable to avoid confusing users
+ return False
+
if (os.name != "posix" or sys.platform == "darwin"):
return False
diff --git a/platform/server/os_server.cpp b/platform/server/os_server.cpp
index 300c5cffcc..5b852af738 100644
--- a/platform/server/os_server.cpp
+++ b/platform/server/os_server.cpp
@@ -31,7 +31,6 @@
//#include "servers/visual/rasterizer_dummy.h"
#include "os_server.h"
#include "print_string.h"
-#include "servers/physics/physics_server_sw.h"
#include <stdio.h>
#include <stdlib.h>
@@ -47,10 +46,6 @@ 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);
-}
void OS_Server::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) {
@@ -75,15 +70,10 @@ void OS_Server::initialize(const VideoMode &p_desired, int p_video_driver, int p
ERR_FAIL_COND(!visual_server);
visual_server->init();
- //
- physics_server = memnew(PhysicsServerSW);
- physics_server->init();
- physics_2d_server = memnew(Physics2DServerSW);
- physics_2d_server->init();
input = memnew(InputDefault);
- _ensure_data_dir();
+ _ensure_user_data_dir();
}
void OS_Server::finalize() {
@@ -111,12 +101,6 @@ void OS_Server::finalize() {
memdelete(visual_server);
//memdelete(rasterizer);
- physics_server->finish();
- memdelete(physics_server);
-
- physics_2d_server->finish();
- memdelete(physics_2d_server);
-
memdelete(input);
args.clear();
diff --git a/platform/server/os_server.h b/platform/server/os_server.h
index ba12f649be..03f7c2a6c8 100644
--- a/platform/server/os_server.h
+++ b/platform/server/os_server.h
@@ -35,8 +35,6 @@
#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"
@@ -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;
@@ -71,7 +66,6 @@ class OS_Server : public OS_Unix {
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 void finalize();
diff --git a/platform/uwp/SCsub b/platform/uwp/SCsub
index 7ee5aa2ac3..bbd329a7e5 100644
--- a/platform/uwp/SCsub
+++ b/platform/uwp/SCsub
@@ -8,6 +8,7 @@ files = [
'#platform/windows/packet_peer_udp_winsock.cpp',
'#platform/windows/stream_peer_winsock.cpp',
'#platform/windows/key_mapping_win.cpp',
+ '#platform/windows/windows_terminal_logger.cpp',
'joypad_uwp.cpp',
'power_uwp.cpp',
'gl_context_egl.cpp',
diff --git a/platform/uwp/app.h b/platform/uwp/app.h
index e079fa9c9d..b812512a98 100644
--- a/platform/uwp/app.h
+++ b/platform/uwp/app.h
@@ -33,6 +33,7 @@
#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..7cc8afff06 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])
diff --git a/platform/uwp/export/export.cpp b/platform/uwp/export/export.cpp
index d66bcaa91c..120df9bc3f 100644
--- a/platform/uwp/export/export.cpp
+++ b/platform/uwp/export/export.cpp
@@ -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) {
@@ -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);
diff --git a/platform/uwp/gl_context_egl.cpp b/platform/uwp/gl_context_egl.cpp
index ed3db65cdf..dafe5d5e25 100644
--- a/platform/uwp/gl_context_egl.cpp
+++ b/platform/uwp/gl_context_egl.cpp
@@ -101,26 +101,25 @@ 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"));
diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp
index 031c714514..1655caf04b 100644
--- a/platform/uwp/os_uwp.cpp
+++ b/platform/uwp/os_uwp.cpp
@@ -28,6 +28,7 @@
/* 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"
@@ -69,12 +70,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 {
@@ -183,13 +179,6 @@ void OSUWP::initialize_core() {
cursor_shape = CURSOR_ARROW;
}
-void OSUWP::initialize_logger() {
- Vector<Logger *> loggers;
- loggers.push_back(memnew(WindowsTerminalLogger));
- loggers.push_back(memnew(RotatedFileLogger("user://logs/log.txt")));
- _set_logger(memnew(CompositeLogger(loggers)));
-}
-
bool OSUWP::can_draw() const {
return !minimized;
@@ -262,13 +251,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);
@@ -308,7 +290,7 @@ 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();
@@ -367,12 +349,6 @@ void OSUWP::finalize() {
memdelete(input);
- physics_server->finish();
- memdelete(physics_server);
-
- physics_2d_server->finish();
- memdelete(physics_2d_server);
-
joypad = nullptr;
}
@@ -674,7 +650,7 @@ void OSUWP::set_cursor_shape(CursorShape p_shape) {
cursor_shape = p_shape;
}
-Error OSUWP::execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id, String *r_pipe, int *r_exitcode) {
+Error OSUWP::execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id, String *r_pipe, int *r_exitcode, bool read_stderr) {
return FAILED;
};
@@ -797,7 +773,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;
@@ -849,7 +825,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 df38faa8e1..8d69cd53fd 100644
--- a/platform/uwp/os_uwp.h
+++ b/platform/uwp/os_uwp.h
@@ -40,8 +40,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 +92,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,12 +154,9 @@ 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);
@@ -215,7 +208,7 @@ public:
virtual void delay_usec(uint32_t p_usec) const;
virtual uint64_t get_ticks_usec() const;
- virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL);
+ virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false);
virtual Error kill(const ProcessID &p_pid);
virtual bool has_environment(const String &p_var) const;
@@ -232,7 +225,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);
diff --git a/platform/windows/context_gl_win.cpp b/platform/windows/context_gl_win.cpp
index 8640f27699..81aa18dd23 100644
--- a/platform/windows/context_gl_win.cpp
+++ b/platform/windows/context_gl_win.cpp
@@ -27,25 +27,12 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED) || defined(GLES2_ENABLED)
-
-//
-// 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
@@ -165,7 +152,7 @@ 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_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB /*| _WGL_CONTEXT_DEBUG_BIT_ARB*/,
0
}; //zero indicates the end of the array
diff --git a/platform/windows/context_gl_win.h b/platform/windows/context_gl_win.h
index 912d4d0133..5a280b0d08 100644
--- a/platform/windows/context_gl_win.h
+++ b/platform/windows/context_gl_win.h
@@ -27,18 +27,9 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED) || defined(GLES2_ENABLED)
-//
-// 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..feea3911b2 100644
--- a/platform/windows/crash_handler_win.cpp
+++ b/platform/windows/crash_handler_win.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "main/main.h"
#include "os_windows.h"
+#include "project_settings.h"
#ifdef CRASH_HANDLER_EXCEPTION
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index 031b397988..fbb02c9d1b 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -44,7 +44,6 @@ def can_build():
if (os.system(mingw64 + test) == 0 or os.system(mingw32 + test) == 0):
return True
- print("Could not detect MinGW. Ensure its binaries are in your PATH or that MINGW32_PREFIX or MINGW64_PREFIX are properly defined.")
return False
@@ -65,7 +64,6 @@ def get_opts():
return [
('mingw_prefix_32', 'MinGW prefix (Win32)', mingw32),
('mingw_prefix_64', 'MinGW prefix (Win64)', mingw64),
- BoolVariable('use_lto', 'Use link time optimization (when using MingW)', False),
EnumVariable('debug_symbols', 'Add debug symbols to release version', 'yes', ('yes', 'no', 'full')),
]
@@ -265,10 +263,7 @@ def configure(env):
if env['use_lto']:
env.Append(CCFLAGS=['-flto'])
- if not env['use_llvm'] and env.GetOption("num_jobs") > 1:
- env.Append(LINKFLAGS=['-flto=' + str(env.GetOption("num_jobs"))])
- else:
- env.Append(LINKFLAGS=['-flto'])
+ env.Append(LINKFLAGS=['-flto=' + str(env.GetOption("num_jobs"))])
## Compile flags
diff --git a/platform/windows/godot_res.rc b/platform/windows/godot_res.rc
index b86869d316..c535a749c0 100644
--- a/platform/windows/godot_res.rc
+++ b/platform/windows/godot_res.rc
@@ -23,13 +23,13 @@ BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "Godot Engine"
- VALUE "FileDescription", _MKSTR(VERSION_NAME) " Editor"
+ VALUE "FileDescription", VERSION_NAME " Editor"
VALUE "FileVersion", _MKSTR(VERSION_MAJOR) "." _MKSTR(VERSION_MINOR) "." _MKSTR(VERSION_PATCH)
- VALUE "ProductName", _MKSTR(VERSION_NAME)
+ 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 "Info", "https://godotengine.org"
+ VALUE "ProductVersion", _MKSTR(VERSION_MAJOR) "." _MKSTR(VERSION_MINOR) PATCH_STRING "." VERSION_BUILD
END
END
BLOCK "VarFileInfo"
diff --git a/platform/windows/key_mapping_win.cpp b/platform/windows/key_mapping_win.cpp
index 57f8e965de..76bb5d5723 100644
--- a/platform/windows/key_mapping_win.cpp
+++ b/platform/windows/key_mapping_win.cpp
@@ -50,7 +50,7 @@ static _WinTranslatePair _vk_to_keycode[] = {
{ KEY_CONTROL, VK_CONTROL }, //(0x11)
- { KEY_MENU, VK_MENU }, //(0x12)
+ { KEY_ALT, VK_MENU }, //(0x12)
{ KEY_PAUSE, VK_PAUSE }, //(0x13)
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index bee8c90ad3..c189b3b744 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -42,12 +42,12 @@
#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>
@@ -144,12 +144,7 @@ int OS_Windows::get_video_driver_count() const {
}
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);
+ return "GLES3";
}
int OS_Windows::get_audio_driver_count() const {
@@ -206,13 +201,6 @@ void OS_Windows::initialize_core() {
cursor_shape = CURSOR_ARROW;
}
-void OS_Windows::initialize_logger() {
- Vector<Logger *> loggers;
- loggers.push_back(memnew(WindowsTerminalLogger));
- loggers.push_back(memnew(RotatedFileLogger("user://logs/log.txt")));
- _set_logger(memnew(CompositeLogger(loggers)));
-}
-
bool OS_Windows::can_draw() const {
return !minimized;
@@ -1056,12 +1044,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
@@ -1100,7 +1082,7 @@ void OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int
//RegisterTouchWindow(hWnd, 0); // Windows 7
- _ensure_data_dir();
+ _ensure_user_data_dir();
DragAcceptFiles(hWnd, true);
@@ -1223,12 +1205,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() {
@@ -1812,7 +1788,7 @@ void OS_Windows::set_cursor_shape(CursorShape p_shape) {
cursor_shape = p_shape;
}
-Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id, String *r_pipe, int *r_exitcode) {
+Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id, String *r_pipe, int *r_exitcode, bool read_stderr) {
if (p_blocking && r_pipe) {
@@ -2147,6 +2123,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;
@@ -2183,18 +2196,17 @@ 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 != "") {
+String OS_Windows::get_user_data_dir() const {
- if (has_environment("APPDATA")) {
+ String appname = get_safe_application_name();
+ if (appname != "") {
- 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("\\", "/");
+ bool use_godot_dir = ProjectSettings::get_singleton()->get("application/config/use_shared_user_dir");
+ if (use_godot_dir) {
+ return get_data_path().plus_file(get_godot_dir_name()).plus_file("app_userdata").plus_file(appname).replace("\\", "/");
+ } else {
+ return get_data_path().plus_file(appname).replace("\\", "/");
}
}
@@ -2305,7 +2317,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 9560bc61ca..4367297262 100644
--- a/platform/windows/os_windows.h
+++ b/platform/windows/os_windows.h
@@ -29,8 +29,8 @@
/*************************************************************************/
#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 +38,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 +46,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 +87,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
@@ -147,12 +142,9 @@ 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);
@@ -240,7 +232,7 @@ public:
virtual void delay_usec(uint32_t p_usec) const;
virtual uint64_t get_ticks_usec() const;
- virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL);
+ virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false);
virtual Error kill(const ProcessID &p_pid);
virtual int get_process_id() const;
@@ -260,8 +252,14 @@ public:
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 void release_rendering_thread();
virtual void make_rendering_thread();
diff --git a/platform/x11/context_gl_x11.cpp b/platform/x11/context_gl_x11.cpp
index 0cc9734119..4f9d4a84b9 100644
--- a/platform/x11/context_gl_x11.cpp
+++ b/platform/x11/context_gl_x11.cpp
@@ -30,7 +30,7 @@
#include "context_gl_x11.h"
#ifdef X11_ENABLED
-#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED)
+#if defined(OPENGL_ENABLED)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
diff --git a/platform/x11/context_gl_x11.h b/platform/x11/context_gl_x11.h
index ba01b51d59..c37bac5e9b 100644
--- a/platform/x11/context_gl_x11.h
+++ b/platform/x11/context_gl_x11.h
@@ -35,7 +35,7 @@
*/
#ifdef X11_ENABLED
-#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED)
+#if defined(OPENGL_ENABLED)
#include "drivers/gl_context/context_gl.h"
#include "os/os.h"
diff --git a/platform/x11/crash_handler_x11.cpp b/platform/x11/crash_handler_x11.cpp
index 3c54d5cbc2..005a7459f9 100644
--- a/platform/x11/crash_handler_x11.cpp
+++ b/platform/x11/crash_handler_x11.cpp
@@ -33,6 +33,7 @@
#include "main/main.h"
#include "os_x11.h"
+#include "project_settings.h"
#ifdef CRASH_HANDLER_ENABLED
#include <cxxabi.h>
diff --git a/platform/x11/detect.py b/platform/x11/detect.py
index 1f7f67fe10..3d07851c4f 100644
--- a/platform/x11/detect.py
+++ b/platform/x11/detect.py
@@ -52,7 +52,6 @@ def get_opts():
BoolVariable('use_static_cpp', 'Link stdc++ statically', False),
BoolVariable('use_sanitizer', 'Use LLVM compiler address sanitizer', False),
BoolVariable('use_leak_sanitizer', 'Use LLVM compiler memory leaks sanitizer (implies use_sanitizer)', False),
- BoolVariable('use_lto', 'Use link time optimization', False),
BoolVariable('pulseaudio', 'Detect & use pulseaudio', True),
BoolVariable('udev', 'Use udev for gamepad connection callbacks', False),
EnumVariable('debug_symbols', 'Add debug symbols to release version', 'yes', ('yes', 'no', 'full')),
@@ -101,6 +100,10 @@ def configure(env):
## Compiler configuration
+ if 'CXX' in env and 'clang' in env['CXX']:
+ # Convenience check to enforce the use_llvm overrides when CXX is clang(++)
+ env['use_llvm'] = True
+
if env['use_llvm']:
if ('clang++' not in env['CXX']):
env["CC"] = "clang"
@@ -233,12 +236,15 @@ 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"):
env.Append(LIBS=['dl'])
+ if (platform.system().find("BSD") >= 0):
+ env.Append(LIBS=['execinfo'])
+
## Cross-compilation
if (is64 and env["bits"] == "32"):
diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp
index 041666a594..d1aa129e77 100644
--- a/platform/x11/os_x11.cpp
+++ b/platform/x11/os_x11.cpp
@@ -32,10 +32,13 @@
#include "errno.h"
#include "key_mapping_x11.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"
+
+#ifdef HAVE_MNTENT
#include <mntent.h>
+#endif
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -79,10 +82,6 @@ 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();
}
@@ -235,7 +234,7 @@ void OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_au
// maybe contextgl wants to be in charge of creating the window
//print_line("def videomode "+itos(current_videomode.width)+","+itos(current_videomode.height));
-#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED)
+#if defined(OPENGL_ENABLED)
context_gl = memnew(ContextGL_X11(x11_display, x11_window, current_videomode, true));
context_gl->initialize();
@@ -251,41 +250,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;
@@ -458,12 +429,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);
@@ -471,11 +436,20 @@ 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 (XCheckIfEvent(x11_display, &xevent, _check_window_events, NULL)) {
+ _window_changed(&xevent);
+ }
}
+int OS_X11::_check_window_events(Display *display, XEvent *event, char *arg) {
+ if (event->type == ConfigureNotify) return 1;
+ return 0;
+}
void OS_X11::xim_destroy_callback(::XIM im, ::XPointer client_data,
::XPointer call_data) {
@@ -519,12 +493,6 @@ void OS_X11::finalize() {
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)
@@ -533,7 +501,7 @@ void OS_X11::finalize() {
XUnmapWindow(x11_display, x11_window);
XDestroyWindow(x11_display, x11_window);
-#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED)
+#if defined(OPENGL_ENABLED)
memdelete(context_gl);
#endif
for (int i = 0; i < CURSOR_MAX; i++) {
@@ -661,6 +629,9 @@ 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 (current_videomode.fullscreen == p_enabled)
+ return;
+
if (p_enabled && !is_window_resizable()) {
// Set the window as resizable to prevent window managers to ignore the fullscreen state flag.
XSizeHints *xsh;
@@ -984,6 +955,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);
@@ -1430,6 +1404,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));
@@ -1511,18 +1499,7 @@ void OS_X11::process_xevents() {
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: {
@@ -1939,7 +1916,7 @@ Error OS_X11::shell_open(String p_uri) {
Error ok;
List<String> args;
args.push_back(p_uri);
- ok = execute("/usr/bin/xdg-open", args, false);
+ ok = execute("xdg-open", args, false);
if (ok == OK)
return OK;
ok = execute("gnome-open", args, false);
@@ -1954,6 +1931,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;
@@ -2003,7 +2013,7 @@ String OS_X11::get_system_dir(SystemDir p_dir) const {
String pipe;
List<String> arg;
arg.push_back(xdgparam);
- Error err = const_cast<OS_X11 *>(this)->execute("/usr/bin/xdg-user-dir", arg, true, NULL, &pipe);
+ Error err = const_cast<OS_X11 *>(this)->execute("xdg-user-dir", arg, true, NULL, &pipe);
if (err != OK)
return ".";
return pipe.strip_edges();
@@ -2053,7 +2063,7 @@ void OS_X11::alert(const String &p_alert, const String &p_title) {
args.push_back(p_title);
args.push_back(p_alert);
- execute("/usr/bin/xmessage", args, true);
+ execute("xmessage", args, true);
}
void OS_X11::set_icon(const Ref<Image> &p_icon) {
@@ -2182,6 +2192,7 @@ static String get_mountpoint(const String &p_path) {
return "";
}
+#ifdef HAVE_MNTENT
dev_t dev = s.st_dev;
FILE *fd = setmntent("/proc/mounts", "r");
if (!fd) {
@@ -2199,6 +2210,7 @@ static String get_mountpoint(const String &p_path) {
}
endmntent(fd);
+#endif
return "";
}
@@ -2236,17 +2248,49 @@ Error OS_X11::move_to_trash(const String &p_path) {
List<String> args;
args.push_back("-p");
args.push_back(trashcan);
- Error err = execute("/bin/mkdir", args, true);
+ Error err = execute("mkdir", args, true);
if (err == OK) {
List<String> args2;
args2.push_back(p_path);
args2.push_back(trashcan);
- err = execute("/bin/mv", args2, true);
+ err = execute("mv", args2, true);
}
return err;
}
+OS::LatinKeyboardVariant OS_X11::get_latin_keyboard_variant() const {
+
+ XkbDescRec *xkbdesc = XkbAllocKeyboard();
+ ERR_FAIL_COND_V(!xkbdesc, LATIN_KEYBOARD_QWERTY);
+
+ XkbGetNames(x11_display, XkbSymbolsNameMask, xkbdesc);
+ ERR_FAIL_COND_V(!xkbdesc->names, LATIN_KEYBOARD_QWERTY);
+ ERR_FAIL_COND_V(!xkbdesc->names->symbols, LATIN_KEYBOARD_QWERTY);
+
+ char *layout = XGetAtomName(x11_display, xkbdesc->names->symbols);
+ ERR_FAIL_COND_V(!layout, LATIN_KEYBOARD_QWERTY);
+
+ Vector<String> info = String(layout).split("+");
+ ERR_FAIL_INDEX_V(1, info.size(), LATIN_KEYBOARD_QWERTY);
+
+ if (info[1].find("colemak") != -1) {
+ return LATIN_KEYBOARD_COLEMAK;
+ } else if (info[1].find("qwertz") != -1) {
+ return LATIN_KEYBOARD_QWERTZ;
+ } else if (info[1].find("azerty") != -1) {
+ return LATIN_KEYBOARD_AZERTY;
+ } else if (info[1].find("qzerty") != -1) {
+ return LATIN_KEYBOARD_QZERTY;
+ } else if (info[1].find("dvorak") != -1) {
+ return LATIN_KEYBOARD_DVORAK;
+ } else if (info[1].find("neo") != -1) {
+ return LATIN_KEYBOARD_NEO;
+ }
+
+ return LATIN_KEYBOARD_QWERTY;
+}
+
OS_X11::OS_X11() {
#ifdef PULSEAUDIO_ENABLED
diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h
index 0d5c272ed4..a74e6ee5f3 100644
--- a/platform/x11/os_x11.h
+++ b/platform/x11/os_x11.h
@@ -42,9 +42,6 @@
#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>
@@ -94,7 +91,7 @@ class OS_X11 : public OS_Unix {
int xdnd_version;
-#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED)
+#if defined(OPENGL_ENABLED)
ContextGL_X11 *context_gl;
#endif
//Rasterizer *rasterizer;
@@ -121,10 +118,8 @@ class OS_X11 : public OS_Unix {
uint64_t last_click_ms;
uint32_t last_button_state;
- 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;
@@ -182,7 +177,6 @@ class OS_X11 : public OS_Unix {
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;
@@ -193,6 +187,9 @@ protected:
virtual void set_main_loop(MainLoop *p_main_loop);
+ void _window_changed(XEvent *xevent);
+ static int _check_window_events(Display *display, XEvent *xevent, char *arg);
+
public:
virtual String get_name();
@@ -219,6 +216,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);
@@ -275,6 +276,8 @@ public:
virtual Error move_to_trash(const String &p_path);
+ virtual LatinKeyboardVariant get_latin_keyboard_variant() const;
+
OS_X11();
};