summaryrefslogtreecommitdiff
path: root/platform
diff options
context:
space:
mode:
Diffstat (limited to 'platform')
-rw-r--r--platform/android/detect.py24
-rw-r--r--platform/android/export/export_plugin.cpp15
-rw-r--r--platform/android/export/export_plugin.h2
-rw-r--r--platform/android/java/app/config.gradle4
-rw-r--r--platform/android/java/gradle/wrapper/gradle-wrapper.jarbin54329 -> 59203 bytes
-rw-r--r--platform/android/java/gradle/wrapper/gradle-wrapper.properties5
-rwxr-xr-xplatform/android/java/gradlew53
-rw-r--r--platform/android/java/gradlew.bat47
-rw-r--r--platform/android/os_android.cpp7
-rw-r--r--platform/android/os_android.h1
-rw-r--r--platform/iphone/app_delegate.mm6
-rw-r--r--platform/iphone/detect.py1
-rw-r--r--platform/iphone/godot_iphone.mm4
-rw-r--r--platform/iphone/os_iphone.h5
-rw-r--r--platform/iphone/os_iphone.mm7
-rw-r--r--platform/javascript/.eslintrc.js8
-rw-r--r--platform/javascript/.eslintrc.libs.js1
-rw-r--r--platform/javascript/SCsub1
-rw-r--r--platform/javascript/api/javascript_tools_editor_plugin.cpp3
-rw-r--r--platform/javascript/audio_driver_javascript.cpp5
-rw-r--r--platform/javascript/detect.py1
-rw-r--r--platform/javascript/display_server_javascript.cpp338
-rw-r--r--platform/javascript/display_server_javascript.h43
-rw-r--r--platform/javascript/export/export_plugin.cpp4
-rw-r--r--platform/javascript/godot_js.h30
-rw-r--r--platform/javascript/js/libs/library_godot_audio.js2
-rw-r--r--platform/javascript/js/libs/library_godot_display.js482
-rw-r--r--platform/javascript/js/libs/library_godot_input.js526
-rw-r--r--platform/javascript/js/libs/library_godot_os.js40
-rw-r--r--platform/linuxbsd/detect.py1
-rw-r--r--platform/linuxbsd/display_server_x11.cpp116
-rw-r--r--platform/linuxbsd/display_server_x11.h1
-rw-r--r--platform/linuxbsd/freedesktop_screensaver.cpp4
-rw-r--r--platform/linuxbsd/key_mapping_x11.cpp12
-rw-r--r--platform/linuxbsd/key_mapping_x11.h1
-rw-r--r--platform/linuxbsd/os_linuxbsd.cpp5
-rw-r--r--platform/osx/detect.py1
-rw-r--r--platform/osx/display_server_osx.h1
-rw-r--r--platform/osx/display_server_osx.mm292
-rw-r--r--platform/osx/export/export_plugin.cpp53
-rw-r--r--platform/osx/os_osx.h1
-rw-r--r--platform/osx/os_osx.mm40
-rw-r--r--platform/uwp/detect.py1
-rw-r--r--platform/uwp/os_uwp.cpp4
-rw-r--r--platform/uwp/os_uwp.h1
-rw-r--r--platform/windows/context_gl_windows.h1
-rw-r--r--platform/windows/crash_handler_windows.h1
-rw-r--r--platform/windows/detect.py2
-rw-r--r--platform/windows/display_server_windows.cpp72
-rw-r--r--platform/windows/display_server_windows.h2
-rw-r--r--platform/windows/key_mapping_windows.cpp10
-rw-r--r--platform/windows/key_mapping_windows.h3
-rw-r--r--platform/windows/os_windows.cpp35
-rw-r--r--platform/windows/os_windows.h2
-rw-r--r--platform/windows/vulkan_context_win.h2
-rw-r--r--platform/windows/windows_terminal_logger.cpp1
56 files changed, 1334 insertions, 996 deletions
diff --git a/platform/android/detect.py b/platform/android/detect.py
index 61ccad9ac3..6b0c60fe8b 100644
--- a/platform/android/detect.py
+++ b/platform/android/detect.py
@@ -27,8 +27,7 @@ def get_opts():
("ANDROID_NDK_ROOT", "Path to the Android NDK", get_android_ndk_root()),
("ANDROID_SDK_ROOT", "Path to the Android SDK", get_android_sdk_root()),
("ndk_platform", 'Target platform (android-<api>, e.g. "android-24")', "android-24"),
- EnumVariable("android_arch", "Target architecture", "armv7", ("armv7", "arm64v8", "x86", "x86_64")),
- BoolVariable("android_neon", "Enable NEON support (armv7 only)", True),
+ EnumVariable("android_arch", "Target architecture", "arm64v8", ("armv7", "arm64v8", "x86", "x86_64")),
]
@@ -143,10 +142,7 @@ def configure(env):
if env["android_arch"] not in ["armv7", "arm64v8", "x86", "x86_64"]:
env["android_arch"] = "armv7"
- neon_text = ""
- if env["android_arch"] == "armv7" and env["android_neon"]:
- neon_text = " (with NEON)"
- print("Building for Android, platform " + env["ndk_platform"] + " (" + env["android_arch"] + ")" + neon_text)
+ print("Building for Android, platform " + env["ndk_platform"] + " (" + env["android_arch"] + ")")
can_vectorize = True
if env["android_arch"] == "x86":
@@ -174,10 +170,7 @@ def configure(env):
target_subpath = "arm-linux-androideabi-4.9"
abi_subpath = "arm-linux-androideabi"
arch_subpath = "armeabi-v7a"
- if env["android_neon"]:
- env.extra_suffix = ".armv7.neon" + env.extra_suffix
- else:
- env.extra_suffix = ".armv7" + env.extra_suffix
+ env.extra_suffix = ".armv7" + env.extra_suffix
elif env["android_arch"] == "arm64v8":
if get_platform(env["ndk_platform"]) < 21:
print(
@@ -210,6 +203,7 @@ def configure(env):
env.Append(LINKFLAGS=["-O0"])
env.Append(CCFLAGS=["-O0", "-g", "-fno-limit-debug-info"])
env.Append(CPPDEFINES=["_DEBUG", "DEBUG_ENABLED"])
+ env.Append(CPPDEFINES=["DEV_ENABLED"])
env.Append(CPPFLAGS=["-UNDEBUG"])
# Compiler configuration
@@ -288,7 +282,6 @@ def configure(env):
if get_platform(env["ndk_platform"]) >= 24:
env.Append(CPPDEFINES=[("_FILE_OFFSET_BITS", 64)])
- env["neon_enabled"] = False
if env["android_arch"] == "x86":
target_opts = ["-target", "i686-none-linux-android"]
# The NDK adds this if targeting API < 21, so we can drop it when Godot targets it at least
@@ -301,12 +294,9 @@ def configure(env):
target_opts = ["-target", "armv7-none-linux-androideabi"]
env.Append(CCFLAGS="-march=armv7-a -mfloat-abi=softfp".split())
env.Append(CPPDEFINES=["__ARM_ARCH_7__", "__ARM_ARCH_7A__"])
- if env["android_neon"]:
- env["neon_enabled"] = True
- env.Append(CCFLAGS=["-mfpu=neon"])
- env.Append(CPPDEFINES=["__ARM_NEON__"])
- else:
- env.Append(CCFLAGS=["-mfpu=vfpv3-d16"])
+ # Enable ARM NEON instructions to compile more optimized code.
+ env.Append(CCFLAGS=["-mfpu=neon"])
+ env.Append(CPPDEFINES=["__ARM_NEON__"])
elif env["android_arch"] == "arm64v8":
target_opts = ["-target", "aarch64-none-linux-android"]
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index 60ba1c558a..6ef17faf06 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -429,9 +429,8 @@ String EditorExportPlatformAndroid::get_package_name(const String &p_package) co
return pname;
}
-String EditorExportPlatformAndroid::get_assets_directory(const Ref<EditorExportPreset> &p_preset) const {
- int export_format = int(p_preset->get("custom_template/export_format"));
- return export_format == EXPORT_FORMAT_AAB ? AAB_ASSETS_DIRECTORY : APK_ASSETS_DIRECTORY;
+String EditorExportPlatformAndroid::get_assets_directory(const Ref<EditorExportPreset> &p_preset, int p_export_format) const {
+ return p_export_format == EXPORT_FORMAT_AAB ? AAB_ASSETS_DIRECTORY : APK_ASSETS_DIRECTORY;
}
bool EditorExportPlatformAndroid::is_package_name_valid(const String &p_package, String *r_error) const {
@@ -1645,10 +1644,12 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio
}
plugins_changed.clear();
- Vector<String> abis = get_abis();
+ const Vector<String> abis = get_abis();
for (int i = 0; i < abis.size(); ++i) {
- String abi = abis[i];
- bool is_default = (abi == "armeabi-v7a" || abi == "arm64-v8a");
+ const String abi = abis[i];
+ // All Android devices supporting Vulkan run 64-bit Android,
+ // so there is usually no point in exporting for 32-bit Android.
+ const bool is_default = abi == "arm64-v8a";
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "architectures/" + abi), is_default));
}
@@ -2477,7 +2478,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
return ERR_UNCONFIGURED;
}
}
- const String assets_directory = get_assets_directory(p_preset);
+ const String assets_directory = get_assets_directory(p_preset, export_format);
String sdk_path = EDITOR_GET("export/android/android_sdk_path");
ERR_FAIL_COND_V_MSG(sdk_path.is_empty(), ERR_UNCONFIGURED, "Android SDK path must be configured in Editor Settings at 'export/android/android_sdk_path'.");
print_verbose("Android sdk path: " + sdk_path);
diff --git a/platform/android/export/export_plugin.h b/platform/android/export/export_plugin.h
index d33f616f11..e0ffaa718b 100644
--- a/platform/android/export/export_plugin.h
+++ b/platform/android/export/export_plugin.h
@@ -104,7 +104,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
String get_package_name(const String &p_package) const;
- String get_assets_directory(const Ref<EditorExportPreset> &p_preset) const;
+ String get_assets_directory(const Ref<EditorExportPreset> &p_preset, int p_export_format) const;
bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const;
diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle
index fcee54e493..3fafdf8573 100644
--- a/platform/android/java/app/config.gradle
+++ b/platform/android/java/app/config.gradle
@@ -1,12 +1,12 @@
ext.versions = [
- androidGradlePlugin: '4.2.2',
+ androidGradlePlugin: '7.0.3',
compileSdk : 30,
minSdk : 19,
targetSdk : 30,
buildTools : '30.0.3',
kotlinVersion : '1.5.10',
fragmentVersion : '1.3.6',
- javaVersion : 1.8,
+ javaVersion : 11,
ndkVersion : '21.4.7075529' // Also update 'platform/android/detect.py#get_project_ndk_version()' when this is updated.
]
diff --git a/platform/android/java/gradle/wrapper/gradle-wrapper.jar b/platform/android/java/gradle/wrapper/gradle-wrapper.jar
index f6b961fd5a..e708b1c023 100644
--- a/platform/android/java/gradle/wrapper/gradle-wrapper.jar
+++ b/platform/android/java/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/platform/android/java/gradle/wrapper/gradle-wrapper.properties b/platform/android/java/gradle/wrapper/gradle-wrapper.properties
index 74c5636f8a..ffed3a254e 100644
--- a/platform/android/java/gradle/wrapper/gradle-wrapper.properties
+++ b/platform/android/java/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,5 @@
-#Wed Jun 23 23:42:22 PDT 2021
distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
distributionPath=wrapper/dists
-zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/platform/android/java/gradlew b/platform/android/java/gradlew
index cccdd3d517..4f906e0c81 100755
--- a/platform/android/java/gradlew
+++ b/platform/android/java/gradlew
@@ -1,5 +1,21 @@
#!/usr/bin/env sh
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
##############################################################################
##
## Gradle start up script for UN*X
@@ -28,7 +44,7 @@ APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS=""
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
@@ -66,6 +82,7 @@ esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
@@ -109,10 +126,11 @@ if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin ; then
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
@@ -138,19 +156,19 @@ if $cygwin ; then
else
eval `echo args$i`="\"$arg\""
fi
- i=$((i+1))
+ i=`expr $i + 1`
done
case $i in
- (0) set -- ;;
- (1) set -- "$args0" ;;
- (2) set -- "$args0" "$args1" ;;
- (3) set -- "$args0" "$args1" "$args2" ;;
- (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
@@ -159,14 +177,9 @@ save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
-APP_ARGS=$(save "$@")
+APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
-# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
-if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
- cd "$(dirname "$0")"
-fi
-
exec "$JAVACMD" "$@"
diff --git a/platform/android/java/gradlew.bat b/platform/android/java/gradlew.bat
index 11cc30edb0..107acd32c4 100644
--- a/platform/android/java/gradlew.bat
+++ b/platform/android/java/gradlew.bat
@@ -1,7 +1,23 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
-@rem Gradle startup script for Windows
+@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@@ -13,15 +29,18 @@ if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
@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 DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@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
+if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -35,7 +54,7 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-if exist "%JAVA_EXE%" goto init
+if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@@ -45,28 +64,14 @@ echo location of your Java installation.
goto fail
-:init
-@rem Get command-line arguments, handling Windows variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_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=%*
-
: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%
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
@@ -75,7 +80,7 @@ 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
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp
index 21fb31d991..034885aa32 100644
--- a/platform/android/os_android.cpp
+++ b/platform/android/os_android.cpp
@@ -230,10 +230,13 @@ String OS_Android::get_user_data_dir() const {
}
String OS_Android::get_cache_path() const {
+ if (cache_dir_cache != String())
+ return cache_dir_cache;
+
String cache_dir = godot_io_java->get_cache_dir();
if (cache_dir != "") {
- cache_dir = _remove_symlink(cache_dir);
- return cache_dir;
+ cache_dir_cache = _remove_symlink(cache_dir);
+ return cache_dir_cache;
}
return ".";
}
diff --git a/platform/android/os_android.h b/platform/android/os_android.h
index c938297821..ce8083388f 100644
--- a/platform/android/os_android.h
+++ b/platform/android/os_android.h
@@ -57,6 +57,7 @@ private:
#endif
mutable String data_dir_cache;
+ mutable String cache_dir_cache;
AudioDriverOpenSL audio_driver_android;
diff --git a/platform/iphone/app_delegate.mm b/platform/iphone/app_delegate.mm
index d10ea5c68c..c6f91665c5 100644
--- a/platform/iphone/app_delegate.mm
+++ b/platform/iphone/app_delegate.mm
@@ -44,7 +44,7 @@
extern int gargc;
extern char **gargv;
-extern int iphone_main(int, char **, String);
+extern int iphone_main(int, char **, String, String);
extern void iphone_finish();
@implementation AppDelegate
@@ -67,8 +67,10 @@ static ViewController *mainViewController = nil;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
+ paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
+ NSString *cacheDirectory = [paths objectAtIndex:0];
- int err = iphone_main(gargc, gargv, String::utf8([documentsDirectory UTF8String]));
+ int err = iphone_main(gargc, gargv, String::utf8([documentsDirectory UTF8String]), String::utf8([cacheDirectory UTF8String]));
if (err != 0) {
// bail, things did not go very well for us, should probably output a message on screen with our error code...
diff --git a/platform/iphone/detect.py b/platform/iphone/detect.py
index 05e24c5003..3c6453ff9b 100644
--- a/platform/iphone/detect.py
+++ b/platform/iphone/detect.py
@@ -59,6 +59,7 @@ def configure(env):
elif env["target"] == "debug":
env.Append(CCFLAGS=["-gdwarf-2", "-O0"])
env.Append(CPPDEFINES=["_DEBUG", ("DEBUG", 1), "DEBUG_ENABLED"])
+ env.Append(CPPDEFINES=["DEV_ENABLED"])
if env["use_lto"]:
env.Append(CCFLAGS=["-flto"])
diff --git a/platform/iphone/godot_iphone.mm b/platform/iphone/godot_iphone.mm
index 6c3e1eabde..6f6f9d0708 100644
--- a/platform/iphone/godot_iphone.mm
+++ b/platform/iphone/godot_iphone.mm
@@ -74,7 +74,7 @@ int add_cmdline(int p_argc, char **p_args) {
return p_argc;
};
-int iphone_main(int argc, char **argv, String data_dir) {
+int iphone_main(int argc, char **argv, String data_dir, String cache_dir) {
size_t len = strlen(argv[0]);
while (len--) {
@@ -95,7 +95,7 @@ int iphone_main(int argc, char **argv, String data_dir) {
char cwd[512];
getcwd(cwd, sizeof(cwd));
printf("cwd %s\n", cwd);
- os = new OSIPhone(data_dir);
+ os = new OSIPhone(data_dir, cache_dir);
// We must override main when testing is enabled
TEST_MAIN_OVERRIDE
diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h
index 248369369d..7a81d8f593 100644
--- a/platform/iphone/os_iphone.h
+++ b/platform/iphone/os_iphone.h
@@ -72,6 +72,7 @@ private:
virtual void finalize() override;
String user_data_dir;
+ String cache_dir;
bool is_focused = false;
@@ -80,7 +81,7 @@ private:
public:
static OSIPhone *get_singleton();
- OSIPhone(String p_data_dir);
+ OSIPhone(String p_data_dir, String p_cache_dir);
~OSIPhone();
void initialize_modules();
@@ -103,6 +104,8 @@ public:
void set_user_data_dir(String p_dir);
virtual String get_user_data_dir() const override;
+ virtual String get_cache_path() const override;
+
virtual String get_locale() const override;
virtual String get_unique_id() const override;
diff --git a/platform/iphone/os_iphone.mm b/platform/iphone/os_iphone.mm
index c88d253691..fc07d321b7 100644
--- a/platform/iphone/os_iphone.mm
+++ b/platform/iphone/os_iphone.mm
@@ -87,7 +87,7 @@ OSIPhone *OSIPhone::get_singleton() {
return (OSIPhone *)OS::get_singleton();
}
-OSIPhone::OSIPhone(String p_data_dir) {
+OSIPhone::OSIPhone(String p_data_dir, String p_cache_dir) {
for (int i = 0; i < ios_init_callbacks_count; ++i) {
ios_init_callbacks[i]();
}
@@ -101,6 +101,7 @@ OSIPhone::OSIPhone(String p_data_dir) {
// can't call set_data_dir from here, since it requires DirAccess
// which is initialized in initialize_core
user_data_dir = p_data_dir;
+ cache_dir = p_cache_dir;
Vector<Logger *> loggers;
loggers.push_back(memnew(SyslogLogger));
@@ -266,6 +267,10 @@ String OSIPhone::get_user_data_dir() const {
return user_data_dir;
}
+String OSIPhone::get_cache_path() const {
+ return cache_dir;
+}
+
String OSIPhone::get_locale() const {
NSString *preferedLanguage = [NSLocale preferredLanguages].firstObject;
diff --git a/platform/javascript/.eslintrc.js b/platform/javascript/.eslintrc.js
index 0ff9d67d26..2c81f1f02d 100644
--- a/platform/javascript/.eslintrc.js
+++ b/platform/javascript/.eslintrc.js
@@ -39,5 +39,13 @@ module.exports = {
// Closure compiler (exported properties)
"quote-props": ["error", "consistent"],
"dot-notation": "off",
+ // No comma dangle for functions (it's madness, and ES2017)
+ "comma-dangle": ["error", {
+ "arrays": "always-multiline",
+ "objects": "always-multiline",
+ "imports": "always-multiline",
+ "exports": "always-multiline",
+ "functions": "never"
+ }],
}
};
diff --git a/platform/javascript/.eslintrc.libs.js b/platform/javascript/.eslintrc.libs.js
index 81b1b8c864..8e579fd462 100644
--- a/platform/javascript/.eslintrc.libs.js
+++ b/platform/javascript/.eslintrc.libs.js
@@ -15,6 +15,7 @@ module.exports = {
"IDBFS": true,
"GodotOS": true,
"GodotConfig": true,
+ "GodotEventListeners": true,
"GodotRuntime": true,
"GodotFS": true,
"IDHandler": true,
diff --git a/platform/javascript/SCsub b/platform/javascript/SCsub
index 62a8660ae4..fa9e6eed15 100644
--- a/platform/javascript/SCsub
+++ b/platform/javascript/SCsub
@@ -20,6 +20,7 @@ sys_env.AddJSLibraries(
"js/libs/library_godot_fetch.js",
"js/libs/library_godot_os.js",
"js/libs/library_godot_runtime.js",
+ "js/libs/library_godot_input.js",
]
)
diff --git a/platform/javascript/api/javascript_tools_editor_plugin.cpp b/platform/javascript/api/javascript_tools_editor_plugin.cpp
index 45a2cd595a..df4c790755 100644
--- a/platform/javascript/api/javascript_tools_editor_plugin.cpp
+++ b/platform/javascript/api/javascript_tools_editor_plugin.cpp
@@ -131,9 +131,10 @@ void JavaScriptToolsEditorPlugin::_zip_recursive(String p_path, String p_base_pa
}
dir->list_dir_begin();
String cur = dir->get_next();
+ String project_data_dir_name = ProjectSettings::get_singleton()->get_project_data_dir_name();
while (!cur.is_empty()) {
String cs = p_path.plus_file(cur);
- if (cur == "." || cur == ".." || cur == ".import") {
+ if (cur == "." || cur == ".." || cur == project_data_dir_name) {
// Skip
} else if (dir->current_is_dir()) {
String path = cs.replace_first(p_base_path, "") + "/";
diff --git a/platform/javascript/audio_driver_javascript.cpp b/platform/javascript/audio_driver_javascript.cpp
index cfe6c69072..626aef3c60 100644
--- a/platform/javascript/audio_driver_javascript.cpp
+++ b/platform/javascript/audio_driver_javascript.cpp
@@ -117,14 +117,15 @@ Error AudioDriverJavaScript::init() {
if (output_rb) {
memdelete_arr(output_rb);
}
- output_rb = memnew_arr(float, buffer_length *channel_count);
+ const size_t array_size = buffer_length * (size_t)channel_count;
+ output_rb = memnew_arr(float, array_size);
if (!output_rb) {
return ERR_OUT_OF_MEMORY;
}
if (input_rb) {
memdelete_arr(input_rb);
}
- input_rb = memnew_arr(float, buffer_length *channel_count);
+ input_rb = memnew_arr(float, array_size);
if (!input_rb) {
return ERR_OUT_OF_MEMORY;
}
diff --git a/platform/javascript/detect.py b/platform/javascript/detect.py
index 173b558b6d..9494ab6fa5 100644
--- a/platform/javascript/detect.py
+++ b/platform/javascript/detect.py
@@ -82,6 +82,7 @@ def configure(env):
env.Append(LINKFLAGS=["--profiling-funcs"])
else: # "debug"
env.Append(CPPDEFINES=["DEBUG_ENABLED"])
+ env.Append(CPPDEFINES=["DEV_ENABLED"])
env.Append(CCFLAGS=["-O1", "-g"])
env.Append(LINKFLAGS=["-O1", "-g"])
env["use_assertions"] = True
diff --git a/platform/javascript/display_server_javascript.cpp b/platform/javascript/display_server_javascript.cpp
index be4d2cba20..c2eb826db9 100644
--- a/platform/javascript/display_server_javascript.cpp
+++ b/platform/javascript/display_server_javascript.cpp
@@ -62,26 +62,13 @@ bool DisplayServerJavaScript::check_size_force_redraw() {
return godot_js_display_size_update() != 0;
}
-Point2 DisplayServerJavaScript::compute_position_in_canvas(int p_x, int p_y) {
- int point[2];
- godot_js_display_compute_position(p_x, p_y, point, point + 1);
- return Point2(point[0], point[1]);
-}
-
-EM_BOOL DisplayServerJavaScript::fullscreen_change_callback(int p_event_type, const EmscriptenFullscreenChangeEvent *p_event, void *p_user_data) {
+void DisplayServerJavaScript::fullscreen_change_callback(int p_fullscreen) {
DisplayServerJavaScript *display = get_singleton();
- // Empty ID is canvas.
- String target_id = String::utf8(p_event->id);
- if (target_id.is_empty() || target_id == String::utf8(&(display->canvas_id[1]))) {
- // This event property is the only reliable data on
- // browser fullscreen state.
- if (p_event->isFullscreen) {
- display->window_mode = WINDOW_MODE_FULLSCREEN;
- } else {
- display->window_mode = WINDOW_MODE_WINDOWED;
- }
+ if (p_fullscreen) {
+ display->window_mode = WINDOW_MODE_FULLSCREEN;
+ } else {
+ display->window_mode = WINDOW_MODE_WINDOWED;
}
- return false;
}
// Drag and drop callback.
@@ -118,88 +105,51 @@ void DisplayServerJavaScript::request_quit_callback() {
// Keys
-template <typename T>
-void DisplayServerJavaScript::dom2godot_mod(T *emscripten_event_ptr, Ref<InputEventWithModifiers> godot_event) {
- godot_event->set_shift_pressed(emscripten_event_ptr->shiftKey);
- godot_event->set_alt_pressed(emscripten_event_ptr->altKey);
- godot_event->set_ctrl_pressed(emscripten_event_ptr->ctrlKey);
- godot_event->set_meta_pressed(emscripten_event_ptr->metaKey);
+void DisplayServerJavaScript::dom2godot_mod(Ref<InputEventWithModifiers> ev, int p_mod) {
+ ev->set_shift_pressed(p_mod & 1);
+ ev->set_alt_pressed(p_mod & 2);
+ ev->set_ctrl_pressed(p_mod & 4);
+ ev->set_meta_pressed(p_mod & 8);
}
-Ref<InputEventKey> DisplayServerJavaScript::setup_key_event(const EmscriptenKeyboardEvent *emscripten_event) {
+void DisplayServerJavaScript::key_callback(int p_pressed, int p_repeat, int p_modifiers) {
+ DisplayServerJavaScript *ds = get_singleton();
+ JSKeyEvent &key_event = ds->key_event;
+ // Resume audio context after input in case autoplay was denied.
+ OS_JavaScript::get_singleton()->resume_audio();
+
Ref<InputEventKey> ev;
ev.instantiate();
- ev->set_echo(emscripten_event->repeat);
- dom2godot_mod(emscripten_event, ev);
- ev->set_keycode(dom_code2godot_scancode(emscripten_event->code, emscripten_event->key, false));
- ev->set_physical_keycode(dom_code2godot_scancode(emscripten_event->code, emscripten_event->key, true));
-
- String unicode = String::utf8(emscripten_event->key);
- // Check if empty or multi-character (e.g. `CapsLock`).
- if (unicode.length() != 1) {
- // Might be empty as well, but better than nonsense.
- unicode = String::utf8(emscripten_event->charValue);
- }
+ ev->set_echo(p_repeat);
+ ev->set_keycode(dom_code2godot_scancode(key_event.code, key_event.key, false));
+ ev->set_physical_keycode(dom_code2godot_scancode(key_event.code, key_event.key, true));
+ ev->set_pressed(p_pressed);
+ dom2godot_mod(ev, p_modifiers);
+
+ String unicode = String::utf8(key_event.key);
if (unicode.length() == 1) {
ev->set_unicode(unicode[0]);
}
-
- return ev;
-}
-
-EM_BOOL DisplayServerJavaScript::keydown_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data) {
- DisplayServerJavaScript *display = get_singleton();
- Ref<InputEventKey> ev = setup_key_event(p_event);
- ev->set_pressed(true);
- if (ev->get_unicode() == 0 && keycode_has_unicode(ev->get_keycode())) {
- // Defer to keypress event for legacy unicode retrieval.
- display->deferred_key_event = ev;
- // Do not suppress keypress event.
- return false;
- }
Input::get_singleton()->parse_input_event(ev);
// Make sure to flush all events so we can call restricted APIs inside the event.
Input::get_singleton()->flush_buffered_events();
-
- return true;
-}
-
-EM_BOOL DisplayServerJavaScript::keypress_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data) {
- DisplayServerJavaScript *display = get_singleton();
- display->deferred_key_event->set_unicode(p_event->charCode);
- Input::get_singleton()->parse_input_event(display->deferred_key_event);
-
- // Make sure to flush all events so we can call restricted APIs inside the event.
- Input::get_singleton()->flush_buffered_events();
-
- return true;
-}
-
-EM_BOOL DisplayServerJavaScript::keyup_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data) {
- Ref<InputEventKey> ev = setup_key_event(p_event);
- ev->set_pressed(false);
- Input::get_singleton()->parse_input_event(ev);
-
- // Make sure to flush all events so we can call restricted APIs inside the event.
- Input::get_singleton()->flush_buffered_events();
-
- return ev->get_keycode() != KEY_UNKNOWN && ev->get_keycode() != (Key)0;
}
// Mouse
-EM_BOOL DisplayServerJavaScript::mouse_button_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data) {
- DisplayServerJavaScript *display = get_singleton();
+int DisplayServerJavaScript::mouse_button_callback(int p_pressed, int p_button, double p_x, double p_y, int p_modifiers) {
+ DisplayServerJavaScript *ds = get_singleton();
Ref<InputEventMouseButton> ev;
ev.instantiate();
- ev->set_pressed(p_event_type == EMSCRIPTEN_EVENT_MOUSEDOWN);
- ev->set_position(compute_position_in_canvas(p_event->clientX, p_event->clientY));
+ ev->set_pressed(p_pressed);
+ ev->set_position(Point2(p_x, p_y));
ev->set_global_position(ev->get_position());
- dom2godot_mod(p_event, ev);
+ ev->set_pressed(p_pressed);
+ dom2godot_mod(ev, p_modifiers);
- switch (p_event->button) {
+ switch (p_button) {
case DOM_BUTTON_LEFT:
ev->set_button_index(MOUSE_BUTTON_LEFT);
break;
@@ -219,34 +169,30 @@ EM_BOOL DisplayServerJavaScript::mouse_button_callback(int p_event_type, const E
return false;
}
- if (ev->is_pressed()) {
- double diff = emscripten_get_now() - display->last_click_ms;
+ if (p_pressed) {
+ uint64_t diff = (OS::get_singleton()->get_ticks_usec() / 1000) - ds->last_click_ms;
- if (ev->get_button_index() == display->last_click_button_index) {
- if (diff < 400 && Point2(display->last_click_pos).distance_to(ev->get_position()) < 5) {
- display->last_click_ms = 0;
- display->last_click_pos = Point2(-100, -100);
- display->last_click_button_index = -1;
+ if (ev->get_button_index() == ds->last_click_button_index) {
+ if (diff < 400 && Point2(ds->last_click_pos).distance_to(ev->get_position()) < 5) {
+ ds->last_click_ms = 0;
+ ds->last_click_pos = Point2(-100, -100);
+ ds->last_click_button_index = -1;
ev->set_double_click(true);
}
} else {
- display->last_click_button_index = ev->get_button_index();
+ ds->last_click_button_index = ev->get_button_index();
}
if (!ev->is_double_click()) {
- display->last_click_ms += diff;
- display->last_click_pos = ev->get_position();
+ ds->last_click_ms += diff;
+ ds->last_click_pos = ev->get_position();
}
}
- Input *input = Input::get_singleton();
- int mask = input->get_mouse_button_mask();
- MouseButton button_flag = MouseButton(1 << (ev->get_button_index() - 1));
+ int mask = Input::get_singleton()->get_mouse_button_mask();
+ int button_flag = 1 << (ev->get_button_index() - 1);
if (ev->is_pressed()) {
- // Since the event is consumed, focus manually. The containing iframe,
- // if exists, may not have focus yet, so focus even if already focused.
- focus_canvas();
mask |= button_flag;
} else if (mask & button_flag) {
mask &= ~button_flag;
@@ -256,7 +202,9 @@ EM_BOOL DisplayServerJavaScript::mouse_button_callback(int p_event_type, const E
}
ev->set_button_mask(mask);
- input->parse_input_event(ev);
+ Input::get_singleton()->parse_input_event(ev);
+ // Resume audio context after input in case autoplay was denied.
+ OS_JavaScript::get_singleton()->resume_audio();
// Make sure to flush all events so we can call restricted APIs inside the event.
Input::get_singleton()->flush_buffered_events();
@@ -266,31 +214,26 @@ EM_BOOL DisplayServerJavaScript::mouse_button_callback(int p_event_type, const E
return true;
}
-EM_BOOL DisplayServerJavaScript::mousemove_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data) {
- DisplayServerJavaScript *ds = get_singleton();
- Input *input = Input::get_singleton();
- int input_mask = input->get_mouse_button_mask();
- Point2 pos = compute_position_in_canvas(p_event->clientX, p_event->clientY);
+void DisplayServerJavaScript::mouse_move_callback(double p_x, double p_y, double p_rel_x, double p_rel_y, int p_modifiers) {
+ int input_mask = Input::get_singleton()->get_mouse_button_mask();
// For motion outside the canvas, only read mouse movement if dragging
// started inside the canvas; imitating desktop app behaviour.
- if (!ds->cursor_inside_canvas && !input_mask)
- return false;
+ if (!get_singleton()->cursor_inside_canvas && !input_mask)
+ return;
Ref<InputEventMouseMotion> ev;
ev.instantiate();
- dom2godot_mod(p_event, ev);
+ dom2godot_mod(ev, p_modifiers);
ev->set_button_mask(input_mask);
- ev->set_position(pos);
+ ev->set_position(Point2(p_x, p_y));
ev->set_global_position(ev->get_position());
- ev->set_relative(Vector2(p_event->movementX, p_event->movementY));
- input->set_mouse_position(ev->get_position());
- ev->set_speed(input->get_last_mouse_speed());
+ ev->set_relative(Vector2(p_rel_x, p_rel_y));
+ Input::get_singleton()->set_mouse_position(ev->get_position());
+ ev->set_speed(Input::get_singleton()->get_last_mouse_speed());
- input->parse_input_event(ev);
- // Don't suppress mouseover/-leave events.
- return false;
+ Input::get_singleton()->parse_input_event(ev);
}
// Cursor
@@ -430,17 +373,15 @@ void DisplayServerJavaScript::mouse_set_mode(MouseMode p_mode) {
if (p_mode == MOUSE_MODE_VISIBLE) {
godot_js_display_cursor_set_visible(1);
- emscripten_exit_pointerlock();
+ godot_js_display_cursor_lock_set(0);
} else if (p_mode == MOUSE_MODE_HIDDEN) {
godot_js_display_cursor_set_visible(0);
- emscripten_exit_pointerlock();
+ godot_js_display_cursor_lock_set(0);
} else if (p_mode == MOUSE_MODE_CAPTURED) {
godot_js_display_cursor_set_visible(1);
- EMSCRIPTEN_RESULT result = emscripten_request_pointerlock(canvas_id, false);
- ERR_FAIL_COND_MSG(result == EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED, "MOUSE_MODE_CAPTURED can only be entered from within an appropriate input callback.");
- ERR_FAIL_COND_MSG(result != EMSCRIPTEN_RESULT_SUCCESS, "MOUSE_MODE_CAPTURED can only be entered from within an appropriate input callback.");
+ godot_js_display_cursor_lock_set(1);
}
}
@@ -449,18 +390,17 @@ DisplayServer::MouseMode DisplayServerJavaScript::mouse_get_mode() const {
return MOUSE_MODE_HIDDEN;
}
- EmscriptenPointerlockChangeEvent ev;
- emscripten_get_pointerlock_status(&ev);
- return (ev.isActive && String::utf8(ev.id) == String::utf8(&canvas_id[1])) ? MOUSE_MODE_CAPTURED : MOUSE_MODE_VISIBLE;
+ if (godot_js_display_cursor_is_locked()) {
+ return MOUSE_MODE_CAPTURED;
+ }
+ return MOUSE_MODE_VISIBLE;
}
// Wheel
-EM_BOOL DisplayServerJavaScript::wheel_callback(int p_event_type, const EmscriptenWheelEvent *p_event, void *p_user_data) {
- ERR_FAIL_COND_V(p_event_type != EMSCRIPTEN_EVENT_WHEEL, false);
- DisplayServerJavaScript *ds = get_singleton();
- if (!is_canvas_focused()) {
- if (ds->cursor_inside_canvas) {
- focus_canvas();
+int DisplayServerJavaScript::mouse_wheel_callback(double p_delta_x, double p_delta_y) {
+ if (!godot_js_display_canvas_is_focused()) {
+ if (get_singleton()->cursor_inside_canvas) {
+ godot_js_display_canvas_focus();
} else {
return false;
}
@@ -477,16 +417,17 @@ EM_BOOL DisplayServerJavaScript::wheel_callback(int p_event_type, const Emscript
ev->set_ctrl_pressed(input->is_key_pressed(KEY_CTRL));
ev->set_meta_pressed(input->is_key_pressed(KEY_META));
- if (p_event->deltaY < 0)
+ if (p_delta_y < 0) {
ev->set_button_index(MOUSE_BUTTON_WHEEL_UP);
- else if (p_event->deltaY > 0)
+ } else if (p_delta_y > 0) {
ev->set_button_index(MOUSE_BUTTON_WHEEL_DOWN);
- else if (p_event->deltaX > 0)
+ } else if (p_delta_x > 0) {
ev->set_button_index(MOUSE_BUTTON_WHEEL_LEFT);
- else if (p_event->deltaX < 0)
+ } else if (p_delta_x < 0) {
ev->set_button_index(MOUSE_BUTTON_WHEEL_RIGHT);
- else
+ } else {
return false;
+ }
// Different browsers give wildly different delta values, and we can't
// interpret deltaMode, so use default value for wheel events' factor.
@@ -494,7 +435,7 @@ EM_BOOL DisplayServerJavaScript::wheel_callback(int p_event_type, const Emscript
int button_flag = 1 << (ev->get_button_index() - 1);
ev->set_pressed(true);
- ev->set_button_mask(MouseButton(input->get_mouse_button_mask() | button_flag));
+ ev->set_button_mask(input->get_mouse_button_mask() | button_flag);
input->parse_input_event(ev);
Ref<InputEventMouseButton> release = ev->duplicate();
@@ -506,52 +447,43 @@ EM_BOOL DisplayServerJavaScript::wheel_callback(int p_event_type, const Emscript
}
// Touch
-EM_BOOL DisplayServerJavaScript::touch_press_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data) {
- DisplayServerJavaScript *display = get_singleton();
- Ref<InputEventScreenTouch> ev;
- int lowest_id_index = -1;
- for (int i = 0; i < p_event->numTouches; ++i) {
- const EmscriptenTouchPoint &touch = p_event->touches[i];
- if (lowest_id_index == -1 || touch.identifier < p_event->touches[lowest_id_index].identifier)
- lowest_id_index = i;
- if (!touch.isChanged)
- continue;
- ev.instantiate();
- ev->set_index(touch.identifier);
- ev->set_position(compute_position_in_canvas(touch.clientX, touch.clientY));
- display->touches[i] = ev->get_position();
- ev->set_pressed(p_event_type == EMSCRIPTEN_EVENT_TOUCHSTART);
+void DisplayServerJavaScript::touch_callback(int p_type, int p_count) {
+ DisplayServerJavaScript *ds = get_singleton();
- Input::get_singleton()->parse_input_event(ev);
- }
+ const JSTouchEvent &touch_event = ds->touch_event;
+ for (int i = 0; i < p_count; i++) {
+ Point2 point(touch_event.coords[i * 2], touch_event.coords[i * 2 + 1]);
+ if (p_type == 2) {
+ // touchmove
+ Ref<InputEventScreenDrag> ev;
+ ev.instantiate();
+ ev->set_index(touch_event.identifier[i]);
+ ev->set_position(point);
+
+ Point2 &prev = ds->touches[i];
+ ev->set_relative(ev->get_position() - prev);
+ prev = ev->get_position();
+
+ Input::get_singleton()->parse_input_event(ev);
+ } else {
+ // touchstart/touchend
+ Ref<InputEventScreenTouch> ev;
- // Make sure to flush all events so we can call restricted APIs inside the event.
- Input::get_singleton()->flush_buffered_events();
+ // Resume audio context after input in case autoplay was denied.
+ OS_JavaScript::get_singleton()->resume_audio();
- // Resume audio context after input in case autoplay was denied.
- return true;
-}
+ ev.instantiate();
+ ev->set_index(touch_event.identifier[i]);
+ ev->set_position(point);
+ ev->set_pressed(p_type == 0);
+ ds->touches[i] = point;
-EM_BOOL DisplayServerJavaScript::touchmove_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data) {
- DisplayServerJavaScript *display = get_singleton();
- Ref<InputEventScreenDrag> ev;
- int lowest_id_index = -1;
- for (int i = 0; i < p_event->numTouches; ++i) {
- const EmscriptenTouchPoint &touch = p_event->touches[i];
- if (lowest_id_index == -1 || touch.identifier < p_event->touches[lowest_id_index].identifier)
- lowest_id_index = i;
- if (!touch.isChanged)
- continue;
- ev.instantiate();
- ev->set_index(touch.identifier);
- ev->set_position(compute_position_in_canvas(touch.clientX, touch.clientY));
- Point2 &prev = display->touches[i];
- ev->set_relative(ev->get_position() - prev);
- prev = ev->get_position();
-
- Input::get_singleton()->parse_input_event(ev);
+ Input::get_singleton()->parse_input_event(ev);
+
+ // Make sure to flush all events so we can call restricted APIs inside the event.
+ Input::get_singleton()->flush_buffered_events();
+ }
}
- return true;
}
bool DisplayServerJavaScript::screen_is_touchscreen(int p_screen) const {
@@ -595,6 +527,10 @@ void DisplayServerJavaScript::virtual_keyboard_hide() {
godot_js_display_vk_hide();
}
+void DisplayServerJavaScript::window_blur_callback() {
+ Input::get_singleton()->release_pressed_events();
+}
+
// Gamepad
void DisplayServerJavaScript::gamepad_callback(int p_index, int p_connected, const char *p_id, const char *p_guid) {
Input *input = Input::get_singleton();
@@ -607,14 +543,14 @@ void DisplayServerJavaScript::gamepad_callback(int p_index, int p_connected, con
void DisplayServerJavaScript::process_joypads() {
Input *input = Input::get_singleton();
- int32_t pads = godot_js_display_gamepad_sample_count();
+ int32_t pads = godot_js_input_gamepad_sample_count();
int32_t s_btns_num = 0;
int32_t s_axes_num = 0;
int32_t s_standard = 0;
float s_btns[16];
float s_axes[10];
for (int idx = 0; idx < pads; idx++) {
- int err = godot_js_display_gamepad_sample_get(idx, s_btns, &s_btns_num, s_axes, &s_axes_num, &s_standard);
+ int err = godot_js_input_gamepad_sample_get(idx, s_btns, &s_btns_num, s_axes, &s_axes_num, &s_standard);
if (err) {
continue;
}
@@ -712,11 +648,6 @@ void DisplayServerJavaScript::set_icon(const Ref<Image> &p_icon) {
}
void DisplayServerJavaScript::_dispatch_input_event(const Ref<InputEvent> &p_event) {
- OS_JavaScript *os = OS_JavaScript::get_singleton();
-
- // Resume audio context after input in case autoplay was denied.
- os->resume_audio();
-
Callable cb = get_singleton()->input_event_callback;
if (!cb.is_null()) {
Variant ev = p_event;
@@ -782,43 +713,24 @@ DisplayServerJavaScript::DisplayServerJavaScript(const String &p_rendering_drive
video_driver_index = p_video_driver;
#endif
- EMSCRIPTEN_RESULT result;
-#define EM_CHECK(ev) \
- if (result != EMSCRIPTEN_RESULT_SUCCESS) \
- ERR_PRINT("Error while setting " #ev " callback: Code " + itos(result));
-#define SET_EM_CALLBACK(target, ev, cb) \
- result = emscripten_set_##ev##_callback(target, nullptr, true, &cb); \
- EM_CHECK(ev)
-#define SET_EM_WINDOW_CALLBACK(ev, cb) \
- result = emscripten_set_##ev##_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, false, &cb); \
- EM_CHECK(ev)
- // These callbacks from Emscripten's html5.h suffice to access most
- // JavaScript APIs.
- SET_EM_CALLBACK(canvas_id, mousedown, mouse_button_callback)
- SET_EM_WINDOW_CALLBACK(mousemove, mousemove_callback)
- SET_EM_WINDOW_CALLBACK(mouseup, mouse_button_callback)
- SET_EM_CALLBACK(canvas_id, wheel, wheel_callback)
- SET_EM_CALLBACK(canvas_id, touchstart, touch_press_callback)
- SET_EM_CALLBACK(canvas_id, touchmove, touchmove_callback)
- SET_EM_CALLBACK(canvas_id, touchend, touch_press_callback)
- SET_EM_CALLBACK(canvas_id, touchcancel, touch_press_callback)
- SET_EM_CALLBACK(canvas_id, keydown, keydown_callback)
- SET_EM_CALLBACK(canvas_id, keypress, keypress_callback)
- SET_EM_CALLBACK(canvas_id, keyup, keyup_callback)
- SET_EM_CALLBACK(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, fullscreenchange, fullscreen_change_callback)
-#undef SET_EM_CALLBACK
-#undef EM_CHECK
-
- // For APIs that are not (sufficiently) exposed, a
- // library is used below (implemented in library_godot_display.js).
+ // JS Input interface (js/libs/library_godot_input.js)
+ godot_js_input_mouse_button_cb(&DisplayServerJavaScript::mouse_button_callback);
+ godot_js_input_mouse_move_cb(&DisplayServerJavaScript::mouse_move_callback);
+ godot_js_input_mouse_wheel_cb(&DisplayServerJavaScript::mouse_wheel_callback);
+ godot_js_input_touch_cb(&DisplayServerJavaScript::touch_callback, touch_event.identifier, touch_event.coords);
+ godot_js_input_key_cb(&DisplayServerJavaScript::key_callback, key_event.code, key_event.key);
+ godot_js_input_paste_cb(update_clipboard_callback);
+ godot_js_input_drop_files_cb(drop_files_js_callback);
+ godot_js_input_gamepad_cb(&DisplayServerJavaScript::gamepad_callback);
+
+ // JS Display interface (js/libs/library_godot_display.js)
+ godot_js_display_fullscreen_cb(&DisplayServerJavaScript::fullscreen_change_callback);
+ godot_js_display_window_blur_cb(&window_blur_callback);
godot_js_display_notification_cb(&send_window_event_callback,
WINDOW_EVENT_MOUSE_ENTER,
WINDOW_EVENT_MOUSE_EXIT,
WINDOW_EVENT_FOCUS_IN,
WINDOW_EVENT_FOCUS_OUT);
- godot_js_display_paste_cb(update_clipboard_callback);
- godot_js_display_drop_files_cb(drop_files_js_callback);
- godot_js_display_gamepad_cb(&DisplayServerJavaScript::gamepad_callback);
godot_js_display_vk_cb(&vk_input_text_callback);
Input::get_singleton()->set_event_dispatch_function(_dispatch_input_event);
@@ -1041,7 +953,7 @@ bool DisplayServerJavaScript::can_any_window_draw() const {
void DisplayServerJavaScript::process_events() {
Input::get_singleton()->flush_buffered_events();
- if (godot_js_display_gamepad_sample() == OK) {
+ if (godot_js_input_gamepad_sample() == OK) {
process_joypads();
}
}
diff --git a/platform/javascript/display_server_javascript.h b/platform/javascript/display_server_javascript.h
index bf5e229c9a..1aa600c421 100644
--- a/platform/javascript/display_server_javascript.h
+++ b/platform/javascript/display_server_javascript.h
@@ -38,6 +38,19 @@
class DisplayServerJavaScript : public DisplayServer {
private:
+ struct JSTouchEvent {
+ uint32_t identifier[32] = { 0 };
+ double coords[64] = { 0 };
+ };
+ JSTouchEvent touch_event;
+
+ struct JSKeyEvent {
+ char code[32] = { 0 };
+ char key[32] = { 0 };
+ uint8_t modifiers[4] = { 0 };
+ };
+ JSKeyEvent key_event;
+
WindowMode window_mode = WINDOW_MODE_WINDOWED;
ObjectID window_attached_instance_id = {};
@@ -47,44 +60,31 @@ private:
Callable drop_files_callback;
String clipboard;
- Ref<InputEventKey> deferred_key_event;
Point2 touches[32];
char canvas_id[256] = { 0 };
bool cursor_inside_canvas = true;
CursorShape cursor_shape = CURSOR_ARROW;
Point2i last_click_pos = Point2(-100, -100); // TODO check this again.
- double last_click_ms = 0;
+ uint64_t last_click_ms = 0;
int last_click_button_index = -1;
bool swap_cancel_ok = false;
// utilities
- static Point2 compute_position_in_canvas(int p_x, int p_y);
static void focus_canvas();
static bool is_canvas_focused();
- template <typename T>
- static void dom2godot_mod(T *emscripten_event_ptr, Ref<InputEventWithModifiers> godot_event);
- static Ref<InputEventKey> setup_key_event(const EmscriptenKeyboardEvent *emscripten_event);
+ static void dom2godot_mod(Ref<InputEventWithModifiers> ev, int p_mod);
static const char *godot2dom_cursor(DisplayServer::CursorShape p_shape);
// events
- static EM_BOOL fullscreen_change_callback(int p_event_type, const EmscriptenFullscreenChangeEvent *p_event, void *p_user_data);
-
- static EM_BOOL keydown_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data);
- static EM_BOOL keypress_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data);
- static EM_BOOL keyup_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data);
-
+ static void fullscreen_change_callback(int p_fullscreen);
+ static int mouse_button_callback(int p_pressed, int p_button, double p_x, double p_y, int p_modifiers);
+ static void mouse_move_callback(double p_x, double p_y, double p_rel_x, double p_rel_y, int p_modifiers);
+ static int mouse_wheel_callback(double p_delta_x, double p_delta_y);
+ static void touch_callback(int p_type, int p_count);
+ static void key_callback(int p_pressed, int p_repeat, int p_modifiers);
static void vk_input_text_callback(const char *p_text, int p_cursor);
-
- static EM_BOOL mousemove_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data);
- static EM_BOOL mouse_button_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data);
-
- static EM_BOOL wheel_callback(int p_event_type, const EmscriptenWheelEvent *p_event, void *p_user_data);
-
- static EM_BOOL touch_press_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data);
- static EM_BOOL touchmove_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data);
-
static void gamepad_callback(int p_index, int p_connected, const char *p_id, const char *p_guid);
void process_joypads();
@@ -94,6 +94,7 @@ private:
static void _dispatch_input_event(const Ref<InputEvent> &p_event);
static void request_quit_callback();
+ static void window_blur_callback();
static void update_clipboard_callback(const char *p_text);
static void send_window_event_callback(int p_notification);
static void drop_files_js_callback(char **p_filev, int p_filec);
diff --git a/platform/javascript/export/export_plugin.cpp b/platform/javascript/export/export_plugin.cpp
index 5d285a6e60..c7bd172751 100644
--- a/platform/javascript/export/export_plugin.cpp
+++ b/platform/javascript/export/export_plugin.cpp
@@ -99,8 +99,8 @@ void EditorExportPlatformJavaScript::_replace_strings(Map<String, String> p_repl
Vector<String> lines = str_template.split("\n");
for (int i = 0; i < lines.size(); i++) {
String current_line = lines[i];
- for (Map<String, String>::Element *E = p_replaces.front(); E; E = E->next()) {
- current_line = current_line.replace(E->key(), E->get());
+ for (const KeyValue<String, String> &E : p_replaces) {
+ current_line = current_line.replace(E.key, E.value);
}
out += current_line + "\n";
}
diff --git a/platform/javascript/godot_js.h b/platform/javascript/godot_js.h
index d332af2c31..8051b270e6 100644
--- a/platform/javascript/godot_js.h
+++ b/platform/javascript/godot_js.h
@@ -50,12 +50,28 @@ extern int godot_js_os_execute(const char *p_json);
extern void godot_js_os_shell_open(const char *p_uri);
extern int godot_js_os_hw_concurrency_get();
+// Input
+extern void godot_js_input_mouse_button_cb(int (*p_callback)(int p_pressed, int p_button, double p_x, double p_y, int p_modifiers));
+extern void godot_js_input_mouse_move_cb(void (*p_callback)(double p_x, double p_y, double p_rel_x, double p_rel_y, int p_modifiers));
+extern void godot_js_input_mouse_wheel_cb(int (*p_callback)(double p_delta_x, double p_delta_y));
+extern void godot_js_input_touch_cb(void (*p_callback)(int p_type, int p_count), uint32_t *r_identifiers, double *r_coords);
+extern void godot_js_input_key_cb(void (*p_callback)(int p_type, int p_repeat, int p_modifiers), char r_code[32], char r_key[32]);
+
+// Input gamepad
+extern void godot_js_input_gamepad_cb(void (*p_on_change)(int p_index, int p_connected, const char *p_id, const char *p_guid));
+extern int godot_js_input_gamepad_sample();
+extern int godot_js_input_gamepad_sample_count();
+extern int godot_js_input_gamepad_sample_get(int p_idx, float r_btns[16], int32_t *r_btns_num, float r_axes[10], int32_t *r_axes_num, int32_t *r_standard);
+extern void godot_js_input_paste_cb(void (*p_callback)(const char *p_text));
+extern void godot_js_input_drop_files_cb(void (*p_callback)(char **p_filev, int p_filec));
+
// Display
extern int godot_js_display_screen_dpi_get();
extern double godot_js_display_pixel_ratio_get();
extern void godot_js_display_alert(const char *p_text);
extern int godot_js_display_touchscreen_is_available();
extern int godot_js_display_is_swap_ok_cancel();
+extern void godot_js_display_setup_canvas(int p_width, int p_height, int p_fullscreen, int p_hidpi);
// Display canvas
extern void godot_js_display_canvas_focus();
@@ -68,7 +84,6 @@ extern void godot_js_display_window_size_get(int32_t *p_x, int32_t *p_y);
extern void godot_js_display_screen_size_get(int32_t *p_x, int32_t *p_y);
extern int godot_js_display_fullscreen_request();
extern int godot_js_display_fullscreen_exit();
-extern void godot_js_display_compute_position(int p_x, int p_y, int32_t *r_x, int32_t *r_y);
extern void godot_js_display_window_title_set(const char *p_text);
extern void godot_js_display_window_icon_set(const uint8_t *p_ptr, int p_len);
extern int godot_js_display_has_webgl(int p_version);
@@ -82,18 +97,13 @@ extern void godot_js_display_cursor_set_shape(const char *p_cursor);
extern int godot_js_display_cursor_is_hidden();
extern void godot_js_display_cursor_set_custom_shape(const char *p_shape, const uint8_t *p_ptr, int p_len, int p_hotspot_x, int p_hotspot_y);
extern void godot_js_display_cursor_set_visible(int p_visible);
-
-// Display gamepad
-extern void godot_js_display_gamepad_cb(void (*p_on_change)(int p_index, int p_connected, const char *p_id, const char *p_guid));
-extern int godot_js_display_gamepad_sample();
-extern int godot_js_display_gamepad_sample_count();
-extern int godot_js_display_gamepad_sample_get(int p_idx, float r_btns[16], int32_t *r_btns_num, float r_axes[10], int32_t *r_axes_num, int32_t *r_standard);
+extern void godot_js_display_cursor_lock_set(int p_lock);
+extern int godot_js_display_cursor_is_locked();
// Display listeners
+extern void godot_js_display_fullscreen_cb(void (*p_callback)(int p_fullscreen));
+extern void godot_js_display_window_blur_cb(void (*p_callback)());
extern void godot_js_display_notification_cb(void (*p_callback)(int p_notification), int p_enter, int p_exit, int p_in, int p_out);
-extern void godot_js_display_paste_cb(void (*p_callback)(const char *p_text));
-extern void godot_js_display_drop_files_cb(void (*p_callback)(char **p_filev, int p_filec));
-extern void godot_js_display_setup_canvas(int p_width, int p_height, int p_fullscreen, int p_hidpi);
// Display Virtual Keyboard
extern int godot_js_display_vk_available();
diff --git a/platform/javascript/js/libs/library_godot_audio.js b/platform/javascript/js/libs/library_godot_audio.js
index f6010fd12a..6cbb0567f4 100644
--- a/platform/javascript/js/libs/library_godot_audio.js
+++ b/platform/javascript/js/libs/library_godot_audio.js
@@ -229,7 +229,7 @@ const GodotAudioWorklet = {
'godot-processor',
{
'outputChannelCount': [channels],
- },
+ }
);
return Promise.resolve();
});
diff --git a/platform/javascript/js/libs/library_godot_display.js b/platform/javascript/js/libs/library_godot_display.js
index affae90370..2689f1c22c 100644
--- a/platform/javascript/js/libs/library_godot_display.js
+++ b/platform/javascript/js/libs/library_godot_display.js
@@ -28,224 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-/*
- * Display Server listeners.
- * Keeps track of registered event listeners so it can remove them on shutdown.
- */
-const GodotDisplayListeners = {
- $GodotDisplayListeners__deps: ['$GodotOS'],
- $GodotDisplayListeners__postset: 'GodotOS.atexit(function(resolve, reject) { GodotDisplayListeners.clear(); resolve(); });',
- $GodotDisplayListeners: {
- handlers: [],
-
- has: function (target, event, method, capture) {
- return GodotDisplayListeners.handlers.findIndex(function (e) {
- return e.target === target && e.event === event && e.method === method && e.capture === capture;
- }) !== -1;
- },
-
- add: function (target, event, method, capture) {
- if (GodotDisplayListeners.has(target, event, method, capture)) {
- return;
- }
- function Handler(p_target, p_event, p_method, p_capture) {
- this.target = p_target;
- this.event = p_event;
- this.method = p_method;
- this.capture = p_capture;
- }
- GodotDisplayListeners.handlers.push(new Handler(target, event, method, capture));
- target.addEventListener(event, method, capture);
- },
-
- clear: function () {
- GodotDisplayListeners.handlers.forEach(function (h) {
- h.target.removeEventListener(h.event, h.method, h.capture);
- });
- GodotDisplayListeners.handlers.length = 0;
- },
- },
-};
-mergeInto(LibraryManager.library, GodotDisplayListeners);
-
-/*
- * Drag and drop handler.
- * This is pretty big, but basically detect dropped files on GodotConfig.canvas,
- * process them one by one (recursively for directories), and copies them to
- * the temporary FS path '/tmp/drop-[random]/' so it can be emitted as a godot
- * event (that requires a string array of paths).
- *
- * NOTE: The temporary files are removed after the callback. This means that
- * deferred callbacks won't be able to access the files.
- */
-const GodotDisplayDragDrop = {
- $GodotDisplayDragDrop__deps: ['$FS', '$GodotFS'],
- $GodotDisplayDragDrop: {
- promises: [],
- pending_files: [],
-
- add_entry: function (entry) {
- if (entry.isDirectory) {
- GodotDisplayDragDrop.add_dir(entry);
- } else if (entry.isFile) {
- GodotDisplayDragDrop.add_file(entry);
- } else {
- GodotRuntime.error('Unrecognized entry...', entry);
- }
- },
-
- add_dir: function (entry) {
- GodotDisplayDragDrop.promises.push(new Promise(function (resolve, reject) {
- const reader = entry.createReader();
- reader.readEntries(function (entries) {
- for (let i = 0; i < entries.length; i++) {
- GodotDisplayDragDrop.add_entry(entries[i]);
- }
- resolve();
- });
- }));
- },
-
- add_file: function (entry) {
- GodotDisplayDragDrop.promises.push(new Promise(function (resolve, reject) {
- entry.file(function (file) {
- const reader = new FileReader();
- reader.onload = function () {
- const f = {
- 'path': file.relativePath || file.webkitRelativePath,
- 'name': file.name,
- 'type': file.type,
- 'size': file.size,
- 'data': reader.result,
- };
- if (!f['path']) {
- f['path'] = f['name'];
- }
- GodotDisplayDragDrop.pending_files.push(f);
- resolve();
- };
- reader.onerror = function () {
- GodotRuntime.print('Error reading file');
- reject();
- };
- reader.readAsArrayBuffer(file);
- }, function (err) {
- GodotRuntime.print('Error!');
- reject();
- });
- }));
- },
-
- process: function (resolve, reject) {
- if (GodotDisplayDragDrop.promises.length === 0) {
- resolve();
- return;
- }
- GodotDisplayDragDrop.promises.pop().then(function () {
- setTimeout(function () {
- GodotDisplayDragDrop.process(resolve, reject);
- }, 0);
- });
- },
-
- _process_event: function (ev, callback) {
- ev.preventDefault();
- if (ev.dataTransfer.items) {
- // Use DataTransferItemList interface to access the file(s)
- for (let i = 0; i < ev.dataTransfer.items.length; i++) {
- const item = ev.dataTransfer.items[i];
- let entry = null;
- if ('getAsEntry' in item) {
- entry = item.getAsEntry();
- } else if ('webkitGetAsEntry' in item) {
- entry = item.webkitGetAsEntry();
- }
- if (entry) {
- GodotDisplayDragDrop.add_entry(entry);
- }
- }
- } else {
- GodotRuntime.error('File upload not supported');
- }
- new Promise(GodotDisplayDragDrop.process).then(function () {
- const DROP = `/tmp/drop-${parseInt(Math.random() * (1 << 30), 10)}/`;
- const drops = [];
- const files = [];
- FS.mkdir(DROP);
- GodotDisplayDragDrop.pending_files.forEach((elem) => {
- const path = elem['path'];
- GodotFS.copy_to_fs(DROP + path, elem['data']);
- let idx = path.indexOf('/');
- if (idx === -1) {
- // Root file
- drops.push(DROP + path);
- } else {
- // Subdir
- const sub = path.substr(0, idx);
- idx = sub.indexOf('/');
- if (idx < 0 && drops.indexOf(DROP + sub) === -1) {
- drops.push(DROP + sub);
- }
- }
- files.push(DROP + path);
- });
- GodotDisplayDragDrop.promises = [];
- GodotDisplayDragDrop.pending_files = [];
- callback(drops);
- if (GodotConfig.persistent_drops) {
- // Delay removal at exit.
- GodotOS.atexit(function (resolve, reject) {
- GodotDisplayDragDrop.remove_drop(files, DROP);
- resolve();
- });
- } else {
- GodotDisplayDragDrop.remove_drop(files, DROP);
- }
- });
- },
-
- remove_drop: function (files, drop_path) {
- const dirs = [drop_path.substr(0, drop_path.length - 1)];
- // Remove temporary files
- files.forEach(function (file) {
- FS.unlink(file);
- let dir = file.replace(drop_path, '');
- let idx = dir.lastIndexOf('/');
- while (idx > 0) {
- dir = dir.substr(0, idx);
- if (dirs.indexOf(drop_path + dir) === -1) {
- dirs.push(drop_path + dir);
- }
- idx = dir.lastIndexOf('/');
- }
- });
- // Remove dirs.
- dirs.sort(function (a, b) {
- const al = (a.match(/\//g) || []).length;
- const bl = (b.match(/\//g) || []).length;
- if (al > bl) {
- return -1;
- } else if (al < bl) {
- return 1;
- }
- return 0;
- }).forEach(function (dir) {
- FS.rmdir(dir);
- });
- },
-
- handler: function (callback) {
- return function (ev) {
- GodotDisplayDragDrop._process_event(ev, callback);
- };
- },
- },
-};
-mergeInto(LibraryManager.library, GodotDisplayDragDrop);
-
const GodotDisplayVK = {
- $GodotDisplayVK__deps: ['$GodotRuntime', '$GodotConfig', '$GodotDisplayListeners'],
+ $GodotDisplayVK__deps: ['$GodotRuntime', '$GodotConfig', '$GodotEventListeners'],
$GodotDisplayVK__postset: 'GodotOS.atexit(function(resolve, reject) { GodotDisplayVK.clear(); resolve(); });',
$GodotDisplayVK: {
textinput: null,
@@ -271,12 +56,12 @@ const GodotDisplayVK = {
elem.style.outline = 'none';
elem.readonly = true;
elem.disabled = true;
- GodotDisplayListeners.add(elem, 'input', function (evt) {
+ GodotEventListeners.add(elem, 'input', function (evt) {
const c_str = GodotRuntime.allocString(elem.value);
input_cb(c_str, elem.selectionEnd);
GodotRuntime.free(c_str);
}, false);
- GodotDisplayListeners.add(elem, 'blur', function (evt) {
+ GodotEventListeners.add(elem, 'blur', function (evt) {
elem.style.display = 'none';
elem.readonly = true;
elem.disabled = true;
@@ -376,136 +161,23 @@ const GodotDisplayCursor = {
delete GodotDisplayCursor.cursors[key];
});
},
- },
-};
-mergeInto(LibraryManager.library, GodotDisplayCursor);
-
-/*
- * Display Gamepad API helper.
- */
-const GodotDisplayGamepads = {
- $GodotDisplayGamepads__deps: ['$GodotRuntime', '$GodotDisplayListeners'],
- $GodotDisplayGamepads: {
- samples: [],
-
- get_pads: function () {
- try {
- // Will throw in iframe when permission is denied.
- // Will throw/warn in the future for insecure contexts.
- // See https://github.com/w3c/gamepad/pull/120
- const pads = navigator.getGamepads();
- if (pads) {
- return pads;
- }
- return [];
- } catch (e) {
- return [];
- }
- },
-
- get_samples: function () {
- return GodotDisplayGamepads.samples;
- },
-
- get_sample: function (index) {
- const samples = GodotDisplayGamepads.samples;
- return index < samples.length ? samples[index] : null;
- },
-
- sample: function () {
- const pads = GodotDisplayGamepads.get_pads();
- const samples = [];
- for (let i = 0; i < pads.length; i++) {
- const pad = pads[i];
- if (!pad) {
- samples.push(null);
- continue;
- }
- const s = {
- standard: pad.mapping === 'standard',
- buttons: [],
- axes: [],
- connected: pad.connected,
- };
- for (let b = 0; b < pad.buttons.length; b++) {
- s.buttons.push(pad.buttons[b].value);
- }
- for (let a = 0; a < pad.axes.length; a++) {
- s.axes.push(pad.axes[a]);
- }
- samples.push(s);
+ lockPointer: function () {
+ const canvas = GodotConfig.canvas;
+ if (canvas.requestPointerLock) {
+ canvas.requestPointerLock();
}
- GodotDisplayGamepads.samples = samples;
},
-
- init: function (onchange) {
- GodotDisplayListeners.samples = [];
- function add(pad) {
- const guid = GodotDisplayGamepads.get_guid(pad);
- const c_id = GodotRuntime.allocString(pad.id);
- const c_guid = GodotRuntime.allocString(guid);
- onchange(pad.index, 1, c_id, c_guid);
- GodotRuntime.free(c_id);
- GodotRuntime.free(c_guid);
+ releasePointer: function () {
+ if (document.exitPointerLock) {
+ document.exitPointerLock();
}
- const pads = GodotDisplayGamepads.get_pads();
- for (let i = 0; i < pads.length; i++) {
- // Might be reserved space.
- if (pads[i]) {
- add(pads[i]);
- }
- }
- GodotDisplayListeners.add(window, 'gamepadconnected', function (evt) {
- add(evt.gamepad);
- }, false);
- GodotDisplayListeners.add(window, 'gamepaddisconnected', function (evt) {
- onchange(evt.gamepad.index, 0);
- }, false);
},
-
- get_guid: function (pad) {
- if (pad.mapping) {
- return pad.mapping;
- }
- const ua = navigator.userAgent;
- let os = 'Unknown';
- if (ua.indexOf('Android') >= 0) {
- os = 'Android';
- } else if (ua.indexOf('Linux') >= 0) {
- os = 'Linux';
- } else if (ua.indexOf('iPhone') >= 0) {
- os = 'iOS';
- } else if (ua.indexOf('Macintosh') >= 0) {
- // Updated iPads will fall into this category.
- os = 'MacOSX';
- } else if (ua.indexOf('Windows') >= 0) {
- os = 'Windows';
- }
-
- const id = pad.id;
- // Chrom* style: NAME (Vendor: xxxx Product: xxxx)
- const exp1 = /vendor: ([0-9a-f]{4}) product: ([0-9a-f]{4})/i;
- // Firefox/Safari style (safari may remove leading zeores)
- const exp2 = /^([0-9a-f]+)-([0-9a-f]+)-/i;
- let vendor = '';
- let product = '';
- if (exp1.test(id)) {
- const match = exp1.exec(id);
- vendor = match[1].padStart(4, '0');
- product = match[2].padStart(4, '0');
- } else if (exp2.test(id)) {
- const match = exp2.exec(id);
- vendor = match[1].padStart(4, '0');
- product = match[2].padStart(4, '0');
- }
- if (!vendor || !product) {
- return `${os}Unknown`;
- }
- return os + vendor + product;
+ isPointerLocked: function () {
+ return document.pointerLockElement === GodotConfig.canvas;
},
},
};
-mergeInto(LibraryManager.library, GodotDisplayGamepads);
+mergeInto(LibraryManager.library, GodotDisplayCursor);
const GodotDisplayScreen = {
$GodotDisplayScreen__deps: ['$GodotConfig', '$GodotOS', '$GL', 'emscripten_webgl_get_current_context'],
@@ -622,7 +294,7 @@ mergeInto(LibraryManager.library, GodotDisplayScreen);
* Exposes all the functions needed by DisplayServer implementation.
*/
const GodotDisplay = {
- $GodotDisplay__deps: ['$GodotConfig', '$GodotRuntime', '$GodotDisplayCursor', '$GodotDisplayListeners', '$GodotDisplayDragDrop', '$GodotDisplayGamepads', '$GodotDisplayScreen', '$GodotDisplayVK'],
+ $GodotDisplay__deps: ['$GodotConfig', '$GodotRuntime', '$GodotDisplayCursor', '$GodotEventListeners', '$GodotDisplayScreen', '$GodotDisplayVK'],
$GodotDisplay: {
window_icon: '',
findDPI: function () {
@@ -710,15 +382,6 @@ const GodotDisplay = {
GodotRuntime.setHeapValue(p_height, GodotConfig.canvas.height, 'i32');
},
- godot_js_display_compute_position: function (x, y, r_x, r_y) {
- const canvas = GodotConfig.canvas;
- const rect = canvas.getBoundingClientRect();
- const rw = canvas.width / rect.width;
- const rh = canvas.height / rect.height;
- GodotRuntime.setHeapValue(r_x, (x - rect.x) * rw, 'i32');
- GodotRuntime.setHeapValue(r_y, (y - rect.y) * rh, 'i32');
- },
-
godot_js_display_has_webgl__sig: 'ii',
godot_js_display_has_webgl: function (p_version) {
if (p_version !== 1 && p_version !== 2) {
@@ -859,60 +522,64 @@ const GodotDisplay = {
}
},
+ godot_js_display_cursor_lock_set__sig: 'vi',
+ godot_js_display_cursor_lock_set: function (p_lock) {
+ if (p_lock) {
+ GodotDisplayCursor.lockPointer();
+ } else {
+ GodotDisplayCursor.releasePointer();
+ }
+ },
+
+ godot_js_display_cursor_is_locked__sig: 'i',
+ godot_js_display_cursor_is_locked: function () {
+ return GodotDisplayCursor.isPointerLocked() ? 1 : 0;
+ },
+
/*
* Listeners
*/
- godot_js_display_notification_cb__sig: 'viiiii',
- godot_js_display_notification_cb: function (callback, p_enter, p_exit, p_in, p_out) {
+ godot_js_display_fullscreen_cb__sig: 'vi',
+ godot_js_display_fullscreen_cb: function (callback) {
const canvas = GodotConfig.canvas;
const func = GodotRuntime.get_func(callback);
- const notif = [p_enter, p_exit, p_in, p_out];
- ['mouseover', 'mouseleave', 'focus', 'blur'].forEach(function (evt_name, idx) {
- GodotDisplayListeners.add(canvas, evt_name, function () {
- func(notif[idx]);
- }, true);
- });
+ function change_cb(evt) {
+ if (evt.target === canvas) {
+ func(GodotDisplayScreen.isFullscreen());
+ }
+ }
+ GodotEventListeners.add(document, 'fullscreenchange', change_cb, false);
+ GodotEventListeners.add(document, 'mozfullscreenchange', change_cb, false);
+ GodotEventListeners.add(document, 'webkitfullscreenchange', change_cb, false);
},
- godot_js_display_paste_cb__sig: 'vi',
- godot_js_display_paste_cb: function (callback) {
+ godot_js_display_window_blur_cb__sig: 'vi',
+ godot_js_display_window_blur_cb: function (callback) {
const func = GodotRuntime.get_func(callback);
- GodotDisplayListeners.add(window, 'paste', function (evt) {
- const text = evt.clipboardData.getData('text');
- const ptr = GodotRuntime.allocString(text);
- func(ptr);
- GodotRuntime.free(ptr);
+ GodotEventListeners.add(window, 'blur', function () {
+ func();
}, false);
},
- godot_js_display_drop_files_cb__sig: 'vi',
- godot_js_display_drop_files_cb: function (callback) {
- const func = GodotRuntime.get_func(callback);
- const dropFiles = function (files) {
- const args = files || [];
- if (!args.length) {
- return;
- }
- const argc = args.length;
- const argv = GodotRuntime.allocStringArray(args);
- func(argv, argc);
- GodotRuntime.freeStringArray(argv, argc);
- };
+ godot_js_display_notification_cb__sig: 'viiiii',
+ godot_js_display_notification_cb: function (callback, p_enter, p_exit, p_in, p_out) {
const canvas = GodotConfig.canvas;
- GodotDisplayListeners.add(canvas, 'dragover', function (ev) {
- // Prevent default behavior (which would try to open the file(s))
- ev.preventDefault();
- }, false);
- GodotDisplayListeners.add(canvas, 'drop', GodotDisplayDragDrop.handler(dropFiles));
+ const func = GodotRuntime.get_func(callback);
+ const notif = [p_enter, p_exit, p_in, p_out];
+ ['mouseover', 'mouseleave', 'focus', 'blur'].forEach(function (evt_name, idx) {
+ GodotEventListeners.add(canvas, evt_name, function () {
+ func(notif[idx]);
+ }, true);
+ });
},
godot_js_display_setup_canvas__sig: 'viiii',
godot_js_display_setup_canvas: function (p_width, p_height, p_fullscreen, p_hidpi) {
const canvas = GodotConfig.canvas;
- GodotDisplayListeners.add(canvas, 'contextmenu', function (ev) {
+ GodotEventListeners.add(canvas, 'contextmenu', function (ev) {
ev.preventDefault();
}, false);
- GodotDisplayListeners.add(canvas, 'webglcontextlost', function (ev) {
+ GodotEventListeners.add(canvas, 'webglcontextlost', function (ev) {
alert('WebGL context lost, please reload the page'); // eslint-disable-line no-alert
ev.preventDefault();
}, false);
@@ -965,49 +632,6 @@ const GodotDisplay = {
GodotDisplayVK.init(input_cb);
}
},
-
- /*
- * Gamepads
- */
- godot_js_display_gamepad_cb__sig: 'vi',
- godot_js_display_gamepad_cb: function (change_cb) {
- const onchange = GodotRuntime.get_func(change_cb);
- GodotDisplayGamepads.init(onchange);
- },
-
- godot_js_display_gamepad_sample_count__sig: 'i',
- godot_js_display_gamepad_sample_count: function () {
- return GodotDisplayGamepads.get_samples().length;
- },
-
- godot_js_display_gamepad_sample__sig: 'i',
- godot_js_display_gamepad_sample: function () {
- GodotDisplayGamepads.sample();
- return 0;
- },
-
- godot_js_display_gamepad_sample_get__sig: 'iiiiiii',
- godot_js_display_gamepad_sample_get: function (p_index, r_btns, r_btns_num, r_axes, r_axes_num, r_standard) {
- const sample = GodotDisplayGamepads.get_sample(p_index);
- if (!sample || !sample.connected) {
- return 1;
- }
- const btns = sample.buttons;
- const btns_len = btns.length < 16 ? btns.length : 16;
- for (let i = 0; i < btns_len; i++) {
- GodotRuntime.setHeapValue(r_btns + (i << 2), btns[i], 'float');
- }
- GodotRuntime.setHeapValue(r_btns_num, btns_len, 'i32');
- const axes = sample.axes;
- const axes_len = axes.length < 10 ? axes.length : 10;
- for (let i = 0; i < axes_len; i++) {
- GodotRuntime.setHeapValue(r_axes + (i << 2), axes[i], 'float');
- }
- GodotRuntime.setHeapValue(r_axes_num, axes_len, 'i32');
- const is_standard = sample.standard ? 1 : 0;
- GodotRuntime.setHeapValue(r_standard, is_standard, 'i32');
- return 0;
- },
};
autoAddDeps(GodotDisplay, '$GodotDisplay');
diff --git a/platform/javascript/js/libs/library_godot_input.js b/platform/javascript/js/libs/library_godot_input.js
new file mode 100644
index 0000000000..587c320f35
--- /dev/null
+++ b/platform/javascript/js/libs/library_godot_input.js
@@ -0,0 +1,526 @@
+/*************************************************************************/
+/* library_godot_input.js */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 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. */
+/*************************************************************************/
+
+/*
+ * Gamepad API helper.
+ */
+const GodotInputGamepads = {
+ $GodotInputGamepads__deps: ['$GodotRuntime', '$GodotEventListeners'],
+ $GodotInputGamepads: {
+ samples: [],
+
+ get_pads: function () {
+ try {
+ // Will throw in iframe when permission is denied.
+ // Will throw/warn in the future for insecure contexts.
+ // See https://github.com/w3c/gamepad/pull/120
+ const pads = navigator.getGamepads();
+ if (pads) {
+ return pads;
+ }
+ return [];
+ } catch (e) {
+ return [];
+ }
+ },
+
+ get_samples: function () {
+ return GodotInputGamepads.samples;
+ },
+
+ get_sample: function (index) {
+ const samples = GodotInputGamepads.samples;
+ return index < samples.length ? samples[index] : null;
+ },
+
+ sample: function () {
+ const pads = GodotInputGamepads.get_pads();
+ const samples = [];
+ for (let i = 0; i < pads.length; i++) {
+ const pad = pads[i];
+ if (!pad) {
+ samples.push(null);
+ continue;
+ }
+ const s = {
+ standard: pad.mapping === 'standard',
+ buttons: [],
+ axes: [],
+ connected: pad.connected,
+ };
+ for (let b = 0; b < pad.buttons.length; b++) {
+ s.buttons.push(pad.buttons[b].value);
+ }
+ for (let a = 0; a < pad.axes.length; a++) {
+ s.axes.push(pad.axes[a]);
+ }
+ samples.push(s);
+ }
+ GodotInputGamepads.samples = samples;
+ },
+
+ init: function (onchange) {
+ GodotEventListeners.samples = [];
+ function add(pad) {
+ const guid = GodotInputGamepads.get_guid(pad);
+ const c_id = GodotRuntime.allocString(pad.id);
+ const c_guid = GodotRuntime.allocString(guid);
+ onchange(pad.index, 1, c_id, c_guid);
+ GodotRuntime.free(c_id);
+ GodotRuntime.free(c_guid);
+ }
+ const pads = GodotInputGamepads.get_pads();
+ for (let i = 0; i < pads.length; i++) {
+ // Might be reserved space.
+ if (pads[i]) {
+ add(pads[i]);
+ }
+ }
+ GodotEventListeners.add(window, 'gamepadconnected', function (evt) {
+ add(evt.gamepad);
+ }, false);
+ GodotEventListeners.add(window, 'gamepaddisconnected', function (evt) {
+ onchange(evt.gamepad.index, 0);
+ }, false);
+ },
+
+ get_guid: function (pad) {
+ if (pad.mapping) {
+ return pad.mapping;
+ }
+ const ua = navigator.userAgent;
+ let os = 'Unknown';
+ if (ua.indexOf('Android') >= 0) {
+ os = 'Android';
+ } else if (ua.indexOf('Linux') >= 0) {
+ os = 'Linux';
+ } else if (ua.indexOf('iPhone') >= 0) {
+ os = 'iOS';
+ } else if (ua.indexOf('Macintosh') >= 0) {
+ // Updated iPads will fall into this category.
+ os = 'MacOSX';
+ } else if (ua.indexOf('Windows') >= 0) {
+ os = 'Windows';
+ }
+
+ const id = pad.id;
+ // Chrom* style: NAME (Vendor: xxxx Product: xxxx)
+ const exp1 = /vendor: ([0-9a-f]{4}) product: ([0-9a-f]{4})/i;
+ // Firefox/Safari style (safari may remove leading zeores)
+ const exp2 = /^([0-9a-f]+)-([0-9a-f]+)-/i;
+ let vendor = '';
+ let product = '';
+ if (exp1.test(id)) {
+ const match = exp1.exec(id);
+ vendor = match[1].padStart(4, '0');
+ product = match[2].padStart(4, '0');
+ } else if (exp2.test(id)) {
+ const match = exp2.exec(id);
+ vendor = match[1].padStart(4, '0');
+ product = match[2].padStart(4, '0');
+ }
+ if (!vendor || !product) {
+ return `${os}Unknown`;
+ }
+ return os + vendor + product;
+ },
+ },
+};
+mergeInto(LibraryManager.library, GodotInputGamepads);
+
+/*
+ * Drag and drop helper.
+ * This is pretty big, but basically detect dropped files on GodotConfig.canvas,
+ * process them one by one (recursively for directories), and copies them to
+ * the temporary FS path '/tmp/drop-[random]/' so it can be emitted as a godot
+ * event (that requires a string array of paths).
+ *
+ * NOTE: The temporary files are removed after the callback. This means that
+ * deferred callbacks won't be able to access the files.
+ */
+const GodotInputDragDrop = {
+ $GodotInputDragDrop__deps: ['$FS', '$GodotFS'],
+ $GodotInputDragDrop: {
+ promises: [],
+ pending_files: [],
+
+ add_entry: function (entry) {
+ if (entry.isDirectory) {
+ GodotInputDragDrop.add_dir(entry);
+ } else if (entry.isFile) {
+ GodotInputDragDrop.add_file(entry);
+ } else {
+ GodotRuntime.error('Unrecognized entry...', entry);
+ }
+ },
+
+ add_dir: function (entry) {
+ GodotInputDragDrop.promises.push(new Promise(function (resolve, reject) {
+ const reader = entry.createReader();
+ reader.readEntries(function (entries) {
+ for (let i = 0; i < entries.length; i++) {
+ GodotInputDragDrop.add_entry(entries[i]);
+ }
+ resolve();
+ });
+ }));
+ },
+
+ add_file: function (entry) {
+ GodotInputDragDrop.promises.push(new Promise(function (resolve, reject) {
+ entry.file(function (file) {
+ const reader = new FileReader();
+ reader.onload = function () {
+ const f = {
+ 'path': file.relativePath || file.webkitRelativePath,
+ 'name': file.name,
+ 'type': file.type,
+ 'size': file.size,
+ 'data': reader.result,
+ };
+ if (!f['path']) {
+ f['path'] = f['name'];
+ }
+ GodotInputDragDrop.pending_files.push(f);
+ resolve();
+ };
+ reader.onerror = function () {
+ GodotRuntime.print('Error reading file');
+ reject();
+ };
+ reader.readAsArrayBuffer(file);
+ }, function (err) {
+ GodotRuntime.print('Error!');
+ reject();
+ });
+ }));
+ },
+
+ process: function (resolve, reject) {
+ if (GodotInputDragDrop.promises.length === 0) {
+ resolve();
+ return;
+ }
+ GodotInputDragDrop.promises.pop().then(function () {
+ setTimeout(function () {
+ GodotInputDragDrop.process(resolve, reject);
+ }, 0);
+ });
+ },
+
+ _process_event: function (ev, callback) {
+ ev.preventDefault();
+ if (ev.dataTransfer.items) {
+ // Use DataTransferItemList interface to access the file(s)
+ for (let i = 0; i < ev.dataTransfer.items.length; i++) {
+ const item = ev.dataTransfer.items[i];
+ let entry = null;
+ if ('getAsEntry' in item) {
+ entry = item.getAsEntry();
+ } else if ('webkitGetAsEntry' in item) {
+ entry = item.webkitGetAsEntry();
+ }
+ if (entry) {
+ GodotInputDragDrop.add_entry(entry);
+ }
+ }
+ } else {
+ GodotRuntime.error('File upload not supported');
+ }
+ new Promise(GodotInputDragDrop.process).then(function () {
+ const DROP = `/tmp/drop-${parseInt(Math.random() * (1 << 30), 10)}/`;
+ const drops = [];
+ const files = [];
+ FS.mkdir(DROP);
+ GodotInputDragDrop.pending_files.forEach((elem) => {
+ const path = elem['path'];
+ GodotFS.copy_to_fs(DROP + path, elem['data']);
+ let idx = path.indexOf('/');
+ if (idx === -1) {
+ // Root file
+ drops.push(DROP + path);
+ } else {
+ // Subdir
+ const sub = path.substr(0, idx);
+ idx = sub.indexOf('/');
+ if (idx < 0 && drops.indexOf(DROP + sub) === -1) {
+ drops.push(DROP + sub);
+ }
+ }
+ files.push(DROP + path);
+ });
+ GodotInputDragDrop.promises = [];
+ GodotInputDragDrop.pending_files = [];
+ callback(drops);
+ if (GodotConfig.persistent_drops) {
+ // Delay removal at exit.
+ GodotOS.atexit(function (resolve, reject) {
+ GodotInputDragDrop.remove_drop(files, DROP);
+ resolve();
+ });
+ } else {
+ GodotInputDragDrop.remove_drop(files, DROP);
+ }
+ });
+ },
+
+ remove_drop: function (files, drop_path) {
+ const dirs = [drop_path.substr(0, drop_path.length - 1)];
+ // Remove temporary files
+ files.forEach(function (file) {
+ FS.unlink(file);
+ let dir = file.replace(drop_path, '');
+ let idx = dir.lastIndexOf('/');
+ while (idx > 0) {
+ dir = dir.substr(0, idx);
+ if (dirs.indexOf(drop_path + dir) === -1) {
+ dirs.push(drop_path + dir);
+ }
+ idx = dir.lastIndexOf('/');
+ }
+ });
+ // Remove dirs.
+ dirs.sort(function (a, b) {
+ const al = (a.match(/\//g) || []).length;
+ const bl = (b.match(/\//g) || []).length;
+ if (al > bl) {
+ return -1;
+ } else if (al < bl) {
+ return 1;
+ }
+ return 0;
+ }).forEach(function (dir) {
+ FS.rmdir(dir);
+ });
+ },
+
+ handler: function (callback) {
+ return function (ev) {
+ GodotInputDragDrop._process_event(ev, callback);
+ };
+ },
+ },
+};
+mergeInto(LibraryManager.library, GodotInputDragDrop);
+
+/*
+ * Godot exposed input functions.
+ */
+const GodotInput = {
+ $GodotInput__deps: ['$GodotRuntime', '$GodotConfig', '$GodotEventListeners', '$GodotInputGamepads', '$GodotInputDragDrop'],
+ $GodotInput: {
+ getModifiers: function (evt) {
+ return (evt.shiftKey + 0) + ((evt.altKey + 0) << 1) + ((evt.ctrlKey + 0) << 2) + ((evt.metaKey + 0) << 3);
+ },
+ computePosition: function (evt, rect) {
+ const canvas = GodotConfig.canvas;
+ const rw = canvas.width / rect.width;
+ const rh = canvas.height / rect.height;
+ const x = (evt.clientX - rect.x) * rw;
+ const y = (evt.clientY - rect.y) * rh;
+ return [x, y];
+ },
+ },
+
+ /*
+ * Mouse API
+ */
+ godot_js_input_mouse_move_cb__sig: 'vi',
+ godot_js_input_mouse_move_cb: function (callback) {
+ const func = GodotRuntime.get_func(callback);
+ const canvas = GodotConfig.canvas;
+ function move_cb(evt) {
+ const rect = canvas.getBoundingClientRect();
+ const pos = GodotInput.computePosition(evt, rect);
+ // Scale movement
+ const rw = canvas.width / rect.width;
+ const rh = canvas.height / rect.height;
+ const rel_pos_x = evt.movementX * rw;
+ const rel_pos_y = evt.movementY * rh;
+ const modifiers = GodotInput.getModifiers(evt);
+ func(pos[0], pos[1], rel_pos_x, rel_pos_y, modifiers);
+ }
+ GodotEventListeners.add(window, 'mousemove', move_cb, false);
+ },
+
+ godot_js_input_mouse_wheel_cb__sig: 'vi',
+ godot_js_input_mouse_wheel_cb: function (callback) {
+ const func = GodotRuntime.get_func(callback);
+ function wheel_cb(evt) {
+ if (func(evt['deltaX'] || 0, evt['deltaY'] || 0)) {
+ evt.preventDefault();
+ }
+ }
+ GodotEventListeners.add(GodotConfig.canvas, 'wheel', wheel_cb, false);
+ },
+
+ godot_js_input_mouse_button_cb__sig: 'vi',
+ godot_js_input_mouse_button_cb: function (callback) {
+ const func = GodotRuntime.get_func(callback);
+ const canvas = GodotConfig.canvas;
+ function button_cb(p_pressed, evt) {
+ const rect = canvas.getBoundingClientRect();
+ const pos = GodotInput.computePosition(evt, rect);
+ const modifiers = GodotInput.getModifiers(evt);
+ if (func(p_pressed, evt.button, pos[0], pos[1], modifiers)) {
+ evt.preventDefault();
+ }
+ }
+ GodotEventListeners.add(canvas, 'mousedown', button_cb.bind(null, 1), false);
+ GodotEventListeners.add(window, 'mouseup', button_cb.bind(null, 0), false);
+ },
+
+ /*
+ * Touch API
+ */
+ godot_js_input_touch_cb__sig: 'viii',
+ godot_js_input_touch_cb: function (callback, ids, coords) {
+ const func = GodotRuntime.get_func(callback);
+ const canvas = GodotConfig.canvas;
+ function touch_cb(type, evt) {
+ const rect = canvas.getBoundingClientRect();
+ const touches = evt.changedTouches;
+ for (let i = 0; i < touches.length; i++) {
+ const touch = touches[i];
+ const pos = GodotInput.computePosition(touch, rect);
+ GodotRuntime.setHeapValue(coords + (i * 2), pos[0], 'double');
+ GodotRuntime.setHeapValue(coords + (i * 2 + 8), pos[1], 'double');
+ GodotRuntime.setHeapValue(ids + i, touch.identifier, 'i32');
+ }
+ func(type, touches.length);
+ if (evt.cancelable) {
+ evt.preventDefault();
+ }
+ }
+ GodotEventListeners.add(canvas, 'touchstart', touch_cb.bind(null, 0), false);
+ GodotEventListeners.add(canvas, 'touchend', touch_cb.bind(null, 1), false);
+ GodotEventListeners.add(canvas, 'touchcancel', touch_cb.bind(null, 1), false);
+ GodotEventListeners.add(canvas, 'touchmove', touch_cb.bind(null, 2), false);
+ },
+
+ /*
+ * Key API
+ */
+ godot_js_input_key_cb__sig: 'viii',
+ godot_js_input_key_cb: function (callback, code, key) {
+ const func = GodotRuntime.get_func(callback);
+ function key_cb(pressed, evt) {
+ const modifiers = GodotInput.getModifiers(evt);
+ GodotRuntime.stringToHeap(evt.code, code, 32);
+ GodotRuntime.stringToHeap(evt.key, key, 32);
+ func(pressed, evt.repeat, modifiers);
+ evt.preventDefault();
+ }
+ GodotEventListeners.add(GodotConfig.canvas, 'keydown', key_cb.bind(null, 1), false);
+ GodotEventListeners.add(GodotConfig.canvas, 'keyup', key_cb.bind(null, 0), false);
+ },
+
+ /*
+ * Gamepad API
+ */
+ godot_js_input_gamepad_cb__sig: 'vi',
+ godot_js_input_gamepad_cb: function (change_cb) {
+ const onchange = GodotRuntime.get_func(change_cb);
+ GodotInputGamepads.init(onchange);
+ },
+
+ godot_js_input_gamepad_sample_count__sig: 'i',
+ godot_js_input_gamepad_sample_count: function () {
+ return GodotInputGamepads.get_samples().length;
+ },
+
+ godot_js_input_gamepad_sample__sig: 'i',
+ godot_js_input_gamepad_sample: function () {
+ GodotInputGamepads.sample();
+ return 0;
+ },
+
+ godot_js_input_gamepad_sample_get__sig: 'iiiiiii',
+ godot_js_input_gamepad_sample_get: function (p_index, r_btns, r_btns_num, r_axes, r_axes_num, r_standard) {
+ const sample = GodotInputGamepads.get_sample(p_index);
+ if (!sample || !sample.connected) {
+ return 1;
+ }
+ const btns = sample.buttons;
+ const btns_len = btns.length < 16 ? btns.length : 16;
+ for (let i = 0; i < btns_len; i++) {
+ GodotRuntime.setHeapValue(r_btns + (i << 2), btns[i], 'float');
+ }
+ GodotRuntime.setHeapValue(r_btns_num, btns_len, 'i32');
+ const axes = sample.axes;
+ const axes_len = axes.length < 10 ? axes.length : 10;
+ for (let i = 0; i < axes_len; i++) {
+ GodotRuntime.setHeapValue(r_axes + (i << 2), axes[i], 'float');
+ }
+ GodotRuntime.setHeapValue(r_axes_num, axes_len, 'i32');
+ const is_standard = sample.standard ? 1 : 0;
+ GodotRuntime.setHeapValue(r_standard, is_standard, 'i32');
+ return 0;
+ },
+
+ /*
+ * Drag/Drop API
+ */
+ godot_js_input_drop_files_cb__sig: 'vi',
+ godot_js_input_drop_files_cb: function (callback) {
+ const func = GodotRuntime.get_func(callback);
+ const dropFiles = function (files) {
+ const args = files || [];
+ if (!args.length) {
+ return;
+ }
+ const argc = args.length;
+ const argv = GodotRuntime.allocStringArray(args);
+ func(argv, argc);
+ GodotRuntime.freeStringArray(argv, argc);
+ };
+ const canvas = GodotConfig.canvas;
+ GodotEventListeners.add(canvas, 'dragover', function (ev) {
+ // Prevent default behavior (which would try to open the file(s))
+ ev.preventDefault();
+ }, false);
+ GodotEventListeners.add(canvas, 'drop', GodotInputDragDrop.handler(dropFiles));
+ },
+
+ /* Paste API */
+ godot_js_input_paste_cb__sig: 'vi',
+ godot_js_input_paste_cb: function (callback) {
+ const func = GodotRuntime.get_func(callback);
+ GodotEventListeners.add(window, 'paste', function (evt) {
+ const text = evt.clipboardData.getData('text');
+ const ptr = GodotRuntime.allocString(text);
+ func(ptr);
+ GodotRuntime.free(ptr);
+ }, false);
+ },
+};
+
+autoAddDeps(GodotInput, '$GodotInput');
+mergeInto(LibraryManager.library, GodotInput);
diff --git a/platform/javascript/js/libs/library_godot_os.js b/platform/javascript/js/libs/library_godot_os.js
index 99e7ee8b5f..c552e99415 100644
--- a/platform/javascript/js/libs/library_godot_os.js
+++ b/platform/javascript/js/libs/library_godot_os.js
@@ -328,3 +328,43 @@ const GodotOS = {
autoAddDeps(GodotOS, '$GodotOS');
mergeInto(LibraryManager.library, GodotOS);
+
+/*
+ * Godot event listeners.
+ * Keeps track of registered event listeners so it can remove them on shutdown.
+ */
+const GodotEventListeners = {
+ $GodotEventListeners__deps: ['$GodotOS'],
+ $GodotEventListeners__postset: 'GodotOS.atexit(function(resolve, reject) { GodotEventListeners.clear(); resolve(); });',
+ $GodotEventListeners: {
+ handlers: [],
+
+ has: function (target, event, method, capture) {
+ return GodotEventListeners.handlers.findIndex(function (e) {
+ return e.target === target && e.event === event && e.method === method && e.capture === capture;
+ }) !== -1;
+ },
+
+ add: function (target, event, method, capture) {
+ if (GodotEventListeners.has(target, event, method, capture)) {
+ return;
+ }
+ function Handler(p_target, p_event, p_method, p_capture) {
+ this.target = p_target;
+ this.event = p_event;
+ this.method = p_method;
+ this.capture = p_capture;
+ }
+ GodotEventListeners.handlers.push(new Handler(target, event, method, capture));
+ target.addEventListener(event, method, capture);
+ },
+
+ clear: function () {
+ GodotEventListeners.handlers.forEach(function (h) {
+ h.target.removeEventListener(h.event, h.method, h.capture);
+ });
+ GodotEventListeners.handlers.length = 0;
+ },
+ },
+};
+mergeInto(LibraryManager.library, GodotEventListeners);
diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py
index 8eb22c1c72..afb7c7b2ab 100644
--- a/platform/linuxbsd/detect.py
+++ b/platform/linuxbsd/detect.py
@@ -113,6 +113,7 @@ def configure(env):
elif env["target"] == "debug":
env.Prepend(CCFLAGS=["-g3"])
env.Prepend(CPPDEFINES=["DEBUG_ENABLED"])
+ env.Prepend(CPPDEFINES=["DEV_ENABLED"])
env.Append(LINKFLAGS=["-rdynamic"])
## Architecture
diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp
index 5c6824cf44..f2cd336b39 100644
--- a/platform/linuxbsd/display_server_x11.cpp
+++ b/platform/linuxbsd/display_server_x11.cpp
@@ -306,11 +306,11 @@ void DisplayServerX11::mouse_set_mode(MouseMode p_mode) {
// The only modes that show a cursor are VISIBLE and CONFINED
bool showCursor = (p_mode == MOUSE_MODE_VISIBLE || p_mode == MOUSE_MODE_CONFINED);
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ for (const KeyValue<WindowID, WindowData> &E : windows) {
if (showCursor) {
- XDefineCursor(x11_display, E->get().x11_window, cursors[current_cursor]); // show cursor
+ XDefineCursor(x11_display, E.value.x11_window, cursors[current_cursor]); // show cursor
} else {
- XDefineCursor(x11_display, E->get().x11_window, null_cursor); // hide cursor
+ XDefineCursor(x11_display, E.value.x11_window, null_cursor); // hide cursor
}
}
mouse_mode = p_mode;
@@ -785,8 +785,8 @@ Vector<DisplayServer::WindowID> DisplayServerX11::get_window_list() const {
_THREAD_SAFE_METHOD_
Vector<int> ret;
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
- ret.push_back(E->key());
+ for (const KeyValue<WindowID, WindowData> &E : windows) {
+ ret.push_back(E.key);
}
return ret;
}
@@ -864,8 +864,8 @@ DisplayServerX11::WindowID DisplayServerX11::get_window_at_screen_position(const
WindowID found_window = INVALID_WINDOW_ID;
WindowID parent_window = INVALID_WINDOW_ID;
unsigned int focus_order = 0;
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
- const WindowData &wd = E->get();
+ for (const KeyValue<WindowID, WindowData> &E : windows) {
+ const WindowData &wd = E.value;
// Discard windows with no focus.
if (wd.focus_order == 0) {
@@ -873,7 +873,7 @@ DisplayServerX11::WindowID DisplayServerX11::get_window_at_screen_position(const
}
// Find topmost window which contains the given position.
- WindowID window_id = E->key();
+ WindowID window_id = E.key;
Rect2i win_rect = Rect2i(window_get_position(window_id), window_get_size(window_id));
if (win_rect.has_point(p_position)) {
// For siblings, pick the window which was focused last.
@@ -1768,8 +1768,8 @@ bool DisplayServerX11::window_can_draw(WindowID p_window) const {
bool DisplayServerX11::can_any_window_draw() const {
_THREAD_SAFE_METHOD_
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
- if (window_get_mode(E->key()) != WINDOW_MODE_MINIMIZED) {
+ for (const KeyValue<WindowID, WindowData> &E : windows) {
+ if (window_get_mode(E.key) != WINDOW_MODE_MINIMIZED) {
return true;
}
}
@@ -1841,12 +1841,12 @@ void DisplayServerX11::cursor_set_shape(CursorShape p_shape) {
if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {
if (cursors[p_shape] != None) {
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
- XDefineCursor(x11_display, E->get().x11_window, cursors[p_shape]);
+ for (const KeyValue<WindowID, WindowData> &E : windows) {
+ XDefineCursor(x11_display, E.value.x11_window, cursors[p_shape]);
}
} else if (cursors[CURSOR_ARROW] != None) {
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
- XDefineCursor(x11_display, E->get().x11_window, cursors[CURSOR_ARROW]);
+ for (const KeyValue<WindowID, WindowData> &E : windows) {
+ XDefineCursor(x11_display, E.value.x11_window, cursors[CURSOR_ARROW]);
}
}
}
@@ -1944,8 +1944,8 @@ void DisplayServerX11::cursor_set_custom_image(const RES &p_cursor, CursorShape
if (p_shape == current_cursor) {
if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
- XDefineCursor(x11_display, E->get().x11_window, cursors[p_shape]);
+ for (const KeyValue<WindowID, WindowData> &E : windows) {
+ XDefineCursor(x11_display, E.value.x11_window, cursors[p_shape]);
}
}
}
@@ -2068,6 +2068,24 @@ String DisplayServerX11::keyboard_get_layout_name(int p_index) const {
return ret;
}
+Key DisplayServerX11::keyboard_get_keycode_from_physical(Key p_keycode) const {
+ unsigned int modifiers = p_keycode & KEY_MODIFIER_MASK;
+ unsigned int keycode_no_mod = p_keycode & KEY_CODE_MASK;
+ unsigned int xkeycode = KeyMappingX11::get_xlibcode((Key)keycode_no_mod);
+ KeySym xkeysym = XkbKeycodeToKeysym(x11_display, xkeycode, 0, 0);
+ if (xkeysym >= 'a' && xkeysym <= 'z') {
+ xkeysym -= ('a' - 'A');
+ }
+
+ Key key = KeyMappingX11::get_keycode(xkeysym);
+ // If not found, fallback to QWERTY.
+ // This should match the behavior of the event pump
+ if (key == KEY_NONE) {
+ return p_keycode;
+ }
+ return (Key)(key | modifiers);
+}
+
DisplayServerX11::Property DisplayServerX11::_read_property(Display *p_display, Window p_window, Atom p_property) {
Atom actual_type = None;
int actual_format = 0;
@@ -2517,8 +2535,8 @@ void DisplayServerX11::_xim_destroy_callback(::XIM im, ::XPointer client_data,
DisplayServerX11 *ds = reinterpret_cast<DisplayServerX11 *>(client_data);
ds->xim = nullptr;
- for (Map<WindowID, WindowData>::Element *E = ds->windows.front(); E; E = E->next()) {
- E->get().xic = nullptr;
+ for (KeyValue<WindowID, WindowData> &E : ds->windows) {
+ E.value.xic = nullptr;
}
}
@@ -2526,9 +2544,9 @@ void DisplayServerX11::_window_changed(XEvent *event) {
WindowID window_id = MAIN_WINDOW_ID;
// Assign the event to the relevant window
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
- if (event->xany.window == E->get().x11_window) {
- window_id = E->key();
+ for (const KeyValue<WindowID, WindowData> &E : windows) {
+ if (event->xany.window == E.value.x11_window) {
+ window_id = E.key;
break;
}
}
@@ -2602,8 +2620,8 @@ void DisplayServerX11::_dispatch_input_event(const Ref<InputEvent> &p_event) {
callable.call((const Variant **)&evp, 1, ret, ce);
} else {
//send to all windows
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
- Callable callable = E->get().input_event_callback;
+ for (KeyValue<WindowID, WindowData> &E : windows) {
+ Callable callable = E.value.input_event_callback;
if (callable.is_null()) {
continue;
}
@@ -2703,8 +2721,8 @@ void DisplayServerX11::process_events() {
if (app_focused) {
//verify that one of the windows has focus, else send focus out notification
bool focus_found = false;
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
- if (E->get().focused) {
+ for (const KeyValue<WindowID, WindowData> &E : windows) {
+ if (E.value.focused) {
focus_found = true;
break;
}
@@ -2749,9 +2767,9 @@ void DisplayServerX11::process_events() {
WindowID window_id = MAIN_WINDOW_ID;
// Assign the event to the relevant window
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
- if (event.xany.window == E->get().x11_window) {
- window_id = E->key();
+ for (const KeyValue<WindowID, WindowData> &E : windows) {
+ if (event.xany.window == E.value.x11_window) {
+ window_id = E.key;
break;
}
}
@@ -2990,17 +3008,17 @@ void DisplayServerX11::process_events() {
if (mouse_mode_grab) {
// Show and update the cursor if confined and the window regained focus.
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ for (const KeyValue<WindowID, WindowData> &E : windows) {
if (mouse_mode == MOUSE_MODE_CONFINED) {
- XUndefineCursor(x11_display, E->get().x11_window);
+ XUndefineCursor(x11_display, E.value.x11_window);
} else if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) { // Or re-hide it.
- XDefineCursor(x11_display, E->get().x11_window, null_cursor);
+ XDefineCursor(x11_display, E.value.x11_window, null_cursor);
}
XGrabPointer(
- x11_display, E->get().x11_window, True,
+ x11_display, E.value.x11_window, True,
ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
- GrabModeAsync, GrabModeAsync, E->get().x11_window, None, CurrentTime);
+ GrabModeAsync, GrabModeAsync, E.value.x11_window, None, CurrentTime);
}
}
#ifdef TOUCH_ENABLED
@@ -3036,11 +3054,11 @@ void DisplayServerX11::process_events() {
_send_window_event(wd, WINDOW_EVENT_FOCUS_OUT);
if (mouse_mode_grab) {
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ for (const KeyValue<WindowID, WindowData> &E : windows) {
//dear X11, I try, I really try, but you never work, you do whathever you want.
if (mouse_mode == MOUSE_MODE_CAPTURED) {
// Show the cursor if we're in captured mode so it doesn't look weird.
- XUndefineCursor(x11_display, E->get().x11_window);
+ XUndefineCursor(x11_display, E.value.x11_window);
}
}
XUngrabPointer(x11_display, CurrentTime);
@@ -3052,12 +3070,12 @@ void DisplayServerX11::process_events() {
}*/
// Release every pointer to avoid sticky points
- for (Map<int, Vector2>::Element *E = xi.state.front(); E; E = E->next()) {
+ for (const KeyValue<int, Vector2> &E : xi.state) {
Ref<InputEventScreenTouch> st;
st.instantiate();
- st->set_index(E->key());
+ st->set_index(E.key);
st->set_window_id(window_id);
- st->set_position(E->get());
+ st->set_position(E.value);
Input::get_singleton()->parse_input_event(st);
}
xi.state.clear();
@@ -3144,9 +3162,9 @@ void DisplayServerX11::process_events() {
// Note: This is needed for drag & drop to work between windows,
// because the engine expects events to keep being processed
// on the same window dragging started.
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
- const WindowData &wd_other = E->get();
- WindowID window_id_other = E->key();
+ for (const KeyValue<WindowID, WindowData> &E : windows) {
+ const WindowData &wd_other = E.value;
+ WindowID window_id_other = E.key;
if (wd_other.focused) {
if (window_id_other != window_id) {
int x, y;
@@ -3287,8 +3305,8 @@ void DisplayServerX11::process_events() {
// Note: This is needed for drag & drop to work between windows,
// because the engine expects events to keep being processed
// on the same window dragging started.
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
- const WindowData &wd_other = E->get();
+ for (const KeyValue<WindowID, WindowData> &E : windows) {
+ const WindowData &wd_other = E.value;
if (wd_other.focused) {
int x, y;
Window child;
@@ -3296,7 +3314,7 @@ void DisplayServerX11::process_events() {
Point2i pos_focused(x, y);
- mm->set_window_id(E->key());
+ mm->set_window_id(E.key);
mm->set_position(pos_focused);
mm->set_global_position(pos_focused);
mm->set_speed(Input::get_singleton()->get_last_mouse_speed());
@@ -3487,8 +3505,8 @@ void DisplayServerX11::set_context(Context p_context) {
context = p_context;
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
- _update_context(E->get());
+ for (KeyValue<WindowID, WindowData> &E : windows) {
+ _update_context(E.value);
}
}
@@ -4268,14 +4286,14 @@ DisplayServerX11::~DisplayServerX11() {
events_thread.wait_to_finish();
//destroy all windows
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ for (KeyValue<WindowID, WindowData> &E : windows) {
#ifdef VULKAN_ENABLED
if (rendering_driver == "vulkan") {
- context_vulkan->window_destroy(E->key());
+ context_vulkan->window_destroy(E.key);
}
#endif
- WindowData &wd = E->get();
+ WindowData &wd = E.value;
if (wd.xic) {
XDestroyIC(wd.xic);
wd.xic = nullptr;
diff --git a/platform/linuxbsd/display_server_x11.h b/platform/linuxbsd/display_server_x11.h
index 052c6d6b7b..1887c7105b 100644
--- a/platform/linuxbsd/display_server_x11.h
+++ b/platform/linuxbsd/display_server_x11.h
@@ -372,6 +372,7 @@ public:
virtual void keyboard_set_current_layout(int p_index) override;
virtual String keyboard_get_layout_language(int p_index) const override;
virtual String keyboard_get_layout_name(int p_index) const override;
+ virtual Key keyboard_get_keycode_from_physical(Key p_keycode) const override;
virtual void process_events() override;
diff --git a/platform/linuxbsd/freedesktop_screensaver.cpp b/platform/linuxbsd/freedesktop_screensaver.cpp
index a6a3b27d76..3973d43d49 100644
--- a/platform/linuxbsd/freedesktop_screensaver.cpp
+++ b/platform/linuxbsd/freedesktop_screensaver.cpp
@@ -50,6 +50,7 @@ void FreeDesktopScreenSaver::inhibit() {
DBusConnection *bus = dbus_bus_get(DBUS_BUS_SESSION, &error);
if (dbus_error_is_set(&error)) {
+ dbus_error_free(&error);
unsupported = true;
return;
}
@@ -72,6 +73,7 @@ void FreeDesktopScreenSaver::inhibit() {
DBusMessage *reply = dbus_connection_send_with_reply_and_block(bus, message, 50, &error);
dbus_message_unref(message);
if (dbus_error_is_set(&error)) {
+ dbus_error_free(&error);
dbus_connection_unref(bus);
unsupported = false;
return;
@@ -96,6 +98,7 @@ void FreeDesktopScreenSaver::uninhibit() {
DBusConnection *bus = dbus_bus_get(DBUS_BUS_SESSION, &error);
if (dbus_error_is_set(&error)) {
+ dbus_error_free(&error);
unsupported = true;
return;
}
@@ -110,6 +113,7 @@ void FreeDesktopScreenSaver::uninhibit() {
DBusMessage *reply = dbus_connection_send_with_reply_and_block(bus, message, 50, &error);
if (dbus_error_is_set(&error)) {
+ dbus_error_free(&error);
dbus_connection_unref(bus);
unsupported = true;
return;
diff --git a/platform/linuxbsd/key_mapping_x11.cpp b/platform/linuxbsd/key_mapping_x11.cpp
index a1ef28234d..829feda40a 100644
--- a/platform/linuxbsd/key_mapping_x11.cpp
+++ b/platform/linuxbsd/key_mapping_x11.cpp
@@ -309,6 +309,18 @@ unsigned int KeyMappingX11::get_scancode(unsigned int p_code) {
return keycode;
}
+unsigned int KeyMappingX11::get_xlibcode(unsigned int p_keysym) {
+ unsigned int code = 0;
+ for (int i = 0; _scancode_to_keycode[i].keysym != KEY_UNKNOWN; i++) {
+ if (_scancode_to_keycode[i].keysym == p_keysym) {
+ code = _scancode_to_keycode[i].keycode;
+ break;
+ }
+ }
+
+ return code;
+}
+
Key KeyMappingX11::get_keycode(KeySym p_keysym) {
// kinda bruteforce.. could optimize.
diff --git a/platform/linuxbsd/key_mapping_x11.h b/platform/linuxbsd/key_mapping_x11.h
index 598db1c45a..d4f1554671 100644
--- a/platform/linuxbsd/key_mapping_x11.h
+++ b/platform/linuxbsd/key_mapping_x11.h
@@ -45,6 +45,7 @@ class KeyMappingX11 {
public:
static Key get_keycode(KeySym p_keysym);
+ static unsigned int get_xlibcode(unsigned int p_keysym);
static unsigned int get_scancode(unsigned int p_code);
static KeySym get_keysym(Key p_code);
static unsigned int get_unicode_from_keysym(KeySym p_keysym);
diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp
index 2c9801f512..69474c6dec 100644
--- a/platform/linuxbsd/os_linuxbsd.cpp
+++ b/platform/linuxbsd/os_linuxbsd.cpp
@@ -463,7 +463,10 @@ Error OS_LinuxBSD::move_to_trash(const String &p_path) {
// The trash can is successfully created, now we check that we don't exceed our file name length limit.
// If the file name is too long trim it so we can add the identifying number and ".trashinfo".
// Assumes that the file name length limit is 255 characters.
- String file_name = basename(p_path.utf8().get_data());
+ String file_name = p_path.get_file();
+ if (file_name.length() == 0) {
+ file_name = p_path.get_base_dir().get_file();
+ }
if (file_name.length() > 240) {
file_name = file_name.substr(0, file_name.length() - 15);
}
diff --git a/platform/osx/detect.py b/platform/osx/detect.py
index 6b25daf05d..10cf2b591e 100644
--- a/platform/osx/detect.py
+++ b/platform/osx/detect.py
@@ -64,6 +64,7 @@ def configure(env):
elif env["target"] == "debug":
env.Prepend(CCFLAGS=["-g3"])
env.Prepend(CPPDEFINES=["DEBUG_ENABLED"])
+ env.Prepend(CPPDEFINES=["DEV_ENABLED"])
env.Prepend(LINKFLAGS=["-Xlinker", "-no_deduplicate"])
## Architecture
diff --git a/platform/osx/display_server_osx.h b/platform/osx/display_server_osx.h
index 06b14f1c14..e8f2858489 100644
--- a/platform/osx/display_server_osx.h
+++ b/platform/osx/display_server_osx.h
@@ -305,6 +305,7 @@ public:
virtual void keyboard_set_current_layout(int p_index) override;
virtual String keyboard_get_layout_language(int p_index) const override;
virtual String keyboard_get_layout_name(int p_index) const override;
+ virtual Key keyboard_get_keycode_from_physical(Key p_keycode) const override;
virtual void process_events() override;
virtual void force_process_and_drop_events() override;
diff --git a/platform/osx/display_server_osx.mm b/platform/osx/display_server_osx.mm
index f037d75fbd..3fe055a511 100644
--- a/platform/osx/display_server_osx.mm
+++ b/platform/osx/display_server_osx.mm
@@ -930,146 +930,155 @@ static bool isNumpadKey(unsigned int key) {
return false;
}
+// Keyboard symbol translation table
+static const Key _osx_to_godot_table[128] = {
+ /* 00 */ KEY_A,
+ /* 01 */ KEY_S,
+ /* 02 */ KEY_D,
+ /* 03 */ KEY_F,
+ /* 04 */ KEY_H,
+ /* 05 */ KEY_G,
+ /* 06 */ KEY_Z,
+ /* 07 */ KEY_X,
+ /* 08 */ KEY_C,
+ /* 09 */ KEY_V,
+ /* 0a */ KEY_SECTION, /* ISO Section */
+ /* 0b */ KEY_B,
+ /* 0c */ KEY_Q,
+ /* 0d */ KEY_W,
+ /* 0e */ KEY_E,
+ /* 0f */ KEY_R,
+ /* 10 */ KEY_Y,
+ /* 11 */ KEY_T,
+ /* 12 */ KEY_1,
+ /* 13 */ KEY_2,
+ /* 14 */ KEY_3,
+ /* 15 */ KEY_4,
+ /* 16 */ KEY_6,
+ /* 17 */ KEY_5,
+ /* 18 */ KEY_EQUAL,
+ /* 19 */ KEY_9,
+ /* 1a */ KEY_7,
+ /* 1b */ KEY_MINUS,
+ /* 1c */ KEY_8,
+ /* 1d */ KEY_0,
+ /* 1e */ KEY_BRACERIGHT,
+ /* 1f */ KEY_O,
+ /* 20 */ KEY_U,
+ /* 21 */ KEY_BRACELEFT,
+ /* 22 */ KEY_I,
+ /* 23 */ KEY_P,
+ /* 24 */ KEY_ENTER,
+ /* 25 */ KEY_L,
+ /* 26 */ KEY_J,
+ /* 27 */ KEY_APOSTROPHE,
+ /* 28 */ KEY_K,
+ /* 29 */ KEY_SEMICOLON,
+ /* 2a */ KEY_BACKSLASH,
+ /* 2b */ KEY_COMMA,
+ /* 2c */ KEY_SLASH,
+ /* 2d */ KEY_N,
+ /* 2e */ KEY_M,
+ /* 2f */ KEY_PERIOD,
+ /* 30 */ KEY_TAB,
+ /* 31 */ KEY_SPACE,
+ /* 32 */ KEY_QUOTELEFT,
+ /* 33 */ KEY_BACKSPACE,
+ /* 34 */ KEY_UNKNOWN,
+ /* 35 */ KEY_ESCAPE,
+ /* 36 */ KEY_META,
+ /* 37 */ KEY_META,
+ /* 38 */ KEY_SHIFT,
+ /* 39 */ KEY_CAPSLOCK,
+ /* 3a */ KEY_ALT,
+ /* 3b */ KEY_CTRL,
+ /* 3c */ KEY_SHIFT,
+ /* 3d */ KEY_ALT,
+ /* 3e */ KEY_CTRL,
+ /* 3f */ KEY_UNKNOWN, /* Function */
+ /* 40 */ KEY_UNKNOWN, /* F17 */
+ /* 41 */ KEY_KP_PERIOD,
+ /* 42 */ KEY_UNKNOWN,
+ /* 43 */ KEY_KP_MULTIPLY,
+ /* 44 */ KEY_UNKNOWN,
+ /* 45 */ KEY_KP_ADD,
+ /* 46 */ KEY_UNKNOWN,
+ /* 47 */ KEY_NUMLOCK, /* Really KeypadClear... */
+ /* 48 */ KEY_VOLUMEUP, /* VolumeUp */
+ /* 49 */ KEY_VOLUMEDOWN, /* VolumeDown */
+ /* 4a */ KEY_VOLUMEMUTE, /* Mute */
+ /* 4b */ KEY_KP_DIVIDE,
+ /* 4c */ KEY_KP_ENTER,
+ /* 4d */ KEY_UNKNOWN,
+ /* 4e */ KEY_KP_SUBTRACT,
+ /* 4f */ KEY_UNKNOWN, /* F18 */
+ /* 50 */ KEY_UNKNOWN, /* F19 */
+ /* 51 */ KEY_EQUAL, /* KeypadEqual */
+ /* 52 */ KEY_KP_0,
+ /* 53 */ KEY_KP_1,
+ /* 54 */ KEY_KP_2,
+ /* 55 */ KEY_KP_3,
+ /* 56 */ KEY_KP_4,
+ /* 57 */ KEY_KP_5,
+ /* 58 */ KEY_KP_6,
+ /* 59 */ KEY_KP_7,
+ /* 5a */ KEY_UNKNOWN, /* F20 */
+ /* 5b */ KEY_KP_8,
+ /* 5c */ KEY_KP_9,
+ /* 5d */ KEY_YEN, /* JIS Yen */
+ /* 5e */ KEY_UNDERSCORE, /* JIS Underscore */
+ /* 5f */ KEY_COMMA, /* JIS KeypadComma */
+ /* 60 */ KEY_F5,
+ /* 61 */ KEY_F6,
+ /* 62 */ KEY_F7,
+ /* 63 */ KEY_F3,
+ /* 64 */ KEY_F8,
+ /* 65 */ KEY_F9,
+ /* 66 */ KEY_UNKNOWN, /* JIS Eisu */
+ /* 67 */ KEY_F11,
+ /* 68 */ KEY_UNKNOWN, /* JIS Kana */
+ /* 69 */ KEY_F13,
+ /* 6a */ KEY_F16,
+ /* 6b */ KEY_F14,
+ /* 6c */ KEY_UNKNOWN,
+ /* 6d */ KEY_F10,
+ /* 6e */ KEY_MENU,
+ /* 6f */ KEY_F12,
+ /* 70 */ KEY_UNKNOWN,
+ /* 71 */ KEY_F15,
+ /* 72 */ KEY_INSERT, /* Really Help... */
+ /* 73 */ KEY_HOME,
+ /* 74 */ KEY_PAGEUP,
+ /* 75 */ KEY_DELETE,
+ /* 76 */ KEY_F4,
+ /* 77 */ KEY_END,
+ /* 78 */ KEY_F2,
+ /* 79 */ KEY_PAGEDOWN,
+ /* 7a */ KEY_F1,
+ /* 7b */ KEY_LEFT,
+ /* 7c */ KEY_RIGHT,
+ /* 7d */ KEY_DOWN,
+ /* 7e */ KEY_UP,
+ /* 7f */ KEY_UNKNOWN,
+};
+
// Translates a OS X keycode to a Godot keycode
-//
static Key translateKey(unsigned int key) {
- // Keyboard symbol translation table
- static const Key table[128] = {
- /* 00 */ KEY_A,
- /* 01 */ KEY_S,
- /* 02 */ KEY_D,
- /* 03 */ KEY_F,
- /* 04 */ KEY_H,
- /* 05 */ KEY_G,
- /* 06 */ KEY_Z,
- /* 07 */ KEY_X,
- /* 08 */ KEY_C,
- /* 09 */ KEY_V,
- /* 0a */ KEY_SECTION, /* ISO Section */
- /* 0b */ KEY_B,
- /* 0c */ KEY_Q,
- /* 0d */ KEY_W,
- /* 0e */ KEY_E,
- /* 0f */ KEY_R,
- /* 10 */ KEY_Y,
- /* 11 */ KEY_T,
- /* 12 */ KEY_1,
- /* 13 */ KEY_2,
- /* 14 */ KEY_3,
- /* 15 */ KEY_4,
- /* 16 */ KEY_6,
- /* 17 */ KEY_5,
- /* 18 */ KEY_EQUAL,
- /* 19 */ KEY_9,
- /* 1a */ KEY_7,
- /* 1b */ KEY_MINUS,
- /* 1c */ KEY_8,
- /* 1d */ KEY_0,
- /* 1e */ KEY_BRACERIGHT,
- /* 1f */ KEY_O,
- /* 20 */ KEY_U,
- /* 21 */ KEY_BRACELEFT,
- /* 22 */ KEY_I,
- /* 23 */ KEY_P,
- /* 24 */ KEY_ENTER,
- /* 25 */ KEY_L,
- /* 26 */ KEY_J,
- /* 27 */ KEY_APOSTROPHE,
- /* 28 */ KEY_K,
- /* 29 */ KEY_SEMICOLON,
- /* 2a */ KEY_BACKSLASH,
- /* 2b */ KEY_COMMA,
- /* 2c */ KEY_SLASH,
- /* 2d */ KEY_N,
- /* 2e */ KEY_M,
- /* 2f */ KEY_PERIOD,
- /* 30 */ KEY_TAB,
- /* 31 */ KEY_SPACE,
- /* 32 */ KEY_QUOTELEFT,
- /* 33 */ KEY_BACKSPACE,
- /* 34 */ KEY_UNKNOWN,
- /* 35 */ KEY_ESCAPE,
- /* 36 */ KEY_META,
- /* 37 */ KEY_META,
- /* 38 */ KEY_SHIFT,
- /* 39 */ KEY_CAPSLOCK,
- /* 3a */ KEY_ALT,
- /* 3b */ KEY_CTRL,
- /* 3c */ KEY_SHIFT,
- /* 3d */ KEY_ALT,
- /* 3e */ KEY_CTRL,
- /* 3f */ KEY_UNKNOWN, /* Function */
- /* 40 */ KEY_UNKNOWN, /* F17 */
- /* 41 */ KEY_KP_PERIOD,
- /* 42 */ KEY_UNKNOWN,
- /* 43 */ KEY_KP_MULTIPLY,
- /* 44 */ KEY_UNKNOWN,
- /* 45 */ KEY_KP_ADD,
- /* 46 */ KEY_UNKNOWN,
- /* 47 */ KEY_NUMLOCK, /* Really KeypadClear... */
- /* 48 */ KEY_VOLUMEUP, /* VolumeUp */
- /* 49 */ KEY_VOLUMEDOWN, /* VolumeDown */
- /* 4a */ KEY_VOLUMEMUTE, /* Mute */
- /* 4b */ KEY_KP_DIVIDE,
- /* 4c */ KEY_KP_ENTER,
- /* 4d */ KEY_UNKNOWN,
- /* 4e */ KEY_KP_SUBTRACT,
- /* 4f */ KEY_UNKNOWN, /* F18 */
- /* 50 */ KEY_UNKNOWN, /* F19 */
- /* 51 */ KEY_EQUAL, /* KeypadEqual */
- /* 52 */ KEY_KP_0,
- /* 53 */ KEY_KP_1,
- /* 54 */ KEY_KP_2,
- /* 55 */ KEY_KP_3,
- /* 56 */ KEY_KP_4,
- /* 57 */ KEY_KP_5,
- /* 58 */ KEY_KP_6,
- /* 59 */ KEY_KP_7,
- /* 5a */ KEY_UNKNOWN, /* F20 */
- /* 5b */ KEY_KP_8,
- /* 5c */ KEY_KP_9,
- /* 5d */ KEY_YEN, /* JIS Yen */
- /* 5e */ KEY_UNDERSCORE, /* JIS Underscore */
- /* 5f */ KEY_COMMA, /* JIS KeypadComma */
- /* 60 */ KEY_F5,
- /* 61 */ KEY_F6,
- /* 62 */ KEY_F7,
- /* 63 */ KEY_F3,
- /* 64 */ KEY_F8,
- /* 65 */ KEY_F9,
- /* 66 */ KEY_UNKNOWN, /* JIS Eisu */
- /* 67 */ KEY_F11,
- /* 68 */ KEY_UNKNOWN, /* JIS Kana */
- /* 69 */ KEY_F13,
- /* 6a */ KEY_F16,
- /* 6b */ KEY_F14,
- /* 6c */ KEY_UNKNOWN,
- /* 6d */ KEY_F10,
- /* 6e */ KEY_MENU,
- /* 6f */ KEY_F12,
- /* 70 */ KEY_UNKNOWN,
- /* 71 */ KEY_F15,
- /* 72 */ KEY_INSERT, /* Really Help... */
- /* 73 */ KEY_HOME,
- /* 74 */ KEY_PAGEUP,
- /* 75 */ KEY_DELETE,
- /* 76 */ KEY_F4,
- /* 77 */ KEY_END,
- /* 78 */ KEY_F2,
- /* 79 */ KEY_PAGEDOWN,
- /* 7a */ KEY_F1,
- /* 7b */ KEY_LEFT,
- /* 7c */ KEY_RIGHT,
- /* 7d */ KEY_DOWN,
- /* 7e */ KEY_UP,
- /* 7f */ KEY_UNKNOWN,
- };
-
if (key >= 128) {
return KEY_UNKNOWN;
}
- return table[key];
+ return _osx_to_godot_table[key];
+}
+
+// Translates a Godot keycode back to a OSX keycode
+static unsigned int unmapKey(Key key) {
+ for (int i = 0; i <= 126; i++) {
+ if (_osx_to_godot_table[i] == key) {
+ return i;
+ }
+ }
+ return 127;
}
struct _KeyCodeMap {
@@ -3005,7 +3014,7 @@ void DisplayServerOSX::cursor_set_custom_image(const RES &p_cursor, CursorShape
ERR_FAIL_COND(!image.is_valid());
NSBitmapImageRep *imgrep = [[NSBitmapImageRep alloc]
- initWithBitmapDataPlanes:NULL
+ initWithBitmapDataPlanes:nullptr
pixelsWide:int(texture_size.width)
pixelsHigh:int(texture_size.height)
bitsPerSample:8
@@ -3200,6 +3209,17 @@ String DisplayServerOSX::keyboard_get_layout_name(int p_index) const {
return kbd_layouts[p_index].name;
}
+Key DisplayServerOSX::keyboard_get_keycode_from_physical(Key p_keycode) const {
+ if (p_keycode == KEY_PAUSE) {
+ return p_keycode;
+ }
+
+ unsigned int modifiers = p_keycode & KEY_MODIFIER_MASK;
+ unsigned int keycode_no_mod = p_keycode & KEY_CODE_MASK;
+ unsigned int osx_keycode = unmapKey((Key)keycode_no_mod);
+ return (Key)(remapKey(osx_keycode, 0) | modifiers);
+}
+
void DisplayServerOSX::_push_input(const Ref<InputEvent> &p_event) {
Ref<InputEvent> ev = p_event;
Input::get_singleton()->parse_input_event(ev);
@@ -3398,7 +3418,7 @@ void DisplayServerOSX::set_icon(const Ref<Image> &p_icon) {
img = img->duplicate();
img->convert(Image::FORMAT_RGBA8);
NSBitmapImageRep *imgrep = [[NSBitmapImageRep alloc]
- initWithBitmapDataPlanes:NULL
+ initWithBitmapDataPlanes:nullptr
pixelsWide:img->get_width()
pixelsHigh:img->get_height()
bitsPerSample:8
diff --git a/platform/osx/export/export_plugin.cpp b/platform/osx/export/export_plugin.cpp
index 54a3104482..2404c20153 100644
--- a/platform/osx/export/export_plugin.cpp
+++ b/platform/osx/export/export_plugin.cpp
@@ -95,6 +95,7 @@ void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_pictures", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_music", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_movies", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::ARRAY, "codesign/entitlements/app_sandbox/helper_executables", PROPERTY_HINT_ARRAY_TYPE, itos(Variant::STRING) + "/" + itos(PROPERTY_HINT_GLOBAL_FILE) + ":"), Array()));
r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "codesign/custom_options"), PackedStringArray()));
@@ -535,6 +536,8 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
err = ERR_CANT_CREATE;
}
+ Array helpers = p_preset->get("codesign/entitlements/app_sandbox/helper_executables");
+
// Create our folder structure.
if (err == OK) {
print_line("Creating " + tmp_app_path_name + "/Contents/MacOS");
@@ -546,6 +549,11 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/Frameworks");
}
+ if ((err == OK) && helpers.size() > 0) {
+ print_line("Creating " + tmp_app_path_name + "/Contents/Helpers");
+ err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/Helpers");
+ }
+
if (err == OK) {
print_line("Creating " + tmp_app_path_name + "/Contents/Resources");
err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/Resources");
@@ -688,6 +696,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
bool sign_enabled = p_preset->get("codesign/enable");
String ent_path = p_preset->get("codesign/entitlements/custom_file");
+ String hlp_ent_path = EditorPaths::get_singleton()->get_cache_dir().plus_file(pkg_name + "_helper.entitlements");
if (sign_enabled && (ent_path == "")) {
ent_path = EditorPaths::get_singleton()->get_cache_dir().plus_file(pkg_name + ".entitlements");
@@ -819,10 +828,43 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
} else {
err = ERR_CANT_CREATE;
}
+
+ if ((err == OK) && helpers.size() > 0) {
+ ent_f = FileAccess::open(hlp_ent_path, FileAccess::WRITE);
+ if (ent_f) {
+ ent_f->store_line("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+ ent_f->store_line("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
+ ent_f->store_line("<plist version=\"1.0\">");
+ ent_f->store_line("<dict>");
+ ent_f->store_line("<key>com.apple.security.app-sandbox</key>");
+ ent_f->store_line("<true/>");
+ ent_f->store_line("<key>com.apple.security.inherit</key>");
+ ent_f->store_line("<true/>");
+ ent_f->store_line("</dict>");
+ ent_f->store_line("</plist>");
+
+ ent_f->close();
+ memdelete(ent_f);
+ } else {
+ err = ERR_CANT_CREATE;
+ }
+ }
+ }
+
+ if ((err == OK) && helpers.size() > 0) {
+ DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ for (int i = 0; i < helpers.size(); i++) {
+ String hlp_path = helpers[i];
+ err = da->copy(hlp_path, tmp_app_path_name + "/Contents/Helpers/" + hlp_path.get_file());
+ if (err == OK && sign_enabled) {
+ err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Helpers/" + hlp_path.get_file(), hlp_ent_path);
+ }
+ FileAccess::set_unix_permissions(tmp_app_path_name + "/Contents/Helpers/" + hlp_path.get_file(), 0755);
+ }
}
if (err == OK) {
- DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
for (int i = 0; i < shared_objects.size(); i++) {
String src_path = ProjectSettings::get_singleton()->globalize_path(shared_objects[i].path);
if (da->dir_exists(src_path)) {
@@ -842,7 +884,6 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file(), ent_path);
}
}
- memdelete(da);
}
if (sign_enabled) {
@@ -903,6 +944,9 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
err = _notarize(p_preset, p_path);
}
+ // Clean up temporary entitlements files.
+ DirAccess::remove_file_or_error(hlp_ent_path);
+
// Clean up temporary .app dir.
tmp_app_dir->change_dir(tmp_app_path_name);
tmp_app_dir->erase_contents_recursive();
@@ -916,7 +960,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
void EditorExportPlatformOSX::_zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name) {
String dir = p_root_path.plus_file(p_folder);
- DirAccess *da = DirAccess::open(dir);
+ DirAccessRef da = DirAccess::open(dir);
da->list_dir_begin();
String f;
while ((f = da->get_next()) != "") {
@@ -967,7 +1011,7 @@ void EditorExportPlatformOSX::_zip_folder_recursive(zipFile &p_zip, const String
} else if (da->current_is_dir()) {
_zip_folder_recursive(p_zip, p_root_path, p_folder.plus_file(f), p_pkg_name);
} else {
- bool is_executable = (p_folder.ends_with("MacOS") && (f == p_pkg_name));
+ bool is_executable = (p_folder.ends_with("MacOS") && (f == p_pkg_name)) || p_folder.ends_with("Helpers");
OS::Time time = OS::get_singleton()->get_time();
OS::Date date = OS::get_singleton()->get_date();
@@ -1012,7 +1056,6 @@ void EditorExportPlatformOSX::_zip_folder_recursive(zipFile &p_zip, const String
}
}
da->list_dir_end();
- memdelete(da);
}
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 df41ccd892..a52436a70a 100644
--- a/platform/osx/os_osx.h
+++ b/platform/osx/os_osx.h
@@ -82,6 +82,7 @@ public:
virtual String get_data_path() const override;
virtual String get_cache_path() const override;
virtual String get_bundle_resource_dir() const override;
+ virtual String get_bundle_icon_path() const override;
virtual String get_godot_dir_name() const override;
virtual String get_system_dir(SystemDir p_dir, bool p_shared_storage = true) const override;
diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm
index c6e35fee83..e9cb46ed21 100644
--- a/platform/osx/os_osx.mm
+++ b/platform/osx/os_osx.mm
@@ -314,17 +314,27 @@ String OS_OSX::get_name() const {
return "macOS";
}
+_FORCE_INLINE_ String _get_framework_executable(const String p_path) {
+ // Append framework executable name, or return as is if p_path is not a framework.
+ DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ if (da->dir_exists(p_path) && da->file_exists(p_path.plus_file(p_path.get_file().get_basename()))) {
+ return p_path.plus_file(p_path.get_file().get_basename());
+ } else {
+ return p_path;
+ }
+}
+
Error OS_OSX::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) {
- String path = p_path;
+ String path = _get_framework_executable(p_path);
if (!FileAccess::exists(path)) {
- //this code exists so gdnative can load .dylib files from within the executable path
- path = get_executable_path().get_base_dir().plus_file(p_path.get_file());
+ // This code exists so gdnative can load .dylib files from within the executable path.
+ path = _get_framework_executable(get_executable_path().get_base_dir().plus_file(p_path.get_file()));
}
if (!FileAccess::exists(path)) {
- //this code exists so gdnative can load .dylib files from a standard macOS location
- path = get_executable_path().get_base_dir().plus_file("../Frameworks").plus_file(p_path.get_file());
+ // This code exists so gdnative can load .dylib files from a standard macOS location.
+ path = _get_framework_executable(get_executable_path().get_base_dir().plus_file("../Frameworks").plus_file(p_path.get_file()));
}
p_library_handle = dlopen(path.utf8().get_data(), RTLD_NOW);
@@ -379,14 +389,26 @@ String OS_OSX::get_cache_path() const {
}
String OS_OSX::get_bundle_resource_dir() const {
+ String ret;
+
NSBundle *main = [NSBundle mainBundle];
- NSString *resourcePath = [main resourcePath];
+ if (main) {
+ NSString *resourcePath = [main resourcePath];
+ ret.parse_utf8([resourcePath UTF8String]);
+ }
+ return ret;
+}
- char *utfs = strdup([resourcePath UTF8String]);
+String OS_OSX::get_bundle_icon_path() const {
String ret;
- ret.parse_utf8(utfs);
- free(utfs);
+ NSBundle *main = [NSBundle mainBundle];
+ if (main) {
+ NSString *iconPath = [[main infoDictionary] objectForKey:@"CFBundleIconFile"];
+ if (iconPath) {
+ ret.parse_utf8([iconPath UTF8String]);
+ }
+ }
return ret;
}
diff --git a/platform/uwp/detect.py b/platform/uwp/detect.py
index 28922a4f59..f31b43cd49 100644
--- a/platform/uwp/detect.py
+++ b/platform/uwp/detect.py
@@ -72,6 +72,7 @@ def configure(env):
env.Append(CCFLAGS=["/Zi"])
env.Append(CCFLAGS=["/MDd"])
env.Append(CPPDEFINES=["DEBUG_ENABLED"])
+ env.Append(CPPDEFINES=["DEV_ENABLED"])
env.Append(LINKFLAGS=["/SUBSYSTEM:CONSOLE"])
env.AppendUnique(CPPDEFINES=["WINDOWS_SUBSYSTEM_CONSOLE"])
env.Append(LINKFLAGS=["/DEBUG"])
diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp
index 6ac5b55156..daf3c14f3b 100644
--- a/platform/uwp/os_uwp.cpp
+++ b/platform/uwp/os_uwp.cpp
@@ -28,14 +28,12 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-// Must include Winsock before windows.h (included by os_uwp.h)
-#include "drivers/unix/net_socket_posix.h"
-
#include "os_uwp.h"
#include "core/config/project_settings.h"
#include "core/io/marshalls.h"
#include "drivers/unix/ip_unix.h"
+#include "drivers/unix/net_socket_posix.h"
#include "drivers/windows/dir_access_windows.h"
#include "drivers/windows/file_access_windows.h"
#include "drivers/windows/mutex_windows.h"
diff --git a/platform/uwp/os_uwp.h b/platform/uwp/os_uwp.h
index c9b2600c8e..7945f409a1 100644
--- a/platform/uwp/os_uwp.h
+++ b/platform/uwp/os_uwp.h
@@ -45,6 +45,7 @@
#include <fcntl.h>
#include <io.h>
#include <stdio.h>
+#define WIN32_LEAN_AND_MEAN
#include <windows.h>
class OS_UWP : public OS {
diff --git a/platform/windows/context_gl_windows.h b/platform/windows/context_gl_windows.h
index c8e8a0891d..feff1d825b 100644
--- a/platform/windows/context_gl_windows.h
+++ b/platform/windows/context_gl_windows.h
@@ -38,6 +38,7 @@
#include "core/error/error_list.h"
#include "core/os/os.h"
+#define WIN32_LEAN_AND_MEAN
#include <windows.h>
typedef bool(APIENTRY *PFNWGLSWAPINTERVALEXTPROC)(int interval);
diff --git a/platform/windows/crash_handler_windows.h b/platform/windows/crash_handler_windows.h
index e1ec8e6787..5cdc6d3e05 100644
--- a/platform/windows/crash_handler_windows.h
+++ b/platform/windows/crash_handler_windows.h
@@ -31,6 +31,7 @@
#ifndef CRASH_HANDLER_WINDOWS_H
#define CRASH_HANDLER_WINDOWS_H
+#define WIN32_LEAN_AND_MEAN
#include <windows.h>
// Crash handler exception only enabled with MSVC
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index 3961480d23..6f99368e99 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -208,6 +208,7 @@ def configure_msvc(env, manual_msvc_config):
elif env["target"] == "debug":
env.AppendUnique(CCFLAGS=["/Zi", "/FS", "/Od", "/EHsc"])
env.AppendUnique(CPPDEFINES=["DEBUG_ENABLED"])
+ env.AppendUnique(CPPDEFINES=["DEV_ENABLED"])
env.Append(LINKFLAGS=["/DEBUG"])
if env["debug_symbols"]:
@@ -362,6 +363,7 @@ def configure_mingw(env):
elif env["target"] == "debug":
env.Append(CCFLAGS=["-g3"])
env.Append(CPPDEFINES=["DEBUG_ENABLED"])
+ env.Append(CPPDEFINES=["DEV_ENABLED"])
if env["windows_subsystem"] == "gui":
env.Append(LINKFLAGS=["-Wl,--subsystem,windows"])
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index 6402702415..905c4142a8 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -38,7 +38,6 @@
#include <avrt.h>
-#ifdef DEBUG_ENABLED
static String format_error_message(DWORD id) {
LPWSTR messageBuffer = nullptr;
size_t size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
@@ -50,7 +49,6 @@ static String format_error_message(DWORD id) {
return msg;
}
-#endif // DEBUG_ENABLED
bool DisplayServerWindows::has_feature(Feature p_feature) const {
switch (p_feature) {
@@ -454,8 +452,8 @@ Vector<DisplayServer::WindowID> DisplayServerWindows::get_window_list() const {
_THREAD_SAFE_METHOD_
Vector<DisplayServer::WindowID> ret;
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
- ret.push_back(E->key());
+ for (const KeyValue<WindowID, WindowData> &E : windows) {
+ ret.push_back(E.key);
}
return ret;
}
@@ -465,9 +463,9 @@ DisplayServer::WindowID DisplayServerWindows::get_window_at_screen_position(cons
p.x = p_position.x;
p.y = p_position.y;
HWND hwnd = WindowFromPoint(p);
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
- if (E->get().hWnd == hwnd) {
- return E->key();
+ for (const KeyValue<WindowID, WindowData> &E : windows) {
+ if (E.value.hWnd == hwnd) {
+ return E.key;
}
}
@@ -1131,8 +1129,8 @@ bool DisplayServerWindows::window_can_draw(WindowID p_window) const {
bool DisplayServerWindows::can_any_window_draw() const {
_THREAD_SAFE_METHOD_
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
- if (!E->get().minimized) {
+ for (const KeyValue<WindowID, WindowData> &E : windows) {
+ if (!E.value.minimized) {
return true;
}
}
@@ -1210,7 +1208,7 @@ void DisplayServerWindows::cursor_set_shape(CursorShape p_shape) {
IDC_CROSS,
IDC_WAIT,
IDC_APPSTARTING,
- IDC_ARROW,
+ IDC_SIZEALL,
IDC_ARROW,
IDC_NO,
IDC_SIZENS,
@@ -1476,6 +1474,42 @@ String DisplayServerWindows::keyboard_get_layout_language(int p_index) const {
return String::utf16((const char16_t *)buf).substr(0, 2);
}
+Key DisplayServerWindows::keyboard_get_keycode_from_physical(Key p_keycode) const {
+ unsigned int modifiers = p_keycode & KEY_MODIFIER_MASK;
+ Key keycode_no_mod = (Key)(p_keycode & KEY_CODE_MASK);
+
+ if (keycode_no_mod == KEY_PRINT ||
+ keycode_no_mod == KEY_KP_ADD ||
+ keycode_no_mod == KEY_KP_5 ||
+ (keycode_no_mod >= KEY_0 && keycode_no_mod <= KEY_9)) {
+ return p_keycode;
+ }
+
+ unsigned int scancode = KeyMappingWindows::get_scancode(keycode_no_mod);
+ if (scancode == 0) {
+ return p_keycode;
+ }
+
+ HKL current_layout = GetKeyboardLayout(0);
+ UINT vk = MapVirtualKeyEx(scancode, MAPVK_VSC_TO_VK, current_layout);
+ if (vk == 0) {
+ return p_keycode;
+ }
+
+ UINT char_code = MapVirtualKeyEx(vk, MAPVK_VK_TO_CHAR, current_layout) & 0x7FFF;
+ // Unlike a similar Linux/BSD check which matches full Latin-1 range,
+ // we limit these to ASCII to fix some layouts, including Arabic ones
+ if (char_code >= 32 && char_code <= 127) {
+ // Godot uses 'braces' instead of 'brackets'
+ if (char_code == KEY_BRACKETLEFT || char_code == KEY_BRACKETRIGHT) {
+ char_code += 32;
+ }
+ return (Key)(char_code | modifiers);
+ }
+
+ return (Key)(KeyMappingWindows::get_keysym(vk) | modifiers);
+}
+
String _get_full_layout_name_from_registry(HKL p_layout) {
String id = "SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\" + String::num_int64((int64_t)p_layout, 16, false).lpad(8, "0");
String ret;
@@ -1816,8 +1850,8 @@ void DisplayServerWindows::_dispatch_input_event(const Ref<InputEvent> &p_event)
callable.call((const Variant **)&evp, 1, ret, ce);
} else {
// Send to all windows.
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
- Callable callable = E->get().input_event_callback;
+ for (const KeyValue<WindowID, WindowData> &E : windows) {
+ const Callable callable = E.value.input_event_callback;
if (callable.is_null()) {
continue;
}
@@ -1844,9 +1878,9 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
bool window_created = false;
// Check whether window exists.
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
- if (E->get().hWnd == hWnd) {
- window_id = E->key();
+ for (const KeyValue<WindowID, WindowData> &E : windows) {
+ if (E.value.hWnd == hWnd) {
+ window_id = E.key;
window_created = true;
break;
}
@@ -1882,8 +1916,8 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
ReleaseCapture();
// Release every touch to avoid sticky points.
- for (Map<int, Vector2>::Element *E = touch_state.front(); E; E = E->next()) {
- _touch_event(window_id, false, E->get().x, E->get().y, E->key());
+ for (const KeyValue<int, Vector2> &E : touch_state) {
+ _touch_event(window_id, false, E.value.x, E.value.y, E.key);
}
touch_state.clear();
@@ -2943,8 +2977,8 @@ void DisplayServerWindows::_process_key_events() {
}
void DisplayServerWindows::_update_tablet_ctx(const String &p_old_driver, const String &p_new_driver) {
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
- WindowData &wd = E->get();
+ for (KeyValue<WindowID, WindowData> &E : windows) {
+ WindowData &wd = E.value;
wd.block_mm = false;
if ((p_old_driver == "wintab") && wintab_available && wd.wtctx) {
wintab_WTEnable(wd.wtctx, false);
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index c02a90c543..6a90b28579 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -63,6 +63,7 @@
#include <fcntl.h>
#include <io.h>
#include <stdio.h>
+#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <windowsx.h>
@@ -543,6 +544,7 @@ public:
virtual void keyboard_set_current_layout(int p_index) override;
virtual String keyboard_get_layout_language(int p_index) const override;
virtual String keyboard_get_layout_name(int p_index) const override;
+ virtual Key keyboard_get_keycode_from_physical(Key p_keycode) const override;
virtual int tablet_get_driver_count() const override;
virtual String tablet_get_driver_name(int p_driver) const override;
diff --git a/platform/windows/key_mapping_windows.cpp b/platform/windows/key_mapping_windows.cpp
index db99d6c122..8016d20470 100644
--- a/platform/windows/key_mapping_windows.cpp
+++ b/platform/windows/key_mapping_windows.cpp
@@ -345,6 +345,16 @@ unsigned int KeyMappingWindows::get_keysym(unsigned int p_code) {
return KEY_UNKNOWN;
}
+unsigned int KeyMappingWindows::get_scancode(Key p_keycode) {
+ for (int i = 0; _scancode_to_keycode[i].keysym != KEY_UNKNOWN; i++) {
+ if (_scancode_to_keycode[i].keysym == p_keycode) {
+ return _scancode_to_keycode[i].keycode;
+ }
+ }
+
+ return 0;
+}
+
unsigned int KeyMappingWindows::get_scansym(unsigned int p_code, bool p_extended) {
unsigned int keycode = KEY_UNKNOWN;
for (int i = 0; _scancode_to_keycode[i].keysym != KEY_UNKNOWN; i++) {
diff --git a/platform/windows/key_mapping_windows.h b/platform/windows/key_mapping_windows.h
index fb07227014..d056e88f06 100644
--- a/platform/windows/key_mapping_windows.h
+++ b/platform/windows/key_mapping_windows.h
@@ -33,8 +33,8 @@
#include "core/os/keyboard.h"
+#define WIN32_LEAN_AND_MEAN
#include <windows.h>
-
#include <winuser.h>
class KeyMappingWindows {
@@ -42,6 +42,7 @@ class KeyMappingWindows {
public:
static unsigned int get_keysym(unsigned int p_code);
+ static unsigned int get_scancode(Key p_keycode);
static unsigned int get_scansym(unsigned int p_code, bool p_extended);
static bool is_extended_key(unsigned int p_code);
};
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index 78b7be8a30..6c02bf1e16 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -28,15 +28,13 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-// Must include Winsock before windows.h (included by os_windows.h)
-#include "drivers/unix/net_socket_posix.h"
-
#include "os_windows.h"
#include "core/debugger/engine_debugger.h"
#include "core/debugger/script_debugger.h"
#include "core/io/marshalls.h"
#include "core/version_generated.gen.h"
+#include "drivers/unix/net_socket_posix.h"
#include "drivers/windows/dir_access_windows.h"
#include "drivers/windows/file_access_windows.h"
#include "joypad_windows.h"
@@ -75,7 +73,6 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
#define GetProcAddress (void *)GetProcAddress
#endif
-#ifdef DEBUG_ENABLED
static String format_error_message(DWORD id) {
LPWSTR messageBuffer = nullptr;
size_t size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
@@ -87,7 +84,6 @@ static String format_error_message(DWORD id) {
return msg;
}
-#endif // DEBUG_ENABLED
void RedirectIOToConsole() {
int hConHandle;
@@ -681,18 +677,27 @@ String OS_Windows::get_data_path() const {
}
String OS_Windows::get_cache_path() const {
- // The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on Windows as well.
- if (has_environment("XDG_CACHE_HOME")) {
- if (get_environment("XDG_CACHE_HOME").is_absolute_path()) {
- return get_environment("XDG_CACHE_HOME").replace("\\", "/");
- } else {
- WARN_PRINT_ONCE("`XDG_CACHE_HOME` is a relative path. Ignoring its value and falling back to `%TEMP%` or `get_config_path()` per the XDG Base Directory specification.");
+ static String cache_path_cache;
+ if (cache_path_cache.is_empty()) {
+ // The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on Windows as well.
+ if (has_environment("XDG_CACHE_HOME")) {
+ if (get_environment("XDG_CACHE_HOME").is_absolute_path()) {
+ cache_path_cache = get_environment("XDG_CACHE_HOME").replace("\\", "/");
+ } else {
+ WARN_PRINT_ONCE("`XDG_CACHE_HOME` is a relative path. Ignoring its value and falling back to `%LOCALAPPDATA%\\cache`, `%TEMP%` or `get_config_path()` per the XDG Base Directory specification.");
+ }
+ }
+ if (cache_path_cache.is_empty() && has_environment("LOCALAPPDATA")) {
+ cache_path_cache = get_environment("LOCALAPPDATA").replace("\\", "/");
+ }
+ if (cache_path_cache.is_empty() && has_environment("TEMP")) {
+ cache_path_cache = get_environment("TEMP").replace("\\", "/");
+ }
+ if (cache_path_cache.is_empty()) {
+ cache_path_cache = get_config_path();
}
}
- if (has_environment("TEMP")) {
- return get_environment("TEMP").replace("\\", "/");
- }
- return get_config_path();
+ return cache_path_cache;
}
// Get properly capitalized engine name for system paths
diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h
index c4a2eda8f4..d24afa91eb 100644
--- a/platform/windows/os_windows.h
+++ b/platform/windows/os_windows.h
@@ -57,7 +57,9 @@
#include <fcntl.h>
#include <io.h>
+#include <shellapi.h>
#include <stdio.h>
+#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <windowsx.h>
diff --git a/platform/windows/vulkan_context_win.h b/platform/windows/vulkan_context_win.h
index 39dd2641fd..61e66b8ae0 100644
--- a/platform/windows/vulkan_context_win.h
+++ b/platform/windows/vulkan_context_win.h
@@ -32,6 +32,8 @@
#define VULKAN_DEVICE_WIN_H
#include "drivers/vulkan/vulkan_context.h"
+
+#define WIN32_LEAN_AND_MEAN
#include <windows.h>
class VulkanContextWindows : public VulkanContext {
diff --git a/platform/windows/windows_terminal_logger.cpp b/platform/windows/windows_terminal_logger.cpp
index 8cab7ca521..e54a61fdfd 100644
--- a/platform/windows/windows_terminal_logger.cpp
+++ b/platform/windows/windows_terminal_logger.cpp
@@ -33,6 +33,7 @@
#ifdef WINDOWS_ENABLED
#include <stdio.h>
+#define WIN32_LEAN_AND_MEAN
#include <windows.h>
void WindowsTerminalLogger::logv(const char *p_format, va_list p_list, bool p_err) {