summaryrefslogtreecommitdiff
path: root/platform
diff options
context:
space:
mode:
Diffstat (limited to 'platform')
-rw-r--r--platform/android/SCsub12
-rw-r--r--platform/android/android_input_handler.cpp217
-rw-r--r--platform/android/android_input_handler.h22
-rw-r--r--platform/android/detect.py56
-rw-r--r--platform/android/display_server_android.cpp6
-rw-r--r--platform/android/export/export.cpp2
-rw-r--r--platform/android/export/export_plugin.cpp24
-rw-r--r--platform/android/export/gradle_export_util.cpp4
-rw-r--r--platform/android/file_access_android.cpp2
-rw-r--r--platform/android/file_access_android.h2
-rw-r--r--platform/android/file_access_filesystem_jandroid.cpp2
-rw-r--r--platform/android/file_access_filesystem_jandroid.h2
-rw-r--r--platform/android/java/app/config.gradle26
-rw-r--r--platform/android/java/build.gradle43
-rw-r--r--platform/android/java/editor/build.gradle23
-rw-r--r--platform/android/java/editor/src/main/AndroidManifest.xml2
-rw-r--r--platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt16
-rw-r--r--platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt4
-rw-r--r--platform/android/java/lib/build.gradle31
-rw-r--r--platform/android/java/lib/res/values/strings.xml2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.java45
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java33
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotLib.java39
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java33
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt289
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java279
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java2
-rw-r--r--platform/android/java_godot_io_wrapper.h3
-rw-r--r--platform/android/java_godot_lib_jni.cpp57
-rw-r--r--platform/android/java_godot_lib_jni.h14
-rw-r--r--platform/android/java_godot_view_wrapper.cpp15
-rw-r--r--platform/android/java_godot_view_wrapper.h3
-rw-r--r--platform/android/java_godot_wrapper.cpp39
-rw-r--r--platform/android/java_godot_wrapper.h8
-rw-r--r--platform/android/os_android.cpp74
-rw-r--r--platform/android/os_android.h4
-rw-r--r--platform/android/thread_jandroid.cpp2
-rw-r--r--platform/ios/detect.py52
-rw-r--r--platform/ios/display_layer.mm10
-rw-r--r--platform/ios/display_server_ios.mm4
-rw-r--r--platform/ios/export/export_plugin.cpp1
-rw-r--r--platform/ios/godot_view.mm2
-rw-r--r--platform/ios/os_ios.h8
-rw-r--r--platform/ios/os_ios.mm9
-rw-r--r--platform/ios/tts_ios.mm6
-rw-r--r--platform/linuxbsd/crash_handler_linuxbsd.cpp2
-rw-r--r--platform/linuxbsd/detect.py60
-rw-r--r--platform/linuxbsd/detect_prime_x11.h8
-rw-r--r--platform/linuxbsd/display_server_x11.cpp58
-rw-r--r--platform/linuxbsd/display_server_x11.h2
-rw-r--r--platform/linuxbsd/gl_manager_x11.cpp26
-rw-r--r--platform/linuxbsd/key_mapping_x11.cpp26
-rw-r--r--platform/linuxbsd/os_linuxbsd.cpp48
-rw-r--r--platform/linuxbsd/os_linuxbsd.h4
-rw-r--r--platform/macos/SCsub1
-rw-r--r--platform/macos/detect.py49
-rw-r--r--platform/macos/dir_access_macos.h10
-rw-r--r--platform/macos/dir_access_macos.mm4
-rw-r--r--platform/macos/display_server_macos.h14
-rw-r--r--platform/macos/display_server_macos.mm126
-rw-r--r--platform/macos/export/codesign.h6
-rw-r--r--platform/macos/export/export.cpp2
-rw-r--r--platform/macos/export/export_plugin.cpp30
-rw-r--r--platform/macos/export/lipo.h4
-rw-r--r--platform/macos/export/macho.h4
-rw-r--r--platform/macos/export/plist.h4
-rw-r--r--platform/macos/gl_manager_macos_legacy.mm5
-rw-r--r--platform/macos/godot_button_view.h (renamed from platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java)69
-rw-r--r--platform/macos/godot_button_view.mm123
-rw-r--r--platform/macos/godot_content_view.h10
-rw-r--r--platform/macos/godot_content_view.mm59
-rw-r--r--platform/macos/godot_window.h2
-rw-r--r--platform/macos/godot_window.mm13
-rw-r--r--platform/macos/godot_window_delegate.h2
-rw-r--r--platform/macos/godot_window_delegate.mm70
-rw-r--r--platform/macos/os_macos.h2
-rw-r--r--platform/macos/os_macos.mm14
-rw-r--r--platform/macos/tts_macos.mm8
-rw-r--r--platform/uwp/detect.py11
-rw-r--r--platform/uwp/export/app_packager.cpp2
-rw-r--r--platform/uwp/os_uwp.cpp45
-rw-r--r--platform/uwp/os_uwp.h7
-rw-r--r--platform/web/.eslintrc.engine.js1
-rw-r--r--platform/web/SCsub3
-rw-r--r--platform/web/api/api.cpp52
-rw-r--r--platform/web/api/javascript_bridge_singleton.h (renamed from platform/web/api/javascript_singleton.h)20
-rw-r--r--platform/web/audio_driver_web.cpp46
-rw-r--r--platform/web/audio_driver_web.h41
-rw-r--r--platform/web/detect.py51
-rw-r--r--platform/web/display_server_web.cpp6
-rw-r--r--platform/web/emscripten_helpers.py14
-rw-r--r--platform/web/export/editor_http_server.h38
-rw-r--r--platform/web/export/export.cpp12
-rw-r--r--platform/web/export/export_plugin.cpp36
-rw-r--r--platform/web/export/export_plugin.h2
-rw-r--r--platform/web/http_client_web.cpp6
-rw-r--r--platform/web/http_client_web.h2
-rw-r--r--platform/web/javascript_bridge_singleton.cpp (renamed from platform/web/javascript_singleton.cpp)20
-rw-r--r--platform/web/js/engine/config.js3
-rw-r--r--platform/web/js/engine/engine.js25
-rw-r--r--platform/web/js/engine/features.js96
-rw-r--r--platform/web/js/libs/audio.worklet.js2
-rw-r--r--platform/web/js/libs/library_godot_audio.js9
-rw-r--r--platform/web/js/libs/library_godot_os.js14
-rw-r--r--platform/web/os_web.cpp9
-rw-r--r--platform/web/package-lock.json1221
-rw-r--r--platform/web/package.json8
-rw-r--r--platform/web/serve.json21
-rwxr-xr-xplatform/web/serve.py52
-rw-r--r--platform/web/web_main.cpp32
-rw-r--r--platform/windows/detect.py105
-rw-r--r--platform/windows/display_server_windows.cpp275
-rw-r--r--platform/windows/display_server_windows.h8
-rw-r--r--platform/windows/export/export.cpp2
-rw-r--r--platform/windows/gl_manager_windows.cpp7
-rw-r--r--platform/windows/godot.natvis84
-rw-r--r--platform/windows/godot_windows.cpp4
-rw-r--r--platform/windows/os_windows.cpp79
-rw-r--r--platform/windows/os_windows.h9
119 files changed, 2388 insertions, 2486 deletions
diff --git a/platform/android/SCsub b/platform/android/SCsub
index 344ca036de..e4d04f1df9 100644
--- a/platform/android/SCsub
+++ b/platform/android/SCsub
@@ -53,14 +53,14 @@ else:
print("WARN: Architecture not suitable for embedding into APK; keeping .so at \\bin")
if lib_arch_dir != "":
- if env["target"] == "release":
- lib_type_dir = "release"
- elif env["target"] == "release_debug":
- lib_type_dir = "debug"
- else: # debug
+ if env.dev_build:
lib_type_dir = "dev"
+ elif env.debug_features:
+ lib_type_dir = "debug"
+ else: # Release
+ lib_type_dir = "release"
- if env["tools"]:
+ if env.editor_build:
lib_tools_dir = "tools/"
else:
lib_tools_dir = ""
diff --git a/platform/android/android_input_handler.cpp b/platform/android/android_input_handler.cpp
index 6427346365..454bcd2eda 100644
--- a/platform/android/android_input_handler.cpp
+++ b/platform/android/android_input_handler.cpp
@@ -118,20 +118,31 @@ void AndroidInputHandler::process_key_event(int p_keycode, int p_physical_keycod
Input::get_singleton()->parse_input_event(ev);
}
-void AndroidInputHandler::process_touch(int p_event, int p_pointer, const Vector<AndroidInputHandler::TouchPos> &p_points) {
+void AndroidInputHandler::_parse_all_touch(bool p_pressed) {
+ if (touch.size()) {
+ //end all if exist
+ for (int i = 0; i < touch.size(); i++) {
+ Ref<InputEventScreenTouch> ev;
+ ev.instantiate();
+ ev->set_index(touch[i].id);
+ ev->set_pressed(p_pressed);
+ ev->set_position(touch[i].pos);
+ Input::get_singleton()->parse_input_event(ev);
+ }
+ }
+}
+
+void AndroidInputHandler::_release_all_touch() {
+ _parse_all_touch(false);
+ touch.clear();
+}
+
+void AndroidInputHandler::process_touch_event(int p_event, int p_pointer, const Vector<TouchPos> &p_points) {
switch (p_event) {
case AMOTION_EVENT_ACTION_DOWN: { //gesture begin
- if (touch.size()) {
- //end all if exist
- for (int i = 0; i < touch.size(); i++) {
- Ref<InputEventScreenTouch> ev;
- ev.instantiate();
- ev->set_index(touch[i].id);
- ev->set_pressed(false);
- ev->set_position(touch[i].pos);
- Input::get_singleton()->parse_input_event(ev);
- }
- }
+ // Release any remaining touches or mouse event
+ _release_mouse_event_info();
+ _release_all_touch();
touch.resize(p_points.size());
for (int i = 0; i < p_points.size(); i++) {
@@ -140,18 +151,13 @@ void AndroidInputHandler::process_touch(int p_event, int p_pointer, const Vector
}
//send touch
- for (int i = 0; i < touch.size(); i++) {
- Ref<InputEventScreenTouch> ev;
- ev.instantiate();
- ev->set_index(touch[i].id);
- ev->set_pressed(true);
- ev->set_position(touch[i].pos);
- Input::get_singleton()->parse_input_event(ev);
- }
+ _parse_all_touch(true);
} break;
case AMOTION_EVENT_ACTION_MOVE: { //motion
- ERR_FAIL_COND(touch.size() != p_points.size());
+ if (touch.size() != p_points.size()) {
+ return;
+ }
for (int i = 0; i < touch.size(); i++) {
int idx = -1;
@@ -180,18 +186,7 @@ void AndroidInputHandler::process_touch(int p_event, int p_pointer, const Vector
} break;
case AMOTION_EVENT_ACTION_CANCEL:
case AMOTION_EVENT_ACTION_UP: { //release
- if (touch.size()) {
- //end all if exist
- for (int i = 0; i < touch.size(); i++) {
- Ref<InputEventScreenTouch> ev;
- ev.instantiate();
- ev->set_index(touch[i].id);
- ev->set_pressed(false);
- ev->set_position(touch[i].pos);
- Input::get_singleton()->parse_input_event(ev);
- }
- touch.clear();
- }
+ _release_all_touch();
} break;
case AMOTION_EVENT_ACTION_POINTER_DOWN: { // add touch
for (int i = 0; i < p_points.size(); i++) {
@@ -229,88 +224,118 @@ void AndroidInputHandler::process_touch(int p_event, int p_pointer, const Vector
}
}
-void AndroidInputHandler::process_hover(int p_type, Point2 p_pos) {
- // https://developer.android.com/reference/android/view/MotionEvent.html#ACTION_HOVER_ENTER
- switch (p_type) {
+void AndroidInputHandler::_parse_mouse_event_info(MouseButton event_buttons_mask, bool p_pressed, bool p_double_click, bool p_source_mouse_relative) {
+ if (!mouse_event_info.valid) {
+ return;
+ }
+
+ Ref<InputEventMouseButton> ev;
+ ev.instantiate();
+ _set_key_modifier_state(ev);
+ if (p_source_mouse_relative) {
+ ev->set_position(hover_prev_pos);
+ ev->set_global_position(hover_prev_pos);
+ } else {
+ ev->set_position(mouse_event_info.pos);
+ ev->set_global_position(mouse_event_info.pos);
+ hover_prev_pos = mouse_event_info.pos;
+ }
+ ev->set_pressed(p_pressed);
+ MouseButton changed_button_mask = MouseButton(buttons_state ^ event_buttons_mask);
+
+ buttons_state = event_buttons_mask;
+
+ ev->set_button_index(_button_index_from_mask(changed_button_mask));
+ ev->set_button_mask(event_buttons_mask);
+ ev->set_double_click(p_double_click);
+ Input::get_singleton()->parse_input_event(ev);
+}
+
+void AndroidInputHandler::_release_mouse_event_info(bool p_source_mouse_relative) {
+ _parse_mouse_event_info(MouseButton::NONE, false, false, p_source_mouse_relative);
+ mouse_event_info.valid = false;
+}
+
+void AndroidInputHandler::process_mouse_event(int p_event_action, int p_event_android_buttons_mask, Point2 p_event_pos, Vector2 p_delta, bool p_double_click, bool p_source_mouse_relative) {
+ MouseButton event_buttons_mask = _android_button_mask_to_godot_button_mask(p_event_android_buttons_mask);
+ switch (p_event_action) {
case AMOTION_EVENT_ACTION_HOVER_MOVE: // hover move
case AMOTION_EVENT_ACTION_HOVER_ENTER: // hover enter
case AMOTION_EVENT_ACTION_HOVER_EXIT: { // hover exit
+ // https://developer.android.com/reference/android/view/MotionEvent.html#ACTION_HOVER_ENTER
Ref<InputEventMouseMotion> ev;
ev.instantiate();
_set_key_modifier_state(ev);
- ev->set_position(p_pos);
- ev->set_global_position(p_pos);
- ev->set_relative(p_pos - hover_prev_pos);
+ ev->set_position(p_event_pos);
+ ev->set_global_position(p_event_pos);
+ ev->set_relative(p_event_pos - hover_prev_pos);
Input::get_singleton()->parse_input_event(ev);
- hover_prev_pos = p_pos;
+ hover_prev_pos = p_event_pos;
} break;
- }
-}
-void AndroidInputHandler::process_mouse_event(int input_device, int event_action, int event_android_buttons_mask, Point2 event_pos, float event_vertical_factor, float event_horizontal_factor) {
- MouseButton event_buttons_mask = _android_button_mask_to_godot_button_mask(event_android_buttons_mask);
- switch (event_action) {
- case AMOTION_EVENT_ACTION_BUTTON_PRESS:
- case AMOTION_EVENT_ACTION_BUTTON_RELEASE: {
- Ref<InputEventMouseButton> ev;
- ev.instantiate();
- _set_key_modifier_state(ev);
- if ((input_device & AINPUT_SOURCE_MOUSE) == AINPUT_SOURCE_MOUSE) {
- ev->set_position(event_pos);
- ev->set_global_position(event_pos);
- } else {
- ev->set_position(hover_prev_pos);
- ev->set_global_position(hover_prev_pos);
- }
- ev->set_pressed(event_action == AMOTION_EVENT_ACTION_BUTTON_PRESS);
- MouseButton changed_button_mask = MouseButton(buttons_state ^ event_buttons_mask);
+ case AMOTION_EVENT_ACTION_DOWN:
+ case AMOTION_EVENT_ACTION_BUTTON_PRESS: {
+ // Release any remaining touches or mouse event
+ _release_mouse_event_info();
+ _release_all_touch();
- buttons_state = event_buttons_mask;
+ mouse_event_info.valid = true;
+ mouse_event_info.pos = p_event_pos;
+ _parse_mouse_event_info(event_buttons_mask, true, p_double_click, p_source_mouse_relative);
+ } break;
- ev->set_button_index(_button_index_from_mask(changed_button_mask));
- ev->set_button_mask(event_buttons_mask);
- Input::get_singleton()->parse_input_event(ev);
+ case AMOTION_EVENT_ACTION_UP:
+ case AMOTION_EVENT_ACTION_CANCEL:
+ case AMOTION_EVENT_ACTION_BUTTON_RELEASE: {
+ _release_mouse_event_info(p_source_mouse_relative);
} break;
case AMOTION_EVENT_ACTION_MOVE: {
+ if (!mouse_event_info.valid) {
+ return;
+ }
+
Ref<InputEventMouseMotion> ev;
ev.instantiate();
_set_key_modifier_state(ev);
- if ((input_device & AINPUT_SOURCE_MOUSE) == AINPUT_SOURCE_MOUSE) {
- ev->set_position(event_pos);
- ev->set_global_position(event_pos);
- ev->set_relative(event_pos - hover_prev_pos);
- hover_prev_pos = event_pos;
- } else {
+ if (p_source_mouse_relative) {
ev->set_position(hover_prev_pos);
ev->set_global_position(hover_prev_pos);
- ev->set_relative(event_pos);
+ ev->set_relative(p_event_pos);
+ } else {
+ ev->set_position(p_event_pos);
+ ev->set_global_position(p_event_pos);
+ ev->set_relative(p_event_pos - hover_prev_pos);
+ mouse_event_info.pos = p_event_pos;
+ hover_prev_pos = p_event_pos;
}
ev->set_button_mask(event_buttons_mask);
Input::get_singleton()->parse_input_event(ev);
} break;
+
case AMOTION_EVENT_ACTION_SCROLL: {
Ref<InputEventMouseButton> ev;
ev.instantiate();
- if ((input_device & AINPUT_SOURCE_MOUSE) == AINPUT_SOURCE_MOUSE) {
- ev->set_position(event_pos);
- ev->set_global_position(event_pos);
- } else {
+ _set_key_modifier_state(ev);
+ if (p_source_mouse_relative) {
ev->set_position(hover_prev_pos);
ev->set_global_position(hover_prev_pos);
+ } else {
+ ev->set_position(p_event_pos);
+ ev->set_global_position(p_event_pos);
}
ev->set_pressed(true);
buttons_state = event_buttons_mask;
- if (event_vertical_factor > 0) {
- _wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_UP, event_vertical_factor);
- } else if (event_vertical_factor < 0) {
- _wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_DOWN, -event_vertical_factor);
+ if (p_delta.y > 0) {
+ _wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_UP, p_delta.y);
+ } else if (p_delta.y < 0) {
+ _wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_DOWN, -p_delta.y);
}
- if (event_horizontal_factor > 0) {
- _wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_RIGHT, event_horizontal_factor);
- } else if (event_horizontal_factor < 0) {
- _wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_LEFT, -event_horizontal_factor);
+ if (p_delta.x > 0) {
+ _wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_RIGHT, p_delta.x);
+ } else if (p_delta.x < 0) {
+ _wheel_button_click(event_buttons_mask, ev, MouseButton::WHEEL_LEFT, -p_delta.x);
}
} break;
}
@@ -329,18 +354,22 @@ void AndroidInputHandler::_wheel_button_click(MouseButton event_buttons_mask, co
Input::get_singleton()->parse_input_event(evdd);
}
-void AndroidInputHandler::process_double_tap(int event_android_button_mask, Point2 p_pos) {
- MouseButton event_button_mask = _android_button_mask_to_godot_button_mask(event_android_button_mask);
- Ref<InputEventMouseButton> ev;
- ev.instantiate();
- _set_key_modifier_state(ev);
- ev->set_position(p_pos);
- ev->set_global_position(p_pos);
- ev->set_pressed(event_button_mask != MouseButton::NONE);
- ev->set_button_index(_button_index_from_mask(event_button_mask));
- ev->set_button_mask(event_button_mask);
- ev->set_double_click(true);
- Input::get_singleton()->parse_input_event(ev);
+void AndroidInputHandler::process_magnify(Point2 p_pos, float p_factor) {
+ Ref<InputEventMagnifyGesture> magnify_event;
+ magnify_event.instantiate();
+ _set_key_modifier_state(magnify_event);
+ magnify_event->set_position(p_pos);
+ magnify_event->set_factor(p_factor);
+ Input::get_singleton()->parse_input_event(magnify_event);
+}
+
+void AndroidInputHandler::process_pan(Point2 p_pos, Vector2 p_delta) {
+ Ref<InputEventPanGesture> pan_event;
+ pan_event.instantiate();
+ _set_key_modifier_state(pan_event);
+ pan_event->set_position(p_pos);
+ pan_event->set_delta(p_delta);
+ Input::get_singleton()->parse_input_event(pan_event);
}
MouseButton AndroidInputHandler::_button_index_from_mask(MouseButton button_mask) {
diff --git a/platform/android/android_input_handler.h b/platform/android/android_input_handler.h
index 6dfab7def8..88490f0407 100644
--- a/platform/android/android_input_handler.h
+++ b/platform/android/android_input_handler.h
@@ -44,6 +44,11 @@ public:
Point2 pos;
};
+ struct MouseEventInfo {
+ bool valid = false;
+ Point2 pos;
+ };
+
enum {
JOY_EVENT_BUTTON = 0,
JOY_EVENT_AXIS = 1,
@@ -68,6 +73,7 @@ private:
MouseButton buttons_state = MouseButton::NONE;
Vector<TouchPos> touch;
+ MouseEventInfo mouse_event_info;
Point2 hover_prev_pos; // needed to calculate the relative position on hover events
void _set_key_modifier_state(Ref<InputEventWithModifiers> ev);
@@ -77,11 +83,19 @@ private:
void _wheel_button_click(MouseButton event_buttons_mask, const Ref<InputEventMouseButton> &ev, MouseButton wheel_button, float factor);
+ void _parse_mouse_event_info(MouseButton event_buttons_mask, bool p_pressed, bool p_double_click, bool p_source_mouse_relative);
+
+ void _release_mouse_event_info(bool p_source_mouse_relative = false);
+
+ void _parse_all_touch(bool p_pressed);
+
+ void _release_all_touch();
+
public:
- void process_touch(int p_event, int p_pointer, const Vector<TouchPos> &p_points);
- void process_hover(int p_type, Point2 p_pos);
- void process_mouse_event(int input_device, int event_action, int event_android_buttons_mask, Point2 event_pos, float event_vertical_factor = 0, float event_horizontal_factor = 0);
- void process_double_tap(int event_android_button_mask, Point2 p_pos);
+ void process_mouse_event(int p_event_action, int p_event_android_buttons_mask, Point2 p_event_pos, Vector2 p_delta, bool p_double_click, bool p_source_mouse_relative);
+ void process_touch_event(int p_event, int p_pointer, const Vector<TouchPos> &p_points);
+ void process_magnify(Point2 p_pos, float p_factor);
+ void process_pan(Point2 p_pos, Vector2 p_delta);
void process_joy_event(JoypadEvent p_event);
void process_key_event(int p_keycode, int p_physical_keycode, int p_unicode, bool p_pressed);
};
diff --git a/platform/android/detect.py b/platform/android/detect.py
index ad63821162..6eb8ba34ed 100644
--- a/platform/android/detect.py
+++ b/platform/android/detect.py
@@ -3,6 +3,11 @@ import sys
import platform
import subprocess
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from SCons import Environment
+
def is_active():
return True
@@ -17,8 +22,6 @@ def can_build():
def get_opts():
- from SCons.Variables import BoolVariable, EnumVariable
-
return [
("ANDROID_SDK_ROOT", "Path to the Android SDK", get_env_android_sdk_root()),
("ndk_platform", 'Target platform (android-<api>, e.g. "android-24")', "android-24"),
@@ -46,7 +49,7 @@ def get_ndk_version():
def get_flags():
return [
("arch", "arm64"), # Default for convenience.
- ("tools", False),
+ ("target", "template_debug"),
]
@@ -74,7 +77,7 @@ def install_ndk_if_needed(env):
env["ANDROID_NDK_ROOT"] = get_android_ndk_root(env)
-def configure(env):
+def configure(env: "Environment"):
# Validate arch.
supported_arches = ["x86_32", "x86_64", "arm32", "arm64"]
if env["arch"] not in supported_arches:
@@ -114,23 +117,18 @@ def configure(env):
env.Append(CCFLAGS=target_option)
env.Append(LINKFLAGS=target_option)
- # Build type
-
- if env["target"].startswith("release"):
- if env["optimize"] == "speed": # optimize for speed (default)
- # `-O2` is more friendly to debuggers than `-O3`, leading to better crash backtraces
- # when using `target=release_debug`.
- opt = "-O3" if env["target"] == "release" else "-O2"
- env.Append(CCFLAGS=[opt, "-fomit-frame-pointer"])
- elif env["optimize"] == "size": # optimize for size
- env.Append(CCFLAGS=["-Oz"])
- env.Append(CPPDEFINES=["NDEBUG"])
- env.Append(CCFLAGS=["-ftree-vectorize"])
- elif env["target"] == "debug":
- env.Append(LINKFLAGS=["-O0"])
- env.Append(CCFLAGS=["-O0", "-g", "-fno-limit-debug-info"])
- env.Append(CPPDEFINES=["_DEBUG"])
- env.Append(CPPFLAGS=["-UNDEBUG"])
+ # LTO
+
+ if env["lto"] == "auto": # LTO benefits for Android (size, performance) haven't been clearly established yet.
+ env["lto"] = "none"
+
+ if env["lto"] != "none":
+ if env["lto"] == "thin":
+ env.Append(CCFLAGS=["-flto=thin"])
+ env.Append(LINKFLAGS=["-flto=thin"])
+ else:
+ env.Append(CCFLAGS=["-flto"])
+ env.Append(LINKFLAGS=["-flto"])
# Compiler configuration
@@ -158,22 +156,16 @@ def configure(env):
env["RANLIB"] = compiler_path + "/llvm-ranlib"
env["AS"] = compiler_path + "/clang"
- # Disable exceptions and rtti on non-tools (template) builds
- if env["tools"]:
- env.Append(CXXFLAGS=["-frtti"])
- elif env["builtin_icu"]:
- env.Append(CXXFLAGS=["-frtti", "-fno-exceptions"])
- else:
- env.Append(CXXFLAGS=["-fno-rtti", "-fno-exceptions"])
- # Don't use dynamic_cast, necessary with no-rtti.
- env.Append(CPPDEFINES=["NO_SAFE_CAST"])
+ # Disable exceptions on template builds
+ if not env.editor_build:
+ env.Append(CXXFLAGS=["-fno-exceptions"])
env.Append(
CCFLAGS=(
"-fpic -ffunction-sections -funwind-tables -fstack-protector-strong -fvisibility=hidden -fno-strict-aliasing".split()
)
)
- env.Append(CPPDEFINES=["NO_STATVFS", "GLES_ENABLED"])
+ env.Append(CPPDEFINES=["GLES_ENABLED"])
if get_min_sdk_version(env["ndk_platform"]) >= 24:
env.Append(CPPDEFINES=[("_FILE_OFFSET_BITS", 64)])
@@ -195,7 +187,7 @@ def configure(env):
env.Append(LINKFLAGS="-Wl,-soname,libgodot_android.so")
env.Prepend(CPPPATH=["#platform/android"])
- env.Append(CPPDEFINES=["ANDROID_ENABLED", "UNIX_ENABLED", "NO_FCNTL"])
+ env.Append(CPPDEFINES=["ANDROID_ENABLED", "UNIX_ENABLED"])
env.Append(LIBS=["OpenSLES", "EGL", "GLESv2", "android", "log", "z", "dl"])
if env["vulkan"]:
diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp
index d3bce12de1..059c296cb7 100644
--- a/platform/android/display_server_android.cpp
+++ b/platform/android/display_server_android.cpp
@@ -580,6 +580,9 @@ void DisplayServerAndroid::process_gyroscope(const Vector3 &p_gyroscope) {
}
void DisplayServerAndroid::mouse_set_mode(MouseMode p_mode) {
+ if (!OS_Android::get_singleton()->get_godot_java()->get_godot_view()->can_update_pointer_icon() || !OS_Android::get_singleton()->get_godot_java()->get_godot_view()->can_capture_pointer()) {
+ return;
+ }
if (mouse_mode == p_mode) {
return;
}
@@ -612,6 +615,9 @@ MouseButton DisplayServerAndroid::mouse_get_button_state() const {
}
void DisplayServerAndroid::cursor_set_shape(DisplayServer::CursorShape p_shape) {
+ if (!OS_Android::get_singleton()->get_godot_java()->get_godot_view()->can_update_pointer_icon()) {
+ return;
+ }
if (cursor_shape == p_shape) {
return;
}
diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp
index 5bbe0ffab6..f4c4e985fe 100644
--- a/platform/android/export/export.cpp
+++ b/platform/android/export/export.cpp
@@ -36,6 +36,7 @@
#include "export_plugin.h"
void register_android_exporter() {
+#ifndef ANDROID_ENABLED
EDITOR_DEF("export/android/android_sdk_path", "");
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/android_sdk_path", PROPERTY_HINT_GLOBAL_DIR));
EDITOR_DEF("export/android/debug_keystore", "");
@@ -47,6 +48,7 @@ void register_android_exporter() {
EDITOR_DEF("export/android/shutdown_adb_on_exit", true);
EDITOR_DEF("export/android/one_click_deploy_clear_previous_install", false);
+#endif
Ref<EditorExportPlatformAndroid> exporter = Ref<EditorExportPlatformAndroid>(memnew(EditorExportPlatformAndroid));
EditorExport::get_singleton()->add_export_platform(exporter);
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index 0f8ef3f7d6..366bd1c48c 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -569,16 +569,15 @@ bool EditorExportPlatformAndroid::_should_compress_asset(const String &p_path, c
}
zip_fileinfo EditorExportPlatformAndroid::get_zip_fileinfo() {
- OS::Time time = OS::get_singleton()->get_time();
- OS::Date date = OS::get_singleton()->get_date();
+ OS::DateTime dt = OS::get_singleton()->get_datetime();
zip_fileinfo zipfi;
- zipfi.tmz_date.tm_hour = time.hour;
- zipfi.tmz_date.tm_mday = date.day;
- zipfi.tmz_date.tm_min = time.minute;
- zipfi.tmz_date.tm_mon = date.month - 1; // tm_mon is zero indexed
- zipfi.tmz_date.tm_sec = time.second;
- zipfi.tmz_date.tm_year = date.year;
+ zipfi.tmz_date.tm_year = dt.year;
+ zipfi.tmz_date.tm_mon = dt.month - 1; // tm_mon is zero indexed
+ zipfi.tmz_date.tm_mday = dt.day;
+ zipfi.tmz_date.tm_hour = dt.hour;
+ zipfi.tmz_date.tm_min = dt.minute;
+ zipfi.tmz_date.tm_sec = dt.second;
zipfi.dosDate = 0;
zipfi.external_fa = 0;
zipfi.internal_fa = 0;
@@ -1670,14 +1669,7 @@ Vector<String> EditorExportPlatformAndroid::get_enabled_abis(const Ref<EditorExp
}
void EditorExportPlatformAndroid::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const {
- String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name");
- if (driver == "opengl3") {
- r_features->push_back("etc");
- }
- // FIXME: Review what texture formats are used for Vulkan.
- if (driver == "vulkan") {
- r_features->push_back("etc2");
- }
+ r_features->push_back("etc2");
Vector<String> abis = get_enabled_abis(p_preset);
for (int i = 0; i < abis.size(); ++i) {
diff --git a/platform/android/export/gradle_export_util.cpp b/platform/android/export/gradle_export_util.cpp
index 8d370a31a4..2f53942f76 100644
--- a/platform/android/export/gradle_export_util.cpp
+++ b/platform/android/export/gradle_export_util.cpp
@@ -189,9 +189,7 @@ String bool_to_string(bool v) {
}
String _get_gles_tag() {
- bool min_gles3 = ProjectSettings::get_singleton()->get("rendering/driver/driver_name") == "GLES3" &&
- !ProjectSettings::get_singleton()->get("rendering/driver/fallback_to_gles2");
- return min_gles3 ? " <uses-feature android:glEsVersion=\"0x00030000\" android:required=\"true\" />\n" : "";
+ return " <uses-feature android:glEsVersion=\"0x00030000\" android:required=\"true\" />\n";
}
String _get_screen_sizes_tag(const Ref<EditorExportPreset> &p_preset) {
diff --git a/platform/android/file_access_android.cpp b/platform/android/file_access_android.cpp
index ace7636e6c..d6cd62e9f5 100644
--- a/platform/android/file_access_android.cpp
+++ b/platform/android/file_access_android.cpp
@@ -42,7 +42,7 @@ String FileAccessAndroid::get_path_absolute() const {
return absolute_path;
}
-Error FileAccessAndroid::_open(const String &p_path, int p_mode_flags) {
+Error FileAccessAndroid::open_internal(const String &p_path, int p_mode_flags) {
_close();
path_src = p_path;
diff --git a/platform/android/file_access_android.h b/platform/android/file_access_android.h
index 8d7ade8ead..55f8fbe0f4 100644
--- a/platform/android/file_access_android.h
+++ b/platform/android/file_access_android.h
@@ -49,7 +49,7 @@ class FileAccessAndroid : public FileAccess {
public:
static AAssetManager *asset_manager;
- virtual Error _open(const String &p_path, int p_mode_flags) override; // open a file
+ virtual Error open_internal(const String &p_path, int p_mode_flags) override; // open a file
virtual bool is_open() const override; // true when file is open
/// returns the path for the current open file
diff --git a/platform/android/file_access_filesystem_jandroid.cpp b/platform/android/file_access_filesystem_jandroid.cpp
index 56561cb616..c2ee3389ae 100644
--- a/platform/android/file_access_filesystem_jandroid.cpp
+++ b/platform/android/file_access_filesystem_jandroid.cpp
@@ -61,7 +61,7 @@ String FileAccessFilesystemJAndroid::get_path_absolute() const {
return absolute_path;
}
-Error FileAccessFilesystemJAndroid::_open(const String &p_path, int p_mode_flags) {
+Error FileAccessFilesystemJAndroid::open_internal(const String &p_path, int p_mode_flags) {
if (is_open()) {
_close();
}
diff --git a/platform/android/file_access_filesystem_jandroid.h b/platform/android/file_access_filesystem_jandroid.h
index 76d7db6e3a..815ab36516 100644
--- a/platform/android/file_access_filesystem_jandroid.h
+++ b/platform/android/file_access_filesystem_jandroid.h
@@ -60,7 +60,7 @@ class FileAccessFilesystemJAndroid : public FileAccess {
void _set_eof(bool eof);
public:
- virtual Error _open(const String &p_path, int p_mode_flags) override; ///< open a file
+ virtual Error open_internal(const String &p_path, int p_mode_flags) override; ///< open a file
virtual bool is_open() const override; ///< true when file is open
/// returns the path for the current open file
diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle
index fbd97fae0b..0346625e4b 100644
--- a/platform/android/java/app/config.gradle
+++ b/platform/android/java/app/config.gradle
@@ -127,16 +127,36 @@ ext.generateGodotLibraryVersion = { List<String> requiredKeys ->
if (requiredKeys.empty) {
libraryVersionName = map.values().join(".")
try {
+ if (map.containsKey("status")) {
+ int statusCode = 0
+ String statusValue = map["status"]
+ if (statusValue == null) {
+ statusCode = 0
+ } else if (statusValue.startsWith("alpha")) {
+ statusCode = 1
+ } else if (statusValue.startsWith("beta")) {
+ statusCode = 2
+ } else if (statusValue.startsWith("rc")) {
+ statusCode = 3
+ } else if (statusValue.startsWith("stable")) {
+ statusCode = 4
+ } else {
+ statusCode = 0
+ }
+
+ libraryVersionCode = statusCode
+ }
+
if (map.containsKey("patch")) {
- libraryVersionCode = Integer.parseInt(map["patch"])
+ libraryVersionCode += Integer.parseInt(map["patch"]) * 10
}
if (map.containsKey("minor")) {
- libraryVersionCode += (Integer.parseInt(map["minor"]) * 100)
+ libraryVersionCode += (Integer.parseInt(map["minor"]) * 1000)
}
if (map.containsKey("major")) {
- libraryVersionCode += (Integer.parseInt(map["major"]) * 10000)
+ libraryVersionCode += (Integer.parseInt(map["major"]) * 100000)
}
} catch (NumberFormatException ignore) {
libraryVersionCode = 1
diff --git a/platform/android/java/build.gradle b/platform/android/java/build.gradle
index 81c7130c03..05608883d7 100644
--- a/platform/android/java/build.gradle
+++ b/platform/android/java/build.gradle
@@ -29,8 +29,13 @@ allprojects {
ext {
supportedAbis = ["arm32", "arm64", "x86_32", "x86_64"]
- supportedTargetsMap = [release: "release", dev: "debug", debug: "release_debug"]
supportedFlavors = ["editor", "template"]
+ supportedFlavorsBuildTypes = [
+ // The editor can't be used with target=release as debugging tools are then not
+ // included, and it would crash on errors instead of reporting them.
+ "editor": ["dev", "debug"],
+ "template": ["dev", "debug", "release"]
+ ]
// Used by gradle to specify which architecture to build for by default when running
// `./gradlew build` (this command is usually used by Android Studio).
@@ -88,7 +93,7 @@ task copyDebugAARToAppModule(type: Copy) {
dependsOn ':lib:assembleTemplateDebug'
from('lib/build/outputs/aar')
into('app/libs/debug')
- include('godot-lib.debug.aar')
+ include('godot-lib.template_debug.aar')
}
/**
@@ -99,7 +104,7 @@ task copyDebugAARToBin(type: Copy) {
dependsOn ':lib:assembleTemplateDebug'
from('lib/build/outputs/aar')
into(binDir)
- include('godot-lib.debug.aar')
+ include('godot-lib.template_debug.aar')
}
/**
@@ -110,7 +115,7 @@ task copyDevAARToAppModule(type: Copy) {
dependsOn ':lib:assembleTemplateDev'
from('lib/build/outputs/aar')
into('app/libs/dev')
- include('godot-lib.dev.aar')
+ include('godot-lib.template_debug.dev.aar')
}
/**
@@ -121,7 +126,7 @@ task copyDevAARToBin(type: Copy) {
dependsOn ':lib:assembleTemplateDev'
from('lib/build/outputs/aar')
into(binDir)
- include('godot-lib.dev.aar')
+ include('godot-lib.template_debug.dev.aar')
}
/**
@@ -132,7 +137,7 @@ task copyReleaseAARToAppModule(type: Copy) {
dependsOn ':lib:assembleTemplateRelease'
from('lib/build/outputs/aar')
into('app/libs/release')
- include('godot-lib.release.aar')
+ include('godot-lib.template_release.aar')
}
/**
@@ -143,7 +148,7 @@ task copyReleaseAARToBin(type: Copy) {
dependsOn ':lib:assembleTemplateRelease'
from('lib/build/outputs/aar')
into(binDir)
- include('godot-lib.release.aar')
+ include('godot-lib.template_release.aar')
}
/**
@@ -168,13 +173,8 @@ def templateExcludedBuildTask() {
if (!isAndroidStudio()) {
logger.lifecycle("Excluding Android studio build tasks")
for (String flavor : supportedFlavors) {
- for (String buildType : supportedTargetsMap.keySet()) {
- if (buildType == "release" && flavor == "editor") {
- // The editor can't be used with target=release as debugging tools are then not
- // included, and it would crash on errors instead of reporting them.
- continue
- }
-
+ String[] supportedBuildTypes = supportedFlavorsBuildTypes[flavor]
+ for (String buildType : supportedBuildTypes) {
for (String abi : selectedAbis) {
excludedTasks += ":lib:" + getSconsTaskName(flavor, buildType, abi)
}
@@ -188,7 +188,7 @@ def templateBuildTasks() {
def tasks = []
// Only build the apks and aar files for which we have native shared libraries.
- for (String target : supportedTargetsMap.keySet()) {
+ for (String target : supportedFlavorsBuildTypes["template"]) {
File targetLibs = new File("lib/libs/" + target)
if (targetLibs != null
&& targetLibs.isDirectory()
@@ -240,12 +240,7 @@ task generateGodotEditor {
def tasks = []
- for (String target : supportedTargetsMap.keySet()) {
- if (target == "release") {
- // The editor can't be used with target=release as debugging tools are then not
- // included, and it would crash on errors instead of reporting them.
- continue
- }
+ for (String target : supportedFlavorsBuildTypes["editor"]) {
File targetLibs = new File("lib/libs/tools/" + target)
if (targetLibs != null
&& targetLibs.isDirectory()
@@ -322,9 +317,9 @@ task cleanGodotTemplates(type: Delete) {
delete("$binDir/android_dev.apk")
delete("$binDir/android_release.apk")
delete("$binDir/android_source.zip")
- delete("$binDir/godot-lib.debug.aar")
- delete("$binDir/godot-lib.dev.aar")
- delete("$binDir/godot-lib.release.aar")
+ delete("$binDir/godot-lib.template_debug.aar")
+ delete("$binDir/godot-lib.template_debug.dev.aar")
+ delete("$binDir/godot-lib.template_release.aar")
finalizedBy getTasksByName("clean", true)
}
diff --git a/platform/android/java/editor/build.gradle b/platform/android/java/editor/build.gradle
index 729966ee69..9152492e9d 100644
--- a/platform/android/java/editor/build.gradle
+++ b/platform/android/java/editor/build.gradle
@@ -12,6 +12,25 @@ dependencies {
implementation "androidx.window:window:1.0.0"
}
+ext {
+ // Build number added as a suffix to the version code, and incremented for each build/upload to
+ // the Google Play store.
+ // This should be reset on each stable release of Godot.
+ editorBuildNumber = 0
+ // Value by which the Godot version code should be offset by to make room for the build number
+ editorBuildNumberOffset = 100
+}
+
+def generateVersionCode() {
+ int libraryVersionCode = getGodotLibraryVersionCode()
+ return (libraryVersionCode * editorBuildNumberOffset) + editorBuildNumber
+}
+
+def generateVersionName() {
+ String libraryVersionName = getGodotLibraryVersionName()
+ return libraryVersionName + ".$editorBuildNumber"
+}
+
android {
compileSdkVersion versions.compileSdk
buildToolsVersion versions.buildTools
@@ -20,8 +39,8 @@ android {
defaultConfig {
// The 'applicationId' suffix allows to install Godot 3.x(v3) and 4.x(v4) on the same device
applicationId "org.godotengine.editor.v4"
- versionCode getGodotLibraryVersionCode()
- versionName getGodotLibraryVersionName()
+ versionCode generateVersionCode()
+ versionName generateVersionName()
minSdkVersion versions.minSdk
targetSdkVersion versions.targetSdk
diff --git a/platform/android/java/editor/src/main/AndroidManifest.xml b/platform/android/java/editor/src/main/AndroidManifest.xml
index abf506a83c..6aa5f06f31 100644
--- a/platform/android/java/editor/src/main/AndroidManifest.xml
+++ b/platform/android/java/editor/src/main/AndroidManifest.xml
@@ -7,7 +7,7 @@
<supports-screens
android:largeScreens="true"
android:normalScreens="true"
- android:smallScreens="true"
+ android:smallScreens="false"
android:xlargeScreens="true" />
<uses-feature
diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt
index 740f3f48d3..489a81fc1a 100644
--- a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt
+++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt
@@ -77,6 +77,12 @@ open class GodotEditor : FullScreenGodotApp() {
}
super.onCreate(savedInstanceState)
+
+ // Enable long press, panning and scaling gestures
+ godotFragment?.renderView?.inputHandler?.apply {
+ enableLongPress(enableLongPressGestures())
+ enablePanningAndScalingGestures(enablePanAndScaleGestures())
+ }
}
private fun updateCommandLineParams(args: Array<String>?) {
@@ -148,6 +154,16 @@ open class GodotEditor : FullScreenGodotApp() {
*/
protected open fun overrideOrientationRequest() = true
+ /**
+ * Enable long press gestures for the Godot Android editor.
+ */
+ protected open fun enableLongPressGestures() = true
+
+ /**
+ * Enable pan and scale gestures for the Godot Android editor.
+ */
+ protected open fun enablePanAndScaleGestures() = true
+
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
// Check if we got the MANAGE_EXTERNAL_STORAGE permission
diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt
index 783095f93a..b9536a7066 100644
--- a/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt
+++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt
@@ -35,4 +35,8 @@ package org.godotengine.editor
*/
class GodotGame : GodotEditor() {
override fun overrideOrientationRequest() = false
+
+ override fun enableLongPressGestures() = false
+
+ override fun enablePanAndScaleGestures() = false
}
diff --git a/platform/android/java/lib/build.gradle b/platform/android/java/lib/build.gradle
index 318ae1143f..c9e2a5d7d2 100644
--- a/platform/android/java/lib/build.gradle
+++ b/platform/android/java/lib/build.gradle
@@ -100,25 +100,34 @@ android {
throw new GradleException("Invalid product flavor: $flavorName")
}
- boolean toolsFlag = flavorName == "editor"
-
def buildType = variant.buildType.name
- if (buildType == null || buildType == "" || !supportedTargetsMap.containsKey(buildType)) {
+ if (buildType == null || buildType == "" || !supportedFlavorsBuildTypes[flavorName].contains(buildType)) {
throw new GradleException("Invalid build type: $buildType")
}
- def sconsTarget = supportedTargetsMap[buildType]
- if (sconsTarget == null || sconsTarget == "") {
- throw new GradleException("Invalid scons target: $sconsTarget")
+ boolean devBuild = buildType == "dev"
+
+ def sconsTarget = flavorName
+ if (sconsTarget == "template") {
+ switch (buildType) {
+ case "release":
+ sconsTarget += "_release"
+ break
+ case "debug":
+ case "dev":
+ default:
+ sconsTarget += "_debug"
+ break;
+ }
}
// Update the name of the generated library
- def outputSuffix = "${buildType}.aar"
- if (toolsFlag) {
- outputSuffix = "tools.$outputSuffix"
+ def outputSuffix = "${sconsTarget}"
+ if (devBuild) {
+ outputSuffix = "${outputSuffix}.dev"
}
variant.outputs.all { output ->
- output.outputFileName = "godot-lib.${outputSuffix}"
+ output.outputFileName = "godot-lib.${outputSuffix}.aar"
}
// Find scons' executable path
@@ -159,7 +168,7 @@ android {
def taskName = getSconsTaskName(flavorName, buildType, selectedAbi)
tasks.create(name: taskName, type: Exec) {
executable sconsExecutableFile.absolutePath
- args "--directory=${pathToRootDir}", "platform=android", "tools=${toolsFlag}", "target=${sconsTarget}", "arch=${selectedAbi}", "-j" + Runtime.runtime.availableProcessors()
+ args "--directory=${pathToRootDir}", "platform=android", "dev_mode=${devBuild}", "dev_build=${devBuild}", "target=${sconsTarget}", "arch=${selectedAbi}", "-j" + Runtime.runtime.availableProcessors()
}
// Schedule the tasks so the generated libs are present before the aar file is packaged.
diff --git a/platform/android/java/lib/res/values/strings.xml b/platform/android/java/lib/res/values/strings.xml
index 010006b81e..f5a4ab1071 100644
--- a/platform/android/java/lib/res/values/strings.xml
+++ b/platform/android/java/lib/res/values/strings.xml
@@ -12,6 +12,8 @@
<string name="text_button_resume">Resume Download</string>
<string name="text_button_cancel">Cancel</string>
<string name="text_button_cancel_verify">Cancel Verification</string>
+ <string name="text_error_title">Error!</string>
+ <string name="error_engine_setup_message">Unable to setup the Godot Engine! Aborting…</string>
<!-- APK Expansion Strings -->
diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
index 28e689e63a..92e5e59496 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
@@ -57,6 +57,7 @@ import android.content.SharedPreferences.Editor;
import android.content.pm.ConfigurationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.Sensor;
@@ -69,6 +70,7 @@ import android.os.Environment;
import android.os.Messenger;
import android.os.VibrationEffect;
import android.os.Vibrator;
+import android.util.Log;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.Surface;
@@ -85,6 +87,8 @@ import android.widget.TextView;
import androidx.annotation.CallSuper;
import androidx.annotation.Keep;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
import androidx.fragment.app.Fragment;
import com.google.android.vending.expansion.downloader.DownloadProgressInfo;
@@ -105,6 +109,8 @@ import java.util.List;
import java.util.Locale;
public class Godot extends Fragment implements SensorEventListener, IDownloaderClient {
+ private static final String TAG = Godot.class.getSimpleName();
+
private IStub mDownloaderClientStub;
private TextView mStatusText;
private TextView mProgressFraction;
@@ -250,7 +256,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
* Used by the native code (java_godot_lib_jni.cpp) to complete initialization of the GLSurfaceView view and renderer.
*/
@Keep
- private void onVideoInit() {
+ private boolean onVideoInit() {
final Activity activity = getActivity();
containerLayout = new FrameLayout(activity);
containerLayout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
@@ -262,13 +268,17 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
// ...add to FrameLayout
containerLayout.addView(editText);
- GodotLib.setup(command_line);
+ if (!GodotLib.setup(command_line)) {
+ Log.e(TAG, "Unable to setup the Godot engine! Aborting...");
+ alert(R.string.error_engine_setup_message, R.string.text_error_title, this::forceQuit);
+ return false;
+ }
- final String videoDriver = GodotLib.getGlobal("rendering/driver/driver_name");
- if (videoDriver.equals("vulkan")) {
- mRenderView = new GodotVulkanRenderView(activity, this);
- } else {
+ final String renderer = GodotLib.getGlobal("rendering/renderer/rendering_method");
+ if (renderer.equals("gl_compatibility")) {
mRenderView = new GodotGLRenderView(activity, this, xrMode, use_debug_opengl);
+ } else {
+ mRenderView = new GodotVulkanRenderView(activity, this);
}
View view = mRenderView.getView();
@@ -303,6 +313,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
}
}
}
+ return true;
}
public void setKeepScreenOn(final boolean p_enabled) {
@@ -344,13 +355,27 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
}
public void alert(final String message, final String title) {
+ alert(message, title, null);
+ }
+
+ private void alert(@StringRes int messageResId, @StringRes int titleResId, @Nullable Runnable okCallback) {
+ Resources res = getResources();
+ alert(res.getString(messageResId), res.getString(titleResId), okCallback);
+ }
+
+ private void alert(final String message, final String title, @Nullable Runnable okCallback) {
final Activity activity = getActivity();
runOnUiThread(() -> {
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setMessage(message).setTitle(title);
builder.setPositiveButton(
"OK",
- (dialog, id) -> dialog.cancel());
+ (dialog, id) -> {
+ if (okCallback != null) {
+ okCallback.run();
+ }
+ dialog.cancel();
+ });
AlertDialog dialog = builder.create();
dialog.show();
});
@@ -471,7 +496,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
mMagnetometer = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
mGyroscope = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
- GodotLib.initialize(activity,
+ godot_initialized = GodotLib.initialize(activity,
this,
activity.getAssets(),
io,
@@ -482,8 +507,6 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
tts);
result_callback = null;
-
- godot_initialized = true;
}
@Override
@@ -1023,7 +1046,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
}
@Keep
- private GodotRenderView getRenderView() { // used by native side to get renderView
+ public GodotRenderView getRenderView() { // used by native side to get renderView
return mRenderView;
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
index 08da1b1832..3dfc37f6b0 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
@@ -31,7 +31,6 @@
package org.godotengine.godot;
import org.godotengine.godot.gl.GLSurfaceView;
import org.godotengine.godot.gl.GodotRenderer;
-import org.godotengine.godot.input.GodotGestureHandler;
import org.godotengine.godot.input.GodotInputHandler;
import org.godotengine.godot.utils.GLUtils;
import org.godotengine.godot.xr.XRMode;
@@ -46,7 +45,6 @@ import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.PixelFormat;
import android.os.Build;
-import android.view.GestureDetector;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.PointerIcon;
@@ -75,9 +73,7 @@ import androidx.annotation.Keep;
public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView {
private final Godot godot;
private final GodotInputHandler inputHandler;
- private final GestureDetector detector;
private final GodotRenderer godotRenderer;
- private PointerIcon pointerIcon;
public GodotGLRenderView(Context context, Godot godot, XRMode xrMode, boolean p_use_debug_opengl) {
super(context);
@@ -85,10 +81,9 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
this.godot = godot;
this.inputHandler = new GodotInputHandler(this);
- this.detector = new GestureDetector(context, new GodotGestureHandler(this));
this.godotRenderer = new GodotRenderer();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- pointerIcon = PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT);
+ setPointerIcon(PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT));
}
init(xrMode, false);
}
@@ -132,7 +127,6 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
- this.detector.onTouchEvent(event);
return inputHandler.onTouchEvent(event);
}
@@ -156,19 +150,40 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
return inputHandler.onGenericMotionEvent(event);
}
+ @Override
+ public void onPointerCaptureChange(boolean hasCapture) {
+ super.onPointerCaptureChange(hasCapture);
+ inputHandler.onPointerCaptureChange(hasCapture);
+ }
+
+ @Override
+ public void requestPointerCapture() {
+ super.requestPointerCapture();
+ inputHandler.onPointerCaptureChange(true);
+ }
+
+ @Override
+ public void releasePointerCapture() {
+ super.releasePointerCapture();
+ inputHandler.onPointerCaptureChange(false);
+ }
+
/**
* called from JNI to change pointer icon
*/
@Keep
public void setPointerIcon(int pointerType) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- pointerIcon = PointerIcon.getSystemIcon(getContext(), pointerType);
+ setPointerIcon(PointerIcon.getSystemIcon(getContext(), pointerType));
}
}
@Override
public PointerIcon onResolvePointerIcon(MotionEvent me, int pointerIndex) {
- return pointerIcon;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ return getPointerIcon();
+ }
+ return super.onResolvePointerIcon(me, pointerIndex);
}
private void init(XRMode xrMode, boolean translucent) {
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
index f855fc6cf6..26aad867b1 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
@@ -54,7 +54,7 @@ public class GodotLib {
/**
* Invoked on the main thread to initialize Godot native layer.
*/
- public static native void initialize(Activity activity,
+ public static native boolean initialize(Activity activity,
Godot p_instance,
AssetManager p_asset_manager,
GodotIO godotIO,
@@ -74,7 +74,7 @@ public class GodotLib {
* Invoked on the GL thread to complete setup for the Godot native layer logic.
* @param p_cmdline Command line arguments used to configure Godot native layer components.
*/
- public static native void setup(String[] p_cmdline);
+ public static native boolean setup(String[] p_cmdline);
/**
* Invoked on the GL thread when the underlying Android surface has changed size.
@@ -92,7 +92,7 @@ public class GodotLib {
public static native void newcontext(Surface p_surface);
/**
- * Forward {@link Activity#onBackPressed()} event from the main thread to the GL thread.
+ * Forward {@link Activity#onBackPressed()} event.
*/
public static native void back();
@@ -108,63 +108,60 @@ public class GodotLib {
public static native void ttsCallback(int event, int id, int pos);
/**
- * Forward touch events from the main thread to the GL thread.
+ * Forward touch events.
*/
- public static native void touch(int inputDevice, int event, int pointer, int pointerCount, float[] positions);
- public static native void touch(int inputDevice, int event, int pointer, int pointerCount, float[] positions, int buttonsMask);
- public static native void touch(int inputDevice, int event, int pointer, int pointerCount, float[] positions, int buttonsMask, float verticalFactor, float horizontalFactor);
+ public static native void dispatchTouchEvent(int event, int pointer, int pointerCount, float[] positions);
/**
- * Forward hover events from the main thread to the GL thread.
+ * Dispatch mouse events
*/
- public static native void hover(int type, float x, float y);
+ public static native void dispatchMouseEvent(int event, int buttonMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative);
- /**
- * Forward double_tap events from the main thread to the GL thread.
- */
- public static native void doubleTap(int buttonMask, int x, int y);
+ public static native void magnify(float x, float y, float factor);
+
+ public static native void pan(float x, float y, float deltaX, float deltaY);
/**
- * Forward accelerometer sensor events from the main thread to the GL thread.
+ * Forward accelerometer sensor events.
* @see android.hardware.SensorEventListener#onSensorChanged(SensorEvent)
*/
public static native void accelerometer(float x, float y, float z);
/**
- * Forward gravity sensor events from the main thread to the GL thread.
+ * Forward gravity sensor events.
* @see android.hardware.SensorEventListener#onSensorChanged(SensorEvent)
*/
public static native void gravity(float x, float y, float z);
/**
- * Forward magnetometer sensor events from the main thread to the GL thread.
+ * Forward magnetometer sensor events.
* @see android.hardware.SensorEventListener#onSensorChanged(SensorEvent)
*/
public static native void magnetometer(float x, float y, float z);
/**
- * Forward gyroscope sensor events from the main thread to the GL thread.
+ * Forward gyroscope sensor events.
* @see android.hardware.SensorEventListener#onSensorChanged(SensorEvent)
*/
public static native void gyroscope(float x, float y, float z);
/**
- * Forward regular key events from the main thread to the GL thread.
+ * Forward regular key events.
*/
public static native void key(int p_keycode, int p_physical_keycode, int p_unicode, boolean p_pressed);
/**
- * Forward game device's key events from the main thread to the GL thread.
+ * Forward game device's key events.
*/
public static native void joybutton(int p_device, int p_but, boolean p_pressed);
/**
- * Forward joystick devices axis motion events from the main thread to the GL thread.
+ * Forward joystick devices axis motion events.
*/
public static native void joyaxis(int p_device, int p_axis, float p_value);
/**
- * Forward joystick devices hat motion events from the main thread to the GL thread.
+ * Forward joystick devices hat motion events.
*/
public static native void joyhat(int p_device, int p_hat_x, int p_hat_y);
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java
index c386a2d2eb..0becf00d93 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java
@@ -30,7 +30,6 @@
package org.godotengine.godot;
-import org.godotengine.godot.input.GodotGestureHandler;
import org.godotengine.godot.input.GodotInputHandler;
import org.godotengine.godot.vulkan.VkRenderer;
import org.godotengine.godot.vulkan.VkSurfaceView;
@@ -38,7 +37,6 @@ import org.godotengine.godot.vulkan.VkSurfaceView;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Build;
-import android.view.GestureDetector;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.PointerIcon;
@@ -49,19 +47,16 @@ import androidx.annotation.Keep;
public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderView {
private final Godot godot;
private final GodotInputHandler mInputHandler;
- private final GestureDetector mGestureDetector;
private final VkRenderer mRenderer;
- private PointerIcon pointerIcon;
public GodotVulkanRenderView(Context context, Godot godot) {
super(context);
this.godot = godot;
mInputHandler = new GodotInputHandler(this);
- mGestureDetector = new GestureDetector(context, new GodotGestureHandler(this));
mRenderer = new VkRenderer();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- pointerIcon = PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT);
+ setPointerIcon(PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT));
}
setFocusableInTouchMode(true);
startRenderer(mRenderer);
@@ -106,7 +101,6 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
- mGestureDetector.onTouchEvent(event);
return mInputHandler.onTouchEvent(event);
}
@@ -130,19 +124,40 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV
return mInputHandler.onGenericMotionEvent(event);
}
+ @Override
+ public void requestPointerCapture() {
+ super.requestPointerCapture();
+ mInputHandler.onPointerCaptureChange(true);
+ }
+
+ @Override
+ public void releasePointerCapture() {
+ super.releasePointerCapture();
+ mInputHandler.onPointerCaptureChange(false);
+ }
+
+ @Override
+ public void onPointerCaptureChange(boolean hasCapture) {
+ super.onPointerCaptureChange(hasCapture);
+ mInputHandler.onPointerCaptureChange(hasCapture);
+ }
+
/**
* called from JNI to change pointer icon
*/
@Keep
public void setPointerIcon(int pointerType) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- pointerIcon = PointerIcon.getSystemIcon(getContext(), pointerType);
+ setPointerIcon(PointerIcon.getSystemIcon(getContext(), pointerType));
}
}
@Override
public PointerIcon onResolvePointerIcon(MotionEvent me, int pointerIndex) {
- return pointerIcon;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ return getPointerIcon();
+ }
+ return super.onResolvePointerIcon(me, pointerIndex);
}
@Override
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt
new file mode 100644
index 0000000000..9715c31fc1
--- /dev/null
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt
@@ -0,0 +1,289 @@
+/*************************************************************************/
+/* GodotGestureHandler.kt */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 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. */
+/*************************************************************************/
+
+package org.godotengine.godot.input
+
+import android.os.Build
+import android.view.GestureDetector.SimpleOnGestureListener
+import android.view.InputDevice
+import android.view.MotionEvent
+import android.view.ScaleGestureDetector
+import android.view.ScaleGestureDetector.OnScaleGestureListener
+import org.godotengine.godot.GodotLib
+
+/**
+ * Handles regular and scale gesture input related events for the [GodotView] view.
+ *
+ * @See https://developer.android.com/reference/android/view/GestureDetector.SimpleOnGestureListener
+ * @See https://developer.android.com/reference/android/view/ScaleGestureDetector.OnScaleGestureListener
+ */
+internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureListener {
+
+ companion object {
+ private val TAG = GodotGestureHandler::class.java.simpleName
+ }
+
+ /**
+ * Enable pan and scale gestures
+ */
+ var panningAndScalingEnabled = false
+
+ private var doubleTapInProgress = false
+ private var dragInProgress = false
+ private var scaleInProgress = false
+ private var contextClickInProgress = false
+ private var pointerCaptureInProgress = false
+
+ override fun onDown(event: MotionEvent): Boolean {
+ // Don't send / register a down event while we're in the middle of a double-tap
+ if (!doubleTapInProgress) {
+ // Send the down event
+ GodotInputHandler.handleMotionEvent(event)
+ }
+ return true
+ }
+
+ override fun onSingleTapUp(event: MotionEvent): Boolean {
+ GodotInputHandler.handleMotionEvent(event)
+ return true
+ }
+
+ override fun onLongPress(event: MotionEvent) {
+ contextClickRouter(event)
+ }
+
+ private fun contextClickRouter(event: MotionEvent) {
+ if (scaleInProgress) {
+ return
+ }
+
+ // Cancel the previous down event
+ GodotInputHandler.handleMotionEvent(
+ event.source,
+ MotionEvent.ACTION_CANCEL,
+ event.buttonState,
+ event.x,
+ event.y
+ )
+
+ // Turn a context click into a single tap right mouse button click.
+ GodotInputHandler.handleMouseEvent(
+ MotionEvent.ACTION_DOWN,
+ MotionEvent.BUTTON_SECONDARY,
+ event.x,
+ event.y
+ )
+ contextClickInProgress = true
+ }
+
+ fun onPointerCaptureChange(hasCapture: Boolean) {
+ if (pointerCaptureInProgress == hasCapture) {
+ return
+ }
+
+ if (!hasCapture) {
+ // Dispatch a mouse relative ACTION_UP event to signal the end of the capture
+ GodotInputHandler.handleMouseEvent(
+ MotionEvent.ACTION_UP,
+ 0,
+ 0f,
+ 0f,
+ 0f,
+ 0f,
+ false,
+ true
+ )
+ }
+ pointerCaptureInProgress = hasCapture
+ }
+
+ fun onMotionEvent(event: MotionEvent): Boolean {
+ return when (event.actionMasked) {
+ MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_BUTTON_RELEASE -> {
+ onActionUp(event)
+ }
+ MotionEvent.ACTION_MOVE -> {
+ onActionMove(event)
+ }
+ else -> false
+ }
+ }
+
+ private fun onActionUp(event: MotionEvent): Boolean {
+ val sourceMouseRelative = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE)
+ } else {
+ false
+ }
+ when {
+ pointerCaptureInProgress -> {
+ return if (event.actionMasked == MotionEvent.ACTION_CANCEL) {
+ // Don't dispatch the ACTION_CANCEL while a capture is in progress
+ true
+ } else {
+ GodotInputHandler.handleMouseEvent(
+ MotionEvent.ACTION_UP,
+ event.buttonState,
+ event.x,
+ event.y,
+ 0f,
+ 0f,
+ false,
+ sourceMouseRelative
+ )
+ pointerCaptureInProgress = false
+ true
+ }
+ }
+ dragInProgress -> {
+ GodotInputHandler.handleMotionEvent(event)
+ dragInProgress = false
+ return true
+ }
+ contextClickInProgress -> {
+ GodotInputHandler.handleMouseEvent(
+ event.actionMasked,
+ 0,
+ event.x,
+ event.y,
+ 0f,
+ 0f,
+ false,
+ sourceMouseRelative
+ )
+ contextClickInProgress = false
+ return true
+ }
+ else -> return false
+ }
+ }
+
+ private fun onActionMove(event: MotionEvent): Boolean {
+ if (contextClickInProgress) {
+ val sourceMouseRelative = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE)
+ } else {
+ false
+ }
+ GodotInputHandler.handleMouseEvent(
+ event.actionMasked,
+ MotionEvent.BUTTON_SECONDARY,
+ event.x,
+ event.y,
+ 0f,
+ 0f,
+ false,
+ sourceMouseRelative
+ )
+ return true
+ }
+ return false
+ }
+
+ override fun onDoubleTapEvent(event: MotionEvent): Boolean {
+ if (event.actionMasked == MotionEvent.ACTION_UP) {
+ doubleTapInProgress = false
+ }
+ return true
+ }
+
+ override fun onDoubleTap(event: MotionEvent): Boolean {
+ doubleTapInProgress = true
+ val x = event.x
+ val y = event.y
+ val buttonMask =
+ if (GodotInputHandler.isMouseEvent(event)) {
+ event.buttonState
+ } else {
+ MotionEvent.BUTTON_PRIMARY
+ }
+ GodotInputHandler.handleMouseEvent(MotionEvent.ACTION_DOWN, buttonMask, x, y, true)
+ GodotInputHandler.handleMouseEvent(MotionEvent.ACTION_UP, 0, x, y, false)
+
+ return true
+ }
+
+ override fun onScroll(
+ originEvent: MotionEvent,
+ terminusEvent: MotionEvent,
+ distanceX: Float,
+ distanceY: Float
+ ): Boolean {
+ if (scaleInProgress) {
+ if (dragInProgress) {
+ // Cancel the drag
+ GodotInputHandler.handleMotionEvent(
+ originEvent.source,
+ MotionEvent.ACTION_CANCEL,
+ originEvent.buttonState,
+ originEvent.x,
+ originEvent.y
+ )
+ dragInProgress = false
+ }
+ return true
+ }
+
+ dragInProgress = true
+
+ val x = terminusEvent.x
+ val y = terminusEvent.y
+ if (terminusEvent.pointerCount >= 2 && panningAndScalingEnabled) {
+ GodotLib.pan(x, y, distanceX / 5f, distanceY / 5f)
+ } else {
+ GodotInputHandler.handleMotionEvent(terminusEvent)
+ }
+ return true
+ }
+
+ override fun onScale(detector: ScaleGestureDetector?): Boolean {
+ if (detector == null || !panningAndScalingEnabled) {
+ return false
+ }
+ GodotLib.magnify(
+ detector.focusX,
+ detector.focusY,
+ detector.scaleFactor
+ )
+ return true
+ }
+
+ override fun onScaleBegin(detector: ScaleGestureDetector?): Boolean {
+ if (detector == null || !panningAndScalingEnabled) {
+ return false
+ }
+ scaleInProgress = true
+ return true
+ }
+
+ override fun onScaleEnd(detector: ScaleGestureDetector?) {
+ scaleInProgress = false
+ }
+}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
index da15b2490c..03cb8034fa 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
@@ -41,13 +41,13 @@ import android.os.Build;
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseIntArray;
+import android.view.GestureDetector;
import android.view.InputDevice;
-import android.view.InputDevice.MotionRange;
import android.view.KeyEvent;
import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
import java.util.Collections;
-import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
@@ -55,21 +55,49 @@ import java.util.Set;
* Handles input related events for the {@link GodotRenderView} view.
*/
public class GodotInputHandler implements InputManager.InputDeviceListener {
- private final GodotRenderView mRenderView;
- private final InputManager mInputManager;
-
- private final String tag = this.getClass().getSimpleName();
+ private static final String TAG = GodotInputHandler.class.getSimpleName();
private final SparseIntArray mJoystickIds = new SparseIntArray(4);
private final SparseArray<Joystick> mJoysticksDevices = new SparseArray<>(4);
+ private final GodotRenderView mRenderView;
+ private final InputManager mInputManager;
+ private final GestureDetector gestureDetector;
+ private final ScaleGestureDetector scaleGestureDetector;
+ private final GodotGestureHandler godotGestureHandler;
+
public GodotInputHandler(GodotRenderView godotView) {
+ final Context context = godotView.getView().getContext();
mRenderView = godotView;
- mInputManager = (InputManager)mRenderView.getView().getContext().getSystemService(Context.INPUT_SERVICE);
+ mInputManager = (InputManager)context.getSystemService(Context.INPUT_SERVICE);
mInputManager.registerInputDeviceListener(this, null);
+
+ this.godotGestureHandler = new GodotGestureHandler();
+ this.gestureDetector = new GestureDetector(context, godotGestureHandler);
+ this.gestureDetector.setIsLongpressEnabled(false);
+ this.scaleGestureDetector = new ScaleGestureDetector(context, godotGestureHandler);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ this.scaleGestureDetector.setStylusScaleEnabled(true);
+ }
}
- private boolean isKeyEvent_GameDevice(int source) {
+ /**
+ * Enable long press events. This is false by default.
+ */
+ public void enableLongPress(boolean enable) {
+ this.gestureDetector.setIsLongpressEnabled(enable);
+ }
+
+ /**
+ * Enable multi-fingers pan & scale gestures. This is false by default.
+ *
+ * Note: This may interfere with multi-touch handling / support.
+ */
+ public void enablePanningAndScalingGestures(boolean enable) {
+ this.godotGestureHandler.setPanningAndScalingEnabled(enable);
+ }
+
+ private boolean isKeyEventGameDevice(int source) {
// Note that keyboards are often (SOURCE_KEYBOARD | SOURCE_DPAD)
if (source == (InputDevice.SOURCE_KEYBOARD | InputDevice.SOURCE_DPAD))
return false;
@@ -77,6 +105,10 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
return (source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK || (source & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD || (source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD;
}
+ public void onPointerCaptureChange(boolean hasCapture) {
+ godotGestureHandler.onPointerCaptureChange(hasCapture);
+ }
+
public boolean onKeyUp(final int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
return true;
@@ -87,7 +119,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
}
int source = event.getSource();
- if (isKeyEvent_GameDevice(source)) {
+ if (isKeyEventGameDevice(source)) {
// Check if the device exists
final int deviceId = event.getDeviceId();
if (mJoystickIds.indexOfKey(deviceId) >= 0) {
@@ -121,11 +153,10 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
}
int source = event.getSource();
- //Log.e(TAG, String.format("Key down! source %d, device %d, joystick %d, %d, %d", event.getDeviceId(), source, (source & InputDevice.SOURCE_JOYSTICK), (source & InputDevice.SOURCE_DPAD), (source & InputDevice.SOURCE_GAMEPAD)));
final int deviceId = event.getDeviceId();
// Check if source is a game device and that the device is a registered gamepad
- if (isKeyEvent_GameDevice(source)) {
+ if (isKeyEventGameDevice(source)) {
if (event.getRepeatCount() > 0) // ignore key echo
return true;
@@ -145,47 +176,41 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
}
public boolean onTouchEvent(final MotionEvent event) {
- // Mouse drag (mouse pressed and move) doesn't fire onGenericMotionEvent so this is needed
- if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
- if (event.getAction() != MotionEvent.ACTION_MOVE) {
- // we return true because every time a mouse event is fired, the event is already handled
- // in onGenericMotionEvent, so by touch event we can say that the event is also handled
- return true;
- }
- return handleMouseEvent(event);
+ this.scaleGestureDetector.onTouchEvent(event);
+ if (this.gestureDetector.onTouchEvent(event)) {
+ // The gesture detector has handled the event.
+ return true;
}
- final int evcount = event.getPointerCount();
- if (evcount == 0)
+ if (godotGestureHandler.onMotionEvent(event)) {
+ // The gesture handler has handled the event.
return true;
+ }
- if (mRenderView != null) {
- final float[] arr = new float[event.getPointerCount() * 3]; // pointerId1, x1, y1, pointerId2, etc...
+ // Drag events are handled by the [GodotGestureHandler]
+ if (event.getActionMasked() == MotionEvent.ACTION_MOVE) {
+ return true;
+ }
- for (int i = 0; i < event.getPointerCount(); i++) {
- arr[i * 3 + 0] = event.getPointerId(i);
- arr[i * 3 + 1] = event.getX(i);
- arr[i * 3 + 2] = event.getY(i);
- }
- final int action = event.getActionMasked();
- final int pointer_idx = event.getPointerId(event.getActionIndex());
-
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_MOVE:
- case MotionEvent.ACTION_POINTER_UP:
- case MotionEvent.ACTION_POINTER_DOWN: {
- GodotLib.touch(event.getSource(), action, pointer_idx, evcount, arr);
- } break;
- }
+ if (isMouseEvent(event)) {
+ return handleMouseEvent(event);
}
- return true;
+
+ return handleTouchEvent(event);
}
public boolean onGenericMotionEvent(MotionEvent event) {
- if (event.isFromSource(InputDevice.SOURCE_JOYSTICK) && event.getAction() == MotionEvent.ACTION_MOVE) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && gestureDetector.onGenericMotionEvent(event)) {
+ // The gesture detector has handled the event.
+ return true;
+ }
+
+ if (godotGestureHandler.onMotionEvent(event)) {
+ // The gesture handler has handled the event.
+ return true;
+ }
+
+ if (event.isFromSource(InputDevice.SOURCE_JOYSTICK) && event.getActionMasked() == MotionEvent.ACTION_MOVE) {
// Check if the device exists
final int deviceId = event.getDeviceId();
if (mJoystickIds.indexOfKey(deviceId) >= 0) {
@@ -198,15 +223,14 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
for (int i = 0; i < joystick.axes.size(); i++) {
final int axis = joystick.axes.get(i);
final float value = event.getAxisValue(axis);
- /**
- * As all axes are polled for each event, only fire an axis event if the value has actually changed.
- * Prevents flooding Godot with repeated events.
+ /*
+ As all axes are polled for each event, only fire an axis event if the value has actually changed.
+ Prevents flooding Godot with repeated events.
*/
if (joystick.axesValues.indexOfKey(axis) < 0 || (float)joystick.axesValues.get(axis) != value) {
// save value to prevent repeats
joystick.axesValues.put(axis, value);
- final int godotAxisIdx = i;
- GodotLib.joyaxis(godotJoyId, godotAxisIdx, value);
+ GodotLib.joyaxis(godotJoyId, i, value);
}
}
@@ -221,17 +245,8 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
}
return true;
}
- } else if (event.isFromSource(InputDevice.SOURCE_STYLUS)) {
- final float x = event.getX();
- final float y = event.getY();
- final int type = event.getAction();
- GodotLib.hover(type, x, y);
- return true;
-
- } else if (event.isFromSource(InputDevice.SOURCE_MOUSE) || event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE)) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- return handleMouseEvent(event);
- }
+ } else if (isMouseEvent(event)) {
+ return handleMouseEvent(event);
}
return false;
@@ -243,7 +258,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
for (int deviceId : deviceIds) {
InputDevice device = mInputManager.getInputDevice(deviceId);
if (DEBUG) {
- Log.v("GodotInputHandler", String.format("init() deviceId:%d, Name:%s\n", deviceId, device.getName()));
+ Log.v(TAG, String.format("init() deviceId:%d, Name:%s\n", deviceId, device.getName()));
}
onInputDeviceAdded(deviceId);
}
@@ -288,13 +303,12 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
joystick.name = device.getName();
//Helps with creating new joypad mappings.
- Log.i(tag, "=== New Input Device: " + joystick.name);
+ Log.i(TAG, "=== New Input Device: " + joystick.name);
Set<Integer> already = new HashSet<>();
for (InputDevice.MotionRange range : device.getMotionRanges()) {
boolean isJoystick = range.isFromSource(InputDevice.SOURCE_JOYSTICK);
boolean isGamepad = range.isFromSource(InputDevice.SOURCE_GAMEPAD);
- //Log.i(tag, "axis: "+range.getAxis()+ ", isJoystick: "+isJoystick+", isGamepad: "+isGamepad);
if (!isJoystick && !isGamepad) {
continue;
}
@@ -306,14 +320,14 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
already.add(axis);
joystick.axes.add(axis);
} else {
- Log.w(tag, " - DUPLICATE AXIS VALUE IN LIST: " + axis);
+ Log.w(TAG, " - DUPLICATE AXIS VALUE IN LIST: " + axis);
}
}
}
Collections.sort(joystick.axes);
for (int idx = 0; idx < joystick.axes.size(); idx++) {
//Helps with creating new joypad mappings.
- Log.i(tag, " - Mapping Android axis " + joystick.axes.get(idx) + " to Godot axis " + idx);
+ Log.i(TAG, " - Mapping Android axis " + joystick.axes.get(idx) + " to Godot axis " + idx);
}
mJoysticksDevices.put(deviceId, joystick);
@@ -338,13 +352,6 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
onInputDeviceAdded(deviceId);
}
- private static class RangeComparator implements Comparator<MotionRange> {
- @Override
- public int compare(MotionRange arg0, MotionRange arg1) {
- return arg0.getAxis() - arg1.getAxis();
- }
- }
-
public static int getGodotButton(int keyCode) {
int button;
switch (keyCode) {
@@ -410,39 +417,113 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
return button;
}
- private boolean handleMouseEvent(final MotionEvent event) {
- switch (event.getActionMasked()) {
+ static boolean isMouseEvent(MotionEvent event) {
+ return isMouseEvent(event.getSource());
+ }
+
+ private static boolean isMouseEvent(int eventSource) {
+ boolean mouseSource = ((eventSource & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) || ((eventSource & InputDevice.SOURCE_STYLUS) == InputDevice.SOURCE_STYLUS);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ mouseSource = mouseSource || ((eventSource & InputDevice.SOURCE_MOUSE_RELATIVE) == InputDevice.SOURCE_MOUSE_RELATIVE);
+ }
+ return mouseSource;
+ }
+
+ static boolean handleMotionEvent(final MotionEvent event) {
+ if (isMouseEvent(event)) {
+ return handleMouseEvent(event);
+ }
+
+ return handleTouchEvent(event);
+ }
+
+ static boolean handleMotionEvent(int eventSource, int eventAction, int buttonsMask, float x, float y) {
+ return handleMotionEvent(eventSource, eventAction, buttonsMask, x, y, 0, 0);
+ }
+
+ static boolean handleMotionEvent(int eventSource, int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY) {
+ if (isMouseEvent(eventSource)) {
+ return handleMouseEvent(eventAction, buttonsMask, x, y, deltaX, deltaY, false, false);
+ }
+
+ return handleTouchEvent(eventAction, x, y);
+ }
+
+ static boolean handleMouseEvent(final MotionEvent event) {
+ final int eventAction = event.getActionMasked();
+ final float x = event.getX();
+ final float y = event.getY();
+ final int buttonsMask = event.getButtonState();
+
+ final float verticalFactor = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
+ final float horizontalFactor = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
+ boolean sourceMouseRelative = false;
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
+ sourceMouseRelative = event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE);
+ }
+ return handleMouseEvent(eventAction, buttonsMask, x, y, horizontalFactor, verticalFactor, false, sourceMouseRelative);
+ }
+
+ static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y) {
+ return handleMouseEvent(eventAction, buttonsMask, x, y, 0, 0, false, false);
+ }
+
+ static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, boolean doubleClick) {
+ return handleMouseEvent(eventAction, buttonsMask, x, y, 0, 0, doubleClick, false);
+ }
+
+ static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative) {
+ switch (eventAction) {
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ // Zero-up the button state
+ buttonsMask = 0;
+ // FALL THROUGH
+ case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_HOVER_ENTER:
+ case MotionEvent.ACTION_HOVER_EXIT:
case MotionEvent.ACTION_HOVER_MOVE:
- case MotionEvent.ACTION_HOVER_EXIT: {
- final float x = event.getX();
- final float y = event.getY();
- final int type = event.getAction();
- GodotLib.hover(type, x, y);
- return true;
- }
- case MotionEvent.ACTION_BUTTON_PRESS:
- case MotionEvent.ACTION_BUTTON_RELEASE:
- case MotionEvent.ACTION_MOVE: {
- final float x = event.getX();
- final float y = event.getY();
- final int buttonsMask = event.getButtonState();
- final int action = event.getAction();
- GodotLib.touch(event.getSource(), action, 0, 1, new float[] { 0, x, y }, buttonsMask);
- return true;
- }
+ case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_SCROLL: {
- final float x = event.getX();
- final float y = event.getY();
- final int buttonsMask = event.getButtonState();
- final int action = event.getAction();
- final float verticalFactor = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
- final float horizontalFactor = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
- GodotLib.touch(event.getSource(), action, 0, 1, new float[] { 0, x, y }, buttonsMask, verticalFactor, horizontalFactor);
+ GodotLib.dispatchMouseEvent(eventAction, buttonsMask, x, y, deltaX, deltaY, doubleClick, sourceMouseRelative);
+ return true;
}
+ }
+ return false;
+ }
+
+ static boolean handleTouchEvent(final MotionEvent event) {
+ final int pointerCount = event.getPointerCount();
+ if (pointerCount == 0) {
+ return true;
+ }
+
+ final float[] positions = new float[pointerCount * 3]; // pointerId1, x1, y1, pointerId2, etc...
+
+ for (int i = 0; i < pointerCount; i++) {
+ positions[i * 3 + 0] = event.getPointerId(i);
+ positions[i * 3 + 1] = event.getX(i);
+ positions[i * 3 + 2] = event.getY(i);
+ }
+ final int action = event.getActionMasked();
+ final int actionPointerId = event.getPointerId(event.getActionIndex());
+
+ return handleTouchEvent(action, actionPointerId, pointerCount, positions);
+ }
+
+ static boolean handleTouchEvent(int eventAction, float x, float y) {
+ return handleTouchEvent(eventAction, 0, 1, new float[] { 0, x, y });
+ }
+
+ static boolean handleTouchEvent(int eventAction, int actionPointerId, int pointerCount, float[] positions) {
+ switch (eventAction) {
case MotionEvent.ACTION_DOWN:
- case MotionEvent.ACTION_UP: {
- // we can safely ignore these cases because they are always come beside ACTION_BUTTON_PRESS and ACTION_BUTTON_RELEASE
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_MOVE:
+ case MotionEvent.ACTION_POINTER_UP:
+ case MotionEvent.ACTION_POINTER_DOWN: {
+ GodotLib.dispatchTouchEvent(eventAction, actionPointerId, pointerCount, positions);
return true;
}
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java
index c959b5f28c..01ad5ee415 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java
@@ -122,7 +122,7 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene
@Override
public boolean onEditorAction(final TextView pTextView, final int pActionID, final KeyEvent pKeyEvent) {
- if (mEdit == pTextView && isFullScreenEdit()) {
+ if (mEdit == pTextView && isFullScreenEdit() && pKeyEvent != null) {
final String characters = pKeyEvent.getCharacters();
for (int i = 0; i < characters.length(); i++) {
diff --git a/platform/android/java_godot_io_wrapper.h b/platform/android/java_godot_io_wrapper.h
index 9a1a877b6f..24995147d4 100644
--- a/platform/android/java_godot_io_wrapper.h
+++ b/platform/android/java_godot_io_wrapper.h
@@ -28,9 +28,6 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-// note, swapped java and godot around in the file name so all the java
-// wrappers are together
-
#ifndef JAVA_GODOT_IO_WRAPPER_H
#define JAVA_GODOT_IO_WRAPPER_H
diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp
index 422c05e5ce..04b69d5b86 100644
--- a/platform/android/java_godot_lib_jni.cpp
+++ b/platform/android/java_godot_lib_jni.cpp
@@ -79,7 +79,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHei
}
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jclass clazz, jobject p_activity, jobject p_godot_instance, jobject p_asset_manager, jobject p_godot_io, jobject p_net_utils, jobject p_directory_access_handler, jobject p_file_access_handler, jboolean p_use_apk_expansion, jobject p_godot_tts) {
+JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jclass clazz, jobject p_activity, jobject p_godot_instance, jobject p_asset_manager, jobject p_godot_io, jobject p_net_utils, jobject p_directory_access_handler, jobject p_file_access_handler, jboolean p_use_apk_expansion, jobject p_godot_tts) {
JavaVM *jvm;
env->GetJavaVM(&jvm);
@@ -100,7 +100,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *en
os_android = new OS_Android(godot_java, godot_io_java, p_use_apk_expansion);
- godot_java->on_video_init(env);
+ return godot_java->on_video_init(env);
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env, jclass clazz) {
@@ -123,7 +123,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env
}
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jclass clazz, jobjectArray p_cmdline) {
+JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jclass clazz, jobjectArray p_cmdline) {
setup_android_thread();
const char **cmdline = nullptr;
@@ -133,10 +133,10 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jc
cmdlen = env->GetArrayLength(p_cmdline);
if (cmdlen) {
cmdline = (const char **)memalloc((cmdlen + 1) * sizeof(const char *));
- ERR_FAIL_NULL_MSG(cmdline, "Out of memory.");
+ ERR_FAIL_NULL_V_MSG(cmdline, false, "Out of memory.");
cmdline[cmdlen] = nullptr;
j_cmdline = (jstring *)memalloc(cmdlen * sizeof(jstring));
- ERR_FAIL_NULL_MSG(j_cmdline, "Out of memory.");
+ ERR_FAIL_NULL_V_MSG(j_cmdline, false, "Out of memory.");
for (int i = 0; i < cmdlen; i++) {
jstring string = (jstring)env->GetObjectArrayElement(p_cmdline, i);
@@ -161,11 +161,12 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jc
// Note: --help and --version return ERR_HELP, but this should be translated to 0 if exit codes are propagated.
if (err != OK) {
- return; // should exit instead and print the error
+ return false;
}
java_class_wrapper = memnew(JavaClassWrapper(godot_java->get_activity()));
GDREGISTER_CLASS(JNISingleton);
+ return true;
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jclass clazz, jobject p_surface, jint p_width, jint p_height) {
@@ -254,7 +255,17 @@ JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env,
return should_swap_buffers;
}
-void touch_preprocessing(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jint buttons_mask, jfloat vertical_factor, jfloat horizontal_factor) {
+// Called on the UI thread
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchMouseEvent(JNIEnv *env, jclass clazz, jint p_event_type, jint p_button_mask, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y, jboolean p_double_click, jboolean p_source_mouse_relative) {
+ if (step.get() <= 0) {
+ return;
+ }
+
+ input_handler->process_mouse_event(p_event_type, p_button_mask, Point2(p_x, p_y), Vector2(p_delta_x, p_delta_y), p_double_click, p_source_mouse_relative);
+}
+
+// Called on the UI thread
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchTouchEvent(JNIEnv *env, jclass clazz, jint ev, jint pointer, jint pointer_count, jfloatArray position) {
if (step.get() <= 0) {
return;
}
@@ -262,50 +273,30 @@ void touch_preprocessing(JNIEnv *env, jclass clazz, jint input_device, jint ev,
Vector<AndroidInputHandler::TouchPos> points;
for (int i = 0; i < pointer_count; i++) {
jfloat p[3];
- env->GetFloatArrayRegion(positions, i * 3, 3, p);
+ env->GetFloatArrayRegion(position, i * 3, 3, p);
AndroidInputHandler::TouchPos tp;
tp.pos = Point2(p[1], p[2]);
tp.id = (int)p[0];
points.push_back(tp);
}
- if ((input_device & AINPUT_SOURCE_MOUSE) == AINPUT_SOURCE_MOUSE || (input_device & AINPUT_SOURCE_MOUSE_RELATIVE) == AINPUT_SOURCE_MOUSE_RELATIVE) {
- input_handler->process_mouse_event(input_device, ev, buttons_mask, points[0].pos, vertical_factor, horizontal_factor);
- } else {
- input_handler->process_touch(ev, pointer, points);
- }
-}
-// Called on the UI thread
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3F(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray position) {
- touch_preprocessing(env, clazz, input_device, ev, pointer, pointer_count, position);
+ input_handler->process_touch_event(ev, pointer, points);
}
// Called on the UI thread
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3FI(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray position, jint buttons_mask) {
- touch_preprocessing(env, clazz, input_device, ev, pointer, pointer_count, position, buttons_mask);
-}
-
-// Called on the UI thread
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3FIFF(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray position, jint buttons_mask, jfloat vertical_factor, jfloat horizontal_factor) {
- touch_preprocessing(env, clazz, input_device, ev, pointer, pointer_count, position, buttons_mask, vertical_factor, horizontal_factor);
-}
-
-// Called on the UI thread
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jclass clazz, jint p_type, jfloat p_x, jfloat p_y) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_magnify(JNIEnv *env, jclass clazz, jfloat p_x, jfloat p_y, jfloat p_factor) {
if (step.get() <= 0) {
return;
}
-
- input_handler->process_hover(p_type, Point2(p_x, p_y));
+ input_handler->process_magnify(Point2(p_x, p_y), p_factor);
}
// Called on the UI thread
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_doubleTap(JNIEnv *env, jclass clazz, jint p_button_mask, jint p_x, jint p_y) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_pan(JNIEnv *env, jclass clazz, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y) {
if (step.get() <= 0) {
return;
}
-
- input_handler->process_double_tap(p_button_mask, Point2(p_x, p_y));
+ input_handler->process_pan(Point2(p_x, p_y), Vector2(p_delta_x, p_delta_y));
}
// Called on the UI thread
diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h
index 3c48ca0459..09fed15690 100644
--- a/platform/android/java_godot_lib_jni.h
+++ b/platform/android/java_godot_lib_jni.h
@@ -37,20 +37,18 @@
// These functions can be called from within JAVA and are the means by which our JAVA implementation calls back into our C++ code.
// See java/src/org/godotengine/godot/GodotLib.java for the JAVA side of this (yes that's why we have the long names)
extern "C" {
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jclass clazz, jobject p_activity, jobject p_godot_instance, jobject p_asset_manager, jobject p_godot_io, jobject p_net_utils, jobject p_directory_access_handler, jobject p_file_access_handler, jboolean p_use_apk_expansion, jobject p_godot_tts);
+JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jclass clazz, jobject p_activity, jobject p_godot_instance, jobject p_asset_manager, jobject p_godot_io, jobject p_net_utils, jobject p_directory_access_handler, jobject p_file_access_handler, jboolean p_use_apk_expansion, jobject p_godot_tts);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env, jclass clazz);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jclass clazz, jobjectArray p_cmdline);
+JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jclass clazz, jobjectArray p_cmdline);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jclass clazz, jobject p_surface, jint p_width, jint p_height);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jclass clazz, jobject p_surface);
JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jclass clazz);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ttsCallback(JNIEnv *env, jclass clazz, jint event, jint id, jint pos);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jclass clazz);
-void touch_preprocessing(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jint buttons_mask = 0, jfloat vertical_factor = 0, jfloat horizontal_factor = 0);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3F(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3FI(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jint buttons_mask);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3FIFF(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jint buttons_mask, jfloat vertical_factor, jfloat horizontal_factor);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jclass clazz, jint p_type, jfloat p_x, jfloat p_y);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_doubleTap(JNIEnv *env, jclass clazz, jint p_button_mask, jint p_x, jint p_y);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchMouseEvent(JNIEnv *env, jclass clazz, jint p_event_type, jint p_button_mask, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y, jboolean p_double_click, jboolean p_source_mouse_relative);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchTouchEvent(JNIEnv *env, jclass clazz, jint ev, jint pointer, jint pointer_count, jfloatArray positions);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_magnify(JNIEnv *env, jclass clazz, jfloat p_x, jfloat p_y, jfloat p_factor);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_pan(JNIEnv *env, jclass clazz, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jclass clazz, jint p_keycode, jint p_physical_keycode, jint p_unicode, jboolean p_pressed);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env, jclass clazz, jint p_device, jint p_button, jboolean p_pressed);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jclass clazz, jint p_device, jint p_axis, jfloat p_value);
diff --git a/platform/android/java_godot_view_wrapper.cpp b/platform/android/java_godot_view_wrapper.cpp
index 0153ba96fc..762840a4b1 100644
--- a/platform/android/java_godot_view_wrapper.cpp
+++ b/platform/android/java_godot_view_wrapper.cpp
@@ -40,13 +40,24 @@ GodotJavaViewWrapper::GodotJavaViewWrapper(jobject godot_view) {
_cls = (jclass)env->NewGlobalRef(env->GetObjectClass(godot_view));
- if (android_get_device_api_level() >= __ANDROID_API_O__) {
+ int android_device_api_level = android_get_device_api_level();
+ if (android_device_api_level >= __ANDROID_API_N__) {
+ _set_pointer_icon = env->GetMethodID(_cls, "setPointerIcon", "(I)V");
+ }
+ if (android_device_api_level >= __ANDROID_API_O__) {
_request_pointer_capture = env->GetMethodID(_cls, "requestPointerCapture", "()V");
_release_pointer_capture = env->GetMethodID(_cls, "releasePointerCapture", "()V");
- _set_pointer_icon = env->GetMethodID(_cls, "setPointerIcon", "(I)V");
}
}
+bool GodotJavaViewWrapper::can_update_pointer_icon() const {
+ return _set_pointer_icon != nullptr;
+}
+
+bool GodotJavaViewWrapper::can_capture_pointer() const {
+ return _request_pointer_capture != nullptr && _release_pointer_capture != nullptr;
+}
+
void GodotJavaViewWrapper::request_pointer_capture() {
if (_request_pointer_capture != nullptr) {
JNIEnv *env = get_jni_env();
diff --git a/platform/android/java_godot_view_wrapper.h b/platform/android/java_godot_view_wrapper.h
index c52f459d64..b398c73cac 100644
--- a/platform/android/java_godot_view_wrapper.h
+++ b/platform/android/java_godot_view_wrapper.h
@@ -50,6 +50,9 @@ private:
public:
GodotJavaViewWrapper(jobject godot_view);
+ bool can_update_pointer_icon() const;
+ bool can_capture_pointer() const;
+
void request_pointer_capture();
void release_pointer_capture();
void set_pointer_icon(int pointer_type);
diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp
index e3456fe4e4..416b98c895 100644
--- a/platform/android/java_godot_wrapper.cpp
+++ b/platform/android/java_godot_wrapper.cpp
@@ -58,7 +58,7 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_
}
// get some Godot method pointers...
- _on_video_init = p_env->GetMethodID(godot_class, "onVideoInit", "()V");
+ _on_video_init = p_env->GetMethodID(godot_class, "onVideoInit", "()Z");
_restart = p_env->GetMethodID(godot_class, "restart", "()V");
_finish = p_env->GetMethodID(godot_class, "forceQuit", "()V");
_set_keep_screen_on = p_env->GetMethodID(godot_class, "setKeepScreenOn", "(Z)V");
@@ -78,13 +78,23 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_
_on_godot_setup_completed = p_env->GetMethodID(godot_class, "onGodotSetupCompleted", "()V");
_on_godot_main_loop_started = p_env->GetMethodID(godot_class, "onGodotMainLoopStarted", "()V");
_create_new_godot_instance = p_env->GetMethodID(godot_class, "createNewGodotInstance", "([Ljava/lang/String;)V");
+ _get_render_view = p_env->GetMethodID(godot_class, "getRenderView", "()Lorg/godotengine/godot/GodotRenderView;");
// get some Activity method pointers...
_get_class_loader = p_env->GetMethodID(activity_class, "getClassLoader", "()Ljava/lang/ClassLoader;");
}
GodotJavaWrapper::~GodotJavaWrapper() {
- // nothing to do here for now
+ if (godot_view) {
+ delete godot_view;
+ }
+
+ JNIEnv *env = get_jni_env();
+ ERR_FAIL_NULL(env);
+ env->DeleteGlobalRef(godot_instance);
+ env->DeleteGlobalRef(godot_class);
+ env->DeleteGlobalRef(activity);
+ env->DeleteGlobalRef(activity_class);
}
jobject GodotJavaWrapper::get_activity() {
@@ -115,24 +125,29 @@ jobject GodotJavaWrapper::get_class_loader() {
}
GodotJavaViewWrapper *GodotJavaWrapper::get_godot_view() {
- if (_godot_view != nullptr) {
- return _godot_view;
+ if (godot_view != nullptr) {
+ return godot_view;
}
- JNIEnv *env = get_jni_env();
- ERR_FAIL_NULL_V(env, nullptr);
- jmethodID godot_view_getter = env->GetMethodID(godot_class, "getRenderView", "()Lorg/godotengine/godot/GodotRenderView;");
- _godot_view = new GodotJavaViewWrapper(env->CallObjectMethod(godot_instance, godot_view_getter));
- return _godot_view;
+ if (_get_render_view) {
+ JNIEnv *env = get_jni_env();
+ ERR_FAIL_NULL_V(env, nullptr);
+ jobject godot_render_view = env->CallObjectMethod(godot_instance, _get_render_view);
+ if (!env->IsSameObject(godot_render_view, nullptr)) {
+ godot_view = new GodotJavaViewWrapper(godot_render_view);
+ }
+ }
+ return godot_view;
}
-void GodotJavaWrapper::on_video_init(JNIEnv *p_env) {
+bool GodotJavaWrapper::on_video_init(JNIEnv *p_env) {
if (_on_video_init) {
if (p_env == nullptr) {
p_env = get_jni_env();
}
- ERR_FAIL_NULL(p_env);
- p_env->CallVoidMethod(godot_instance, _on_video_init);
+ ERR_FAIL_NULL_V(p_env, false);
+ return p_env->CallBooleanMethod(godot_instance, _on_video_init);
}
+ return false;
}
void GodotJavaWrapper::on_godot_setup_completed(JNIEnv *p_env) {
diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h
index bbf7c0ae33..fb9c4c77fc 100644
--- a/platform/android/java_godot_wrapper.h
+++ b/platform/android/java_godot_wrapper.h
@@ -28,9 +28,6 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-// note, swapped java and godot around in the file name so all the java
-// wrappers are together
-
#ifndef JAVA_GODOT_WRAPPER_H
#define JAVA_GODOT_WRAPPER_H
@@ -49,7 +46,7 @@ private:
jclass godot_class;
jclass activity_class;
- GodotJavaViewWrapper *_godot_view = nullptr;
+ GodotJavaViewWrapper *godot_view = nullptr;
jmethodID _on_video_init = nullptr;
jmethodID _restart = nullptr;
@@ -72,6 +69,7 @@ private:
jmethodID _on_godot_main_loop_started = nullptr;
jmethodID _get_class_loader = nullptr;
jmethodID _create_new_godot_instance = nullptr;
+ jmethodID _get_render_view = nullptr;
public:
GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_godot_instance);
@@ -83,7 +81,7 @@ public:
jobject get_class_loader();
GodotJavaViewWrapper *get_godot_view();
- void on_video_init(JNIEnv *p_env = nullptr);
+ bool on_video_init(JNIEnv *p_env = nullptr);
void on_godot_setup_completed(JNIEnv *p_env = nullptr);
void on_godot_main_loop_started(JNIEnv *p_env = nullptr);
void restart(JNIEnv *p_env = nullptr);
diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp
index 142dc54c45..4469c7a0f7 100644
--- a/platform/android/os_android.cpp
+++ b/platform/android/os_android.cpp
@@ -44,6 +44,7 @@
#include "net_socket_android.h"
#include <dlfcn.h>
+#include <sys/system_properties.h>
#include "java_godot_io_wrapper.h"
#include "java_godot_wrapper.h"
@@ -175,6 +176,79 @@ String OS_Android::get_name() const {
return "Android";
}
+String OS_Android::get_system_property(const char *key) const {
+ static String value;
+ char value_str[PROP_VALUE_MAX];
+ if (__system_property_get(key, value_str)) {
+ value = String(value_str);
+ }
+ return value;
+}
+
+String OS_Android::get_distribution_name() const {
+ if (!get_system_property("ro.havoc.version").is_empty()) {
+ return "Havoc OS";
+ } else if (!get_system_property("org.pex.version").is_empty()) { // Putting before "Pixel Experience", because it's derivating from it.
+ return "Pixel Extended";
+ } else if (!get_system_property("org.pixelexperience.version").is_empty()) {
+ return "Pixel Experience";
+ } else if (!get_system_property("ro.potato.version").is_empty()) {
+ return "POSP";
+ } else if (!get_system_property("ro.xtended.version").is_empty()) {
+ return "Project-Xtended";
+ } else if (!get_system_property("org.evolution.version").is_empty()) {
+ return "Evolution X";
+ } else if (!get_system_property("ro.corvus.version").is_empty()) {
+ return "Corvus-Q";
+ } else if (!get_system_property("ro.pa.version").is_empty()) {
+ return "Paranoid Android";
+ } else if (!get_system_property("ro.crdroid.version").is_empty()) {
+ return "crDroid Android";
+ } else if (!get_system_property("ro.syberia.version").is_empty()) {
+ return "Syberia Project";
+ } else if (!get_system_property("ro.arrow.version").is_empty()) {
+ return "ArrowOS";
+ } else if (!get_system_property("ro.lineage.version").is_empty()) { // Putting LineageOS last, just in case any derivative writes to "ro.lineage.version".
+ return "LineageOS";
+ }
+
+ if (!get_system_property("ro.modversion").is_empty()) { // Handles other Android custom ROMs.
+ return vformat("%s %s", get_name(), "Custom ROM");
+ }
+
+ // Handles stock Android.
+ return get_name();
+}
+
+String OS_Android::get_version() const {
+ const Vector<const char *> roms = { "ro.havoc.version", "org.pex.version", "org.pixelexperience.version",
+ "ro.potato.version", "ro.xtended.version", "org.evolution.version", "ro.corvus.version", "ro.pa.version",
+ "ro.crdroid.version", "ro.syberia.version", "ro.arrow.version", "ro.lineage.version" };
+ for (int i = 0; i < roms.size(); i++) {
+ static String rom_version = get_system_property(roms[i]);
+ if (!rom_version.is_empty()) {
+ return rom_version;
+ }
+ }
+
+ static String mod_version = get_system_property("ro.modversion"); // Handles other Android custom ROMs.
+ if (!mod_version.is_empty()) {
+ return mod_version;
+ }
+
+ // Handles stock Android.
+ static String sdk_version = get_system_property("ro.build.version.sdk_int");
+ static String build = get_system_property("ro.build.version.incremental");
+ if (!sdk_version.is_empty()) {
+ if (!build.is_empty()) {
+ return vformat("%s.%s", sdk_version, build);
+ }
+ return sdk_version;
+ }
+
+ return "";
+}
+
MainLoop *OS_Android::get_main_loop() const {
return main_loop;
}
diff --git a/platform/android/os_android.h b/platform/android/os_android.h
index 96c06d715c..d6546a3507 100644
--- a/platform/android/os_android.h
+++ b/platform/android/os_android.h
@@ -65,6 +65,8 @@ private:
GodotJavaWrapper *godot_java = nullptr;
GodotIOJavaWrapper *godot_io_java = nullptr;
+ String get_system_property(const char *key) const;
+
public:
static const char *ANDROID_EXEC_PATH;
@@ -93,6 +95,8 @@ public:
virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false, String *r_resolved_path = nullptr) override;
virtual String get_name() const override;
+ virtual String get_distribution_name() const override;
+ virtual String get_version() const override;
virtual MainLoop *get_main_loop() const override;
void main_loop_begin();
diff --git a/platform/android/thread_jandroid.cpp b/platform/android/thread_jandroid.cpp
index 61b471866f..9f87303341 100644
--- a/platform/android/thread_jandroid.cpp
+++ b/platform/android/thread_jandroid.cpp
@@ -63,7 +63,7 @@ static void term_thread() {
void init_thread_jandroid(JavaVM *p_jvm, JNIEnv *p_env) {
java_vm = p_jvm;
env = p_env;
- Thread::_set_platform_funcs(nullptr, nullptr, &init_thread, &term_thread);
+ Thread::_set_platform_functions({ .init = init_thread, .term = &term_thread });
}
void setup_android_thread() {
diff --git a/platform/ios/detect.py b/platform/ios/detect.py
index 1a8d24d12d..38e62134b5 100644
--- a/platform/ios/detect.py
+++ b/platform/ios/detect.py
@@ -2,6 +2,11 @@ import os
import sys
from methods import detect_darwin_sdk_path
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from SCons import Environment
+
def is_active():
return True
@@ -37,12 +42,12 @@ def get_opts():
def get_flags():
return [
("arch", "arm64"), # Default for convenience.
- ("tools", False),
+ ("target", "template_debug"),
("use_volk", False),
]
-def configure(env):
+def configure(env: "Environment"):
# Validate arch.
supported_arches = ["x86_64", "arm64"]
if env["arch"] not in supported_arches:
@@ -52,27 +57,18 @@ def configure(env):
)
sys.exit()
- ## Build type
+ ## LTO
- if env["target"].startswith("release"):
- env.Append(CPPDEFINES=["NDEBUG", ("NS_BLOCK_ASSERTIONS", 1)])
- if env["optimize"] == "speed": # optimize for speed (default)
- # `-O2` is more friendly to debuggers than `-O3`, leading to better crash backtraces
- # when using `target=release_debug`.
- opt = "-O3" if env["target"] == "release" else "-O2"
- env.Append(CCFLAGS=[opt, "-ftree-vectorize", "-fomit-frame-pointer"])
- env.Append(LINKFLAGS=[opt])
- elif env["optimize"] == "size": # optimize for size
- env.Append(CCFLAGS=["-Os", "-ftree-vectorize"])
- env.Append(LINKFLAGS=["-Os"])
+ if env["lto"] == "auto": # Disable by default as it makes linking in Xcode very slow.
+ env["lto"] = "none"
- elif env["target"] == "debug":
- env.Append(CCFLAGS=["-gdwarf-2", "-O0"])
- env.Append(CPPDEFINES=["_DEBUG", ("DEBUG", 1)])
-
- if env["use_lto"]:
- env.Append(CCFLAGS=["-flto"])
- env.Append(LINKFLAGS=["-flto"])
+ if env["lto"] != "none":
+ if env["lto"] == "thin":
+ env.Append(CCFLAGS=["-flto=thin"])
+ env.Append(LINKFLAGS=["-flto=thin"])
+ else:
+ env.Append(CCFLAGS=["-flto"])
+ env.Append(LINKFLAGS=["-flto"])
## Compiler configuration
@@ -112,6 +108,10 @@ def configure(env):
env.Append(CCFLAGS=["-miphoneos-version-min=11.0"])
if env["arch"] == "x86_64":
+ if not env["ios_simulator"]:
+ print("ERROR: Building for iOS with 'arch=x86_64' requires 'ios_simulator=yes'.")
+ sys.exit(255)
+
env["ENV"]["MACOSX_DEPLOYMENT_TARGET"] = "10.9"
env.Append(
CCFLAGS=(
@@ -133,12 +133,10 @@ def configure(env):
env.Append(ASFLAGS=["-arch", "arm64"])
env.Append(CPPDEFINES=["NEED_LONG_INT"])
- # Disable exceptions on non-tools (template) builds
- if not env["tools"]:
- if env["ios_exceptions"]:
- env.Append(CCFLAGS=["-fexceptions"])
- else:
- env.Append(CCFLAGS=["-fno-exceptions"])
+ if env["ios_exceptions"]:
+ env.Append(CCFLAGS=["-fexceptions"])
+ else:
+ env.Append(CCFLAGS=["-fno-exceptions"])
# Temp fix for ABS/MAX/MIN macros in iOS SDK blocking compilation
env.Append(CCFLAGS=["-Wno-ambiguous-macro"])
diff --git a/platform/ios/display_layer.mm b/platform/ios/display_layer.mm
index 7c83494768..74c760ae9a 100644
--- a/platform/ios/display_layer.mm
+++ b/platform/ios/display_layer.mm
@@ -89,12 +89,12 @@
// FIXME: Add Vulkan support via MoltenVK. Add fallback code back?
- // Create GL ES 2 context
- if (GLOBAL_GET("rendering/driver/driver_name") == "opengl3") {
- context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
- NSLog(@"Setting up an OpenGL ES 2.0 context.");
+ // Create GL ES 3 context
+ if (GLOBAL_GET("rendering/renderer/rendering_method") == "gl_compatibility") {
+ context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
+ NSLog(@"Setting up an OpenGL ES 3.0 context.");
if (!context) {
- NSLog(@"Failed to create OpenGL ES 2.0 context!");
+ NSLog(@"Failed to create OpenGL ES 3.0 context!");
return;
}
}
diff --git a/platform/ios/display_server_ios.mm b/platform/ios/display_server_ios.mm
index 74d6bc2e97..d3a0f38463 100644
--- a/platform/ios/display_server_ios.mm
+++ b/platform/ios/display_server_ios.mm
@@ -62,7 +62,7 @@ DisplayServerIOS::DisplayServerIOS(const String &p_rendering_driver, WindowMode
// Note that we should be checking "opengl3" as the driver, might never enable this seeing OpenGL is deprecated on iOS
// We are hardcoding the rendering_driver to "vulkan" down below
- if (rendering_driver == "opengl_es") {
+ if (rendering_driver == "opengl3") {
bool gl_initialization_error = false;
// FIXME: Add Vulkan support via MoltenVK. Add fallback code back?
@@ -163,7 +163,7 @@ Vector<String> DisplayServerIOS::get_rendering_drivers_func() {
drivers.push_back("vulkan");
#endif
#if defined(GLES3_ENABLED)
- drivers.push_back("opengl_es");
+ drivers.push_back("opengl3");
#endif
return drivers;
diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp
index 7aacb2de85..74a57dc614 100644
--- a/platform/ios/export/export_plugin.cpp
+++ b/platform/ios/export/export_plugin.cpp
@@ -34,7 +34,6 @@
#include "editor/editor_node.h"
void EditorExportPlatformIOS::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const {
- String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name");
// Vulkan and OpenGL ES 3.0 both mandate ETC2 support.
r_features->push_back("etc2");
diff --git a/platform/ios/godot_view.mm b/platform/ios/godot_view.mm
index 9ed219508c..ff90c05b1d 100644
--- a/platform/ios/godot_view.mm
+++ b/platform/ios/godot_view.mm
@@ -74,7 +74,7 @@ static const float earth_gravity = 9.80665;
if ([driverName isEqualToString:@"vulkan"]) {
layer = [GodotMetalLayer layer];
- } else if ([driverName isEqualToString:@"opengl_es"]) {
+ } else if ([driverName isEqualToString:@"opengl3"]) {
if (@available(iOS 13, *)) {
NSLog(@"OpenGL ES is deprecated on iOS 13");
}
diff --git a/platform/ios/os_ios.h b/platform/ios/os_ios.h
index 3b88f53b6a..400040875f 100644
--- a/platform/ios/os_ios.h
+++ b/platform/ios/os_ios.h
@@ -28,11 +28,11 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifdef IOS_ENABLED
-
#ifndef OS_IOS_H
#define OS_IOS_H
+#ifdef IOS_ENABLED
+
#include "drivers/coreaudio/audio_driver_coreaudio.h"
#include "drivers/unix/os_unix.h"
#include "ios.h"
@@ -100,6 +100,8 @@ public:
virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String p_name, void *&p_symbol_handle, bool p_optional = false) override;
virtual String get_name() const override;
+ virtual String get_distribution_name() const override;
+ virtual String get_version() const override;
virtual String get_model_name() const override;
virtual Error shell_open(String p_uri) override;
@@ -122,6 +124,6 @@ public:
void on_focus_in();
};
-#endif // OS_IOS_H
+#endif // IOS_ENABLED
#endif // OS_IOS_H
diff --git a/platform/ios/os_ios.mm b/platform/ios/os_ios.mm
index b9d186f355..a674498620 100644
--- a/platform/ios/os_ios.mm
+++ b/platform/ios/os_ios.mm
@@ -240,6 +240,15 @@ String OS_IOS::get_name() const {
return "iOS";
}
+String OS_IOS::get_distribution_name() const {
+ return get_name();
+}
+
+String OS_IOS::get_version() const {
+ NSOperatingSystemVersion ver = [NSProcessInfo processInfo].operatingSystemVersion;
+ return vformat("%d.%d.%d", (int64_t)ver.majorVersion, (int64_t)ver.minorVersion, (int64_t)ver.patchVersion);
+}
+
String OS_IOS::get_model_name() const {
String model = ios->get_model();
if (model != "") {
diff --git a/platform/ios/tts_ios.mm b/platform/ios/tts_ios.mm
index a079d02add..8319cad117 100644
--- a/platform/ios/tts_ios.mm
+++ b/platform/ios/tts_ios.mm
@@ -78,12 +78,12 @@
AVSpeechUtterance *new_utterance = [[AVSpeechUtterance alloc] initWithString:[NSString stringWithUTF8String:message.text.utf8().get_data()]];
[new_utterance setVoice:[AVSpeechSynthesisVoice voiceWithIdentifier:[NSString stringWithUTF8String:message.voice.utf8().get_data()]]];
if (message.rate > 1.f) {
- [new_utterance setRate:Math::range_lerp(message.rate, 1.f, 10.f, AVSpeechUtteranceDefaultSpeechRate, AVSpeechUtteranceMaximumSpeechRate)];
+ [new_utterance setRate:Math::remap(message.rate, 1.f, 10.f, AVSpeechUtteranceDefaultSpeechRate, AVSpeechUtteranceMaximumSpeechRate)];
} else if (message.rate < 1.f) {
- [new_utterance setRate:Math::range_lerp(message.rate, 0.1f, 1.f, AVSpeechUtteranceMinimumSpeechRate, AVSpeechUtteranceDefaultSpeechRate)];
+ [new_utterance setRate:Math::remap(message.rate, 0.1f, 1.f, AVSpeechUtteranceMinimumSpeechRate, AVSpeechUtteranceDefaultSpeechRate)];
}
[new_utterance setPitchMultiplier:message.pitch];
- [new_utterance setVolume:(Math::range_lerp(message.volume, 0.f, 100.f, 0.f, 1.f))];
+ [new_utterance setVolume:(Math::remap(message.volume, 0.f, 100.f, 0.f, 1.f))];
ids[new_utterance] = message.id;
[av_synth speakUtterance:new_utterance];
diff --git a/platform/linuxbsd/crash_handler_linuxbsd.cpp b/platform/linuxbsd/crash_handler_linuxbsd.cpp
index 33da094860..8c8c8588b8 100644
--- a/platform/linuxbsd/crash_handler_linuxbsd.cpp
+++ b/platform/linuxbsd/crash_handler_linuxbsd.cpp
@@ -89,7 +89,7 @@ static void handle_crash(int sig) {
// Try to demangle the function name to provide a more readable one
if (dladdr(bt_buffer[i], &info) && info.dli_sname) {
if (info.dli_sname[0] == '_') {
- int status;
+ int status = 0;
char *demangled = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status);
if (status == 0 && demangled) {
diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py
index f5f7e65417..86ae1f2d9c 100644
--- a/platform/linuxbsd/detect.py
+++ b/platform/linuxbsd/detect.py
@@ -4,6 +4,11 @@ import sys
from methods import get_compiler_version, using_gcc
from platform_methods import detect_arch
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from SCons import Environment
+
def is_active():
return True
@@ -31,7 +36,6 @@ def get_opts():
return [
EnumVariable("linker", "Linker program", "default", ("default", "bfd", "gold", "lld", "mold")),
BoolVariable("use_llvm", "Use the LLVM compiler", False),
- BoolVariable("use_thinlto", "Use ThinLTO (LLVM only, requires linker=lld, implies use_lto=yes)", False),
BoolVariable("use_static_cpp", "Link libgcc and libstdc++ statically for better portability", True),
BoolVariable("use_coverage", "Test Godot coverage", False),
BoolVariable("use_ubsan", "Use LLVM/GCC compiler undefined behavior sanitizer (UBSAN)", False),
@@ -45,8 +49,6 @@ def get_opts():
BoolVariable("fontconfig", "Detect and use fontconfig for system fonts support", True),
BoolVariable("udev", "Use udev for gamepad connection callbacks", True),
BoolVariable("x11", "Enable X11 display", True),
- BoolVariable("debug_symbols", "Add debugging symbols to release/release_debug builds", True),
- BoolVariable("separate_debug_symbols", "Create a separate file containing debugging symbols", False),
BoolVariable("touch", "Enable touch events", True),
BoolVariable("execinfo", "Use libexecinfo on systems where glibc is not available", False),
]
@@ -58,7 +60,7 @@ def get_flags():
]
-def configure(env):
+def configure(env: "Environment"):
# Validate arch.
supported_arches = ["x86_32", "x86_64", "arm32", "arm64", "rv64", "ppc32", "ppc64"]
if env["arch"] not in supported_arches:
@@ -70,26 +72,9 @@ def configure(env):
## Build type
- if env["target"] == "release":
- if env["optimize"] == "speed": # optimize for speed (default)
- env.Prepend(CCFLAGS=["-O3"])
- elif env["optimize"] == "size": # optimize for size
- env.Prepend(CCFLAGS=["-Os"])
-
- if env["debug_symbols"]:
- env.Prepend(CCFLAGS=["-g2"])
-
- elif env["target"] == "release_debug":
- if env["optimize"] == "speed": # optimize for speed (default)
- env.Prepend(CCFLAGS=["-O2"])
- elif env["optimize"] == "size": # optimize for size
- env.Prepend(CCFLAGS=["-Os"])
-
- if env["debug_symbols"]:
- env.Prepend(CCFLAGS=["-g2"])
-
- elif env["target"] == "debug":
- env.Prepend(CCFLAGS=["-g3"])
+ if env.dev_build:
+ # This is needed for our crash handler to work properly.
+ # gdb works fine without it though, so maybe our crash handler could too.
env.Append(LINKFLAGS=["-rdynamic"])
# CPU architecture flags.
@@ -129,13 +114,6 @@ def configure(env):
else:
env.Append(LINKFLAGS=["-fuse-ld=%s" % env["linker"]])
- if env["use_thinlto"]:
- if not env["use_llvm"] or env["linker"] != "lld":
- print("ThinLTO is only compatible with LLVM and the LLD linker, use `use_llvm=yes linker=lld`.")
- sys.exit(255)
- else:
- env["use_lto"] = True # ThinLTO implies LTO
-
if env["use_coverage"]:
env.Append(CCFLAGS=["-ftest-coverage", "-fprofile-arcs"])
env.Append(LINKFLAGS=["-ftest-coverage", "-fprofile-arcs"])
@@ -178,8 +156,16 @@ def configure(env):
env.Append(CCFLAGS=["-fsanitize-recover=memory"])
env.Append(LINKFLAGS=["-fsanitize=memory"])
- if env["use_lto"]:
- if env["use_thinlto"]:
+ # LTO
+
+ if env["lto"] == "auto": # Full LTO for production.
+ env["lto"] = "full"
+
+ if env["lto"] != "none":
+ if env["lto"] == "thin":
+ if not env["use_llvm"]:
+ print("ThinLTO is only compatible with LLVM, use `use_llvm=yes` or `lto=full`.")
+ sys.exit(255)
env.Append(CCFLAGS=["-flto=thin"])
env.Append(LINKFLAGS=["-flto=thin"])
elif not env["use_llvm"] and env.GetOption("num_jobs") > 1:
@@ -377,13 +363,15 @@ def configure(env):
if platform.system() == "Linux":
env.Append(LIBS=["dl"])
- if platform.system().find("BSD") >= 0:
- env["execinfo"] = True
+ if not env["execinfo"] and platform.libc_ver()[0] != "glibc":
+ # The default crash handler depends on glibc, so if the host uses
+ # a different libc (BSD libc, musl), fall back to libexecinfo.
+ print("Note: Using `execinfo=yes` for the crash handler as required on platforms where glibc is missing.")
if env["execinfo"]:
env.Append(LIBS=["execinfo"])
- if not env["tools"]:
+ if not env.editor_build:
import subprocess
import re
diff --git a/platform/linuxbsd/detect_prime_x11.h b/platform/linuxbsd/detect_prime_x11.h
index 21ebaead32..7eb7064cc5 100644
--- a/platform/linuxbsd/detect_prime_x11.h
+++ b/platform/linuxbsd/detect_prime_x11.h
@@ -28,11 +28,13 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifdef X11_ENABLED
-#if defined(GLES3_ENABLED)
+#ifndef DETECT_PRIME_X11_H
+#define DETECT_PRIME_X11_H
+
+#if defined(X11_ENABLED) && defined(GLES3_ENABLED)
int detect_prime();
-#endif
+#endif // X11_ENABLED && GLES3_ENABLED
#endif // DETECT_PRIME_X11_H
diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp
index c619e8eceb..5256d91a80 100644
--- a/platform/linuxbsd/display_server_x11.cpp
+++ b/platform/linuxbsd/display_server_x11.cpp
@@ -49,10 +49,14 @@
#include "drivers/gles3/rasterizer_gles3.h"
#endif
+#include <dlfcn.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
@@ -66,17 +70,6 @@
#define _NET_WM_STATE_REMOVE 0L // remove/unset property
#define _NET_WM_STATE_ADD 1L // add/set property
-#include <dlfcn.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-//stupid linux.h
-#ifdef KEY_TAB
-#undef KEY_TAB
-#endif
-
#undef CursorShape
#include <X11/XKBlib.h>
@@ -397,7 +390,10 @@ void DisplayServerX11::mouse_set_mode(MouseMode p_mode) {
if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) {
//flush pending motion events
_flush_mouse_motion();
- WindowID window_id = windows.has(last_focused_window) ? last_focused_window : MAIN_WINDOW_ID;
+ WindowID window_id = _get_focused_window_or_popup();
+ if (!windows.has(window_id)) {
+ window_id = MAIN_WINDOW_ID;
+ }
WindowData &window = windows[window_id];
if (XGrabPointer(
@@ -433,7 +429,11 @@ void DisplayServerX11::warp_mouse(const Point2i &p_position) {
if (mouse_mode == MOUSE_MODE_CAPTURED) {
last_mouse_pos = p_position;
} else {
- WindowID window_id = windows.has(last_focused_window) ? last_focused_window : MAIN_WINDOW_ID;
+ WindowID window_id = _get_focused_window_or_popup();
+ if (!windows.has(window_id)) {
+ window_id = MAIN_WINDOW_ID;
+ }
+
XWarpPointer(x11_display, None, windows[window_id].x11_window,
0, 0, 0, 0, (int)p_position.x, (int)p_position.y);
}
@@ -1015,7 +1015,7 @@ Rect2i DisplayServerX11::screen_get_usable_rect(int p_screen) const {
Rect2i left_rect(pos.x, pos.y + left_start_y, left, left_end_y - left_start_y);
if (left_rect.size.x > 0) {
Rect2i intersection = rect.intersection(left_rect);
- if (!intersection.has_no_area() && intersection.size.x < rect.size.x) {
+ if (intersection.has_area() && intersection.size.x < rect.size.x) {
rect.position.x = left_rect.size.x;
rect.size.x = rect.size.x - intersection.size.x;
}
@@ -1024,7 +1024,7 @@ Rect2i DisplayServerX11::screen_get_usable_rect(int p_screen) const {
Rect2i right_rect(pos.x + size.x - right, pos.y + right_start_y, right, right_end_y - right_start_y);
if (right_rect.size.x > 0) {
Rect2i intersection = rect.intersection(right_rect);
- if (!intersection.has_no_area() && right_rect.size.x < rect.size.x) {
+ if (intersection.has_area() && right_rect.size.x < rect.size.x) {
rect.size.x = intersection.position.x - rect.position.x;
}
}
@@ -1032,7 +1032,7 @@ Rect2i DisplayServerX11::screen_get_usable_rect(int p_screen) const {
Rect2i top_rect(pos.x + top_start_x, pos.y, top_end_x - top_start_x, top);
if (top_rect.size.y > 0) {
Rect2i intersection = rect.intersection(top_rect);
- if (!intersection.has_no_area() && intersection.size.y < rect.size.y) {
+ if (intersection.has_area() && intersection.size.y < rect.size.y) {
rect.position.y = top_rect.size.y;
rect.size.y = rect.size.y - intersection.size.y;
}
@@ -1041,7 +1041,7 @@ Rect2i DisplayServerX11::screen_get_usable_rect(int p_screen) const {
Rect2i bottom_rect(pos.x + bottom_start_x, pos.y + size.y - bottom, bottom_end_x - bottom_start_x, bottom);
if (bottom_rect.size.y > 0) {
Rect2i intersection = rect.intersection(bottom_rect);
- if (!intersection.has_no_area() && right_rect.size.y < rect.size.y) {
+ if (intersection.has_area() && right_rect.size.y < rect.size.y) {
rect.size.y = intersection.position.y - rect.position.y;
}
}
@@ -3135,9 +3135,14 @@ void DisplayServerX11::_window_changed(XEvent *event) {
return;
}
+ // Query display server about a possible new window state.
+ wd.fullscreen = _window_fullscreen_check(window_id);
+ wd.minimized = _window_minimize_check(window_id);
+ wd.maximized = _window_maximize_check(window_id, "_NET_WM_STATE");
+
{
//the position in xconfigure is not useful here, obtain it manually
- int x, y;
+ int x = 0, y = 0;
Window child;
XTranslateCoordinates(x11_display, wd.x11_window, DefaultRootWindow(x11_display), 0, 0, &x, &y, &child);
new_rect.position.x = x;
@@ -3181,6 +3186,15 @@ void DisplayServerX11::_window_changed(XEvent *event) {
}
}
+DisplayServer::WindowID DisplayServerX11::_get_focused_window_or_popup() const {
+ const List<WindowID>::Element *E = popup_list.back();
+ if (E) {
+ return E->get();
+ }
+
+ return last_focused_window;
+}
+
void DisplayServerX11::_dispatch_input_events(const Ref<InputEvent> &p_event) {
static_cast<DisplayServerX11 *>(get_singleton())->_dispatch_input_event(p_event);
}
@@ -3291,7 +3305,7 @@ void DisplayServerX11::_check_pending_events(LocalVector<XEvent> &r_events) {
XFlush(x11_display);
// Non-blocking wait for next event and remove it from the queue.
- XEvent ev;
+ XEvent ev = {};
while (XCheckIfEvent(x11_display, &ev, _predicate_all_events, nullptr)) {
// Check if the input manager wants to process the event.
if (XFilterEvent(&ev, None)) {
@@ -3936,7 +3950,11 @@ void DisplayServerX11::process_events() {
// The X11 API requires filtering one-by-one through the motion
// notify events, in order to figure out which event is the one
// generated by warping the mouse pointer.
- WindowID focused_window_id = windows.has(last_focused_window) ? last_focused_window : MAIN_WINDOW_ID;
+ WindowID focused_window_id = _get_focused_window_or_popup();
+ if (!windows.has(focused_window_id)) {
+ focused_window_id = MAIN_WINDOW_ID;
+ }
+
while (true) {
if (mouse_mode == MOUSE_MODE_CAPTURED && event.xmotion.x == windows[focused_window_id].size.width / 2 && event.xmotion.y == windows[focused_window_id].size.height / 2) {
//this is likely the warp event since it was warped here
diff --git a/platform/linuxbsd/display_server_x11.h b/platform/linuxbsd/display_server_x11.h
index ea03b2328c..a5fa7613bc 100644
--- a/platform/linuxbsd/display_server_x11.h
+++ b/platform/linuxbsd/display_server_x11.h
@@ -284,6 +284,8 @@ class DisplayServerX11 : public DisplayServer {
Context context = CONTEXT_ENGINE;
+ WindowID _get_focused_window_or_popup() const;
+
void _send_window_event(const WindowData &wd, WindowEvent p_event);
static void _dispatch_input_events(const Ref<InputEvent> &p_event);
void _dispatch_input_event(const Ref<InputEvent> &p_event);
diff --git a/platform/linuxbsd/gl_manager_x11.cpp b/platform/linuxbsd/gl_manager_x11.cpp
index 04c1df71fb..f586c57dda 100644
--- a/platform/linuxbsd/gl_manager_x11.cpp
+++ b/platform/linuxbsd/gl_manager_x11.cpp
@@ -256,7 +256,11 @@ void GLManager_X11::release_current() {
if (!_current_window) {
return;
}
- glXMakeCurrent(_x_windisp.x11_display, None, nullptr);
+
+ if (!glXMakeCurrent(_x_windisp.x11_display, None, nullptr)) {
+ ERR_PRINT("glXMakeCurrent failed");
+ }
+ _current_window = nullptr;
}
void GLManager_X11::window_make_current(DisplayServer::WindowID p_window_id) {
@@ -276,7 +280,9 @@ void GLManager_X11::window_make_current(DisplayServer::WindowID p_window_id) {
const GLDisplay &disp = get_display(win.gldisplay_id);
- glXMakeCurrent(disp.x11_display, win.x11_window, disp.context->glx_context);
+ if (!glXMakeCurrent(disp.x11_display, win.x11_window, disp.context->glx_context)) {
+ ERR_PRINT("glXMakeCurrent failed");
+ }
_internal_set_current_window(&win);
}
@@ -290,13 +296,12 @@ void GLManager_X11::make_current() {
return;
}
const GLDisplay &disp = get_current_display();
- glXMakeCurrent(_x_windisp.x11_display, _x_windisp.x11_window, disp.context->glx_context);
+ if (!glXMakeCurrent(_x_windisp.x11_display, _x_windisp.x11_window, disp.context->glx_context)) {
+ ERR_PRINT("glXMakeCurrent failed");
+ }
}
void GLManager_X11::swap_buffers() {
- // NO NEED TO CALL SWAP BUFFERS for each window...
- // see https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glXSwapBuffers.xml
-
if (!_current_window) {
return;
}
@@ -305,7 +310,7 @@ void GLManager_X11::swap_buffers() {
return;
}
- // On X11, when enabled, transparancy is always active, so clear alpha manually.
+ // On X11, when enabled, transparency is always active, so clear alpha manually.
if (OS::get_singleton()->is_layered_allowed()) {
if (!DisplayServer::get_singleton()->window_get_flag(DisplayServer::WINDOW_FLAG_TRANSPARENT, _current_window->window_id)) {
glColorMask(false, false, false, true);
@@ -315,13 +320,6 @@ void GLManager_X11::swap_buffers() {
}
}
- // print_line("\tswap_buffers");
-
- // only for debugging without drawing anything
- // glClearColor(Math::randf(), 0, 1, 1);
- //glClear(GL_COLOR_BUFFER_BIT);
-
- //const GLDisplay &disp = get_current_display();
glXSwapBuffers(_x_windisp.x11_display, _x_windisp.x11_window);
}
diff --git a/platform/linuxbsd/key_mapping_x11.cpp b/platform/linuxbsd/key_mapping_x11.cpp
index 047ee74671..f774c99d99 100644
--- a/platform/linuxbsd/key_mapping_x11.cpp
+++ b/platform/linuxbsd/key_mapping_x11.cpp
@@ -108,17 +108,21 @@ static _XTranslatePair _xkeysym_to_keycode[] = {
{ XK_KP_7, Key::KP_7 },
{ XK_KP_8, Key::KP_8 },
{ XK_KP_9, Key::KP_9 },
- // same but with numlock
- { XK_KP_Insert, Key::KP_0 },
- { XK_KP_End, Key::KP_1 },
- { XK_KP_Down, Key::KP_2 },
- { XK_KP_Page_Down, Key::KP_3 },
- { XK_KP_Left, Key::KP_4 },
- { XK_KP_Begin, Key::KP_5 },
- { XK_KP_Right, Key::KP_6 },
- { XK_KP_Home, Key::KP_7 },
- { XK_KP_Up, Key::KP_8 },
- { XK_KP_Page_Up, Key::KP_9 },
+ // same keys but with numlock off
+ { XK_KP_Insert, Key::INSERT },
+ { XK_KP_End, Key::END },
+ { XK_KP_Down, Key::DOWN },
+ { XK_KP_Page_Down, Key::PAGEDOWN },
+ { XK_KP_Left, Key::LEFT },
+ // X11 documents this (numpad 5) as "begin of line" but no toolkit
+ // seems to interpret it this way.
+ // On Windows this is emitting Key::Clear so for consistency it
+ // will be mapped to Key::Clear
+ { XK_KP_Begin, Key::CLEAR },
+ { XK_KP_Right, Key::RIGHT },
+ { XK_KP_Home, Key::HOME },
+ { XK_KP_Up, Key::UP },
+ { XK_KP_Page_Up, Key::PAGEUP },
{ XK_F1, Key::F1 },
{ XK_F2, Key::F2 },
{ XK_F3, Key::F3 },
diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp
index 61faf3061c..4cbd9722ad 100644
--- a/platform/linuxbsd/os_linuxbsd.cpp
+++ b/platform/linuxbsd/os_linuxbsd.cpp
@@ -42,14 +42,13 @@
#include <mntent.h>
#endif
+#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-
-#include <dlfcn.h>
-#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <sys/utsname.h>
#include <unistd.h>
#ifdef FONTCONFIG_ENABLED
@@ -205,6 +204,42 @@ String OS_LinuxBSD::get_name() const {
#endif
}
+String OS_LinuxBSD::get_systemd_os_release_info_value(const String &key) const {
+ static String info;
+ if (info.is_empty()) {
+ Ref<FileAccess> f = FileAccess::open("/etc/os-release", FileAccess::READ);
+ if (f.is_valid()) {
+ while (!f->eof_reached()) {
+ const String line = f->get_line();
+ if (line.find(key) != -1) {
+ return line.split("=")[1].strip_edges();
+ }
+ }
+ }
+ }
+ return info;
+}
+
+String OS_LinuxBSD::get_distribution_name() const {
+ static String systemd_name = get_systemd_os_release_info_value("NAME"); // returns a value for systemd users, otherwise an empty string.
+ if (!systemd_name.is_empty()) {
+ return systemd_name;
+ }
+ struct utsname uts; // returns a decent value for BSD family.
+ uname(&uts);
+ return uts.sysname;
+}
+
+String OS_LinuxBSD::get_version() const {
+ static String systemd_version = get_systemd_os_release_info_value("VERSION"); // returns a value for systemd users, otherwise an empty string.
+ if (!systemd_version.is_empty()) {
+ return systemd_version;
+ }
+ struct utsname uts; // returns a decent value for BSD family.
+ uname(&uts);
+ return uts.version;
+}
+
Error OS_LinuxBSD::shell_open(String p_uri) {
Error ok;
int err_code;
@@ -686,10 +721,9 @@ Error OS_LinuxBSD::move_to_trash(const String &p_path) {
String renamed_path = path.get_base_dir() + "/" + file_name;
// Generates the .trashinfo file
- OS::Date date = OS::get_singleton()->get_date(false);
- OS::Time time = OS::get_singleton()->get_time(false);
- String timestamp = vformat("%04d-%02d-%02dT%02d:%02d:", date.year, (int)date.month, date.day, time.hour, time.minute);
- timestamp = vformat("%s%02d", timestamp, time.second); // vformat only supports up to 6 arguments.
+ OS::DateTime dt = OS::get_singleton()->get_datetime(false);
+ String timestamp = vformat("%04d-%02d-%02dT%02d:%02d:", dt.year, (int)dt.month, dt.day, dt.hour, dt.minute);
+ timestamp = vformat("%s%02d", timestamp, dt.second); // vformat only supports up to 6 arguments.
String trash_info = "[Trash Info]\nPath=" + path.uri_encode() + "\nDeletionDate=" + timestamp + "\n";
{
Error err;
diff --git a/platform/linuxbsd/os_linuxbsd.h b/platform/linuxbsd/os_linuxbsd.h
index d5b2321316..722d83ba19 100644
--- a/platform/linuxbsd/os_linuxbsd.h
+++ b/platform/linuxbsd/os_linuxbsd.h
@@ -67,6 +67,8 @@ class OS_LinuxBSD : public OS_Unix {
MainLoop *main_loop = nullptr;
+ String get_systemd_os_release_info_value(const String &key) const;
+
protected:
virtual void initialize() override;
virtual void finalize() override;
@@ -77,6 +79,8 @@ protected:
public:
virtual String get_name() const override;
+ virtual String get_distribution_name() const override;
+ virtual String get_version() const override;
virtual MainLoop *get_main_loop() const override;
diff --git a/platform/macos/SCsub b/platform/macos/SCsub
index bbd461fba9..7ffb80f70b 100644
--- a/platform/macos/SCsub
+++ b/platform/macos/SCsub
@@ -12,6 +12,7 @@ files = [
"crash_handler_macos.mm",
"macos_terminal_logger.mm",
"display_server_macos.mm",
+ "godot_button_view.mm",
"godot_content_view.mm",
"godot_window_delegate.mm",
"godot_window.mm",
diff --git a/platform/macos/detect.py b/platform/macos/detect.py
index cfd3789b41..511286d52b 100644
--- a/platform/macos/detect.py
+++ b/platform/macos/detect.py
@@ -3,6 +3,11 @@ import sys
from methods import detect_darwin_sdk_path
from platform_methods import detect_arch
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from SCons import Environment
+
def is_active():
return True
@@ -27,8 +32,6 @@ def get_opts():
("MACOS_SDK_PATH", "Path to the macOS SDK", ""),
("vulkan_sdk_path", "Path to the Vulkan SDK", ""),
EnumVariable("macports_clang", "Build using Clang from MacPorts", "no", ("no", "5.0", "devel")),
- BoolVariable("debug_symbols", "Add debugging symbols to release/release_debug builds", True),
- BoolVariable("separate_debug_symbols", "Create a separate file containing debugging symbols", False),
BoolVariable("use_ubsan", "Use LLVM/GCC compiler undefined behavior sanitizer (UBSAN)", False),
BoolVariable("use_asan", "Use LLVM/GCC compiler address sanitizer (ASAN)", False),
BoolVariable("use_tsan", "Use LLVM/GCC compiler thread sanitizer (TSAN)", False),
@@ -74,7 +77,7 @@ def get_mvk_sdk_path():
return os.path.join(os.path.join(dirname, ver_file), "MoltenVK/MoltenVK.xcframework/macos-arm64_x86_64/")
-def configure(env):
+def configure(env: "Environment"):
# Validate arch.
supported_arches = ["x86_64", "arm64"]
if env["arch"] not in supported_arches:
@@ -86,27 +89,10 @@ def configure(env):
## Build type
- if env["target"] == "release":
- if env["optimize"] == "speed": # optimize for speed (default)
- env.Prepend(CCFLAGS=["-O3", "-fomit-frame-pointer", "-ftree-vectorize"])
- elif env["optimize"] == "size": # optimize for size
- env.Prepend(CCFLAGS=["-Os", "-ftree-vectorize"])
+ if env["target"] == "template_release":
if env["arch"] != "arm64":
env.Prepend(CCFLAGS=["-msse2"])
-
- if env["debug_symbols"]:
- env.Prepend(CCFLAGS=["-g2"])
-
- elif env["target"] == "release_debug":
- if env["optimize"] == "speed": # optimize for speed (default)
- env.Prepend(CCFLAGS=["-O2"])
- elif env["optimize"] == "size": # optimize for size
- env.Prepend(CCFLAGS=["-Os"])
- if env["debug_symbols"]:
- env.Prepend(CCFLAGS=["-g2"])
-
- elif env["target"] == "debug":
- env.Prepend(CCFLAGS=["-g3"])
+ elif env.dev_build:
env.Prepend(LINKFLAGS=["-Xlinker", "-no_deduplicate"])
## Compiler configuration
@@ -166,6 +152,21 @@ def configure(env):
env["RANLIB"] = basecmd + "ranlib"
env["AS"] = basecmd + "as"
+ # LTO
+
+ if env["lto"] == "auto": # LTO benefits for macOS (size, performance) haven't been clearly established yet.
+ env["lto"] = "none"
+
+ if env["lto"] != "none":
+ if env["lto"] == "thin":
+ env.Append(CCFLAGS=["-flto=thin"])
+ env.Append(LINKFLAGS=["-flto=thin"])
+ else:
+ env.Append(CCFLAGS=["-flto"])
+ env.Append(LINKFLAGS=["-flto"])
+
+ # Sanitizers
+
if env["use_ubsan"] or env["use_asan"] or env["use_tsan"]:
env.extra_suffix += ".san"
env.Append(CCFLAGS=["-DSANITIZERS_ENABLED"])
@@ -199,9 +200,7 @@ def configure(env):
## Flags
env.Prepend(CPPPATH=["#platform/macos"])
- env.Append(
- CPPDEFINES=["MACOS_ENABLED", "UNIX_ENABLED", "APPLE_STYLE_KEYS", "COREAUDIO_ENABLED", "COREMIDI_ENABLED"]
- )
+ env.Append(CPPDEFINES=["MACOS_ENABLED", "UNIX_ENABLED", "COREAUDIO_ENABLED", "COREMIDI_ENABLED"])
env.Append(
LINKFLAGS=[
"-framework",
diff --git a/platform/macos/dir_access_macos.h b/platform/macos/dir_access_macos.h
index 920e69ef3e..c76b2835e8 100644
--- a/platform/macos/dir_access_macos.h
+++ b/platform/macos/dir_access_macos.h
@@ -31,16 +31,16 @@
#ifndef DIR_ACCESS_MACOS_H
#define DIR_ACCESS_MACOS_H
-#if defined(UNIX_ENABLED) || defined(LIBC_FILEIO_ENABLED)
+#if defined(UNIX_ENABLED)
+
+#include "core/io/dir_access.h"
+#include "drivers/unix/dir_access_unix.h"
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
-#include "core/io/dir_access.h"
-#include "drivers/unix/dir_access_unix.h"
-
class DirAccessMacOS : public DirAccessUnix {
protected:
virtual String fix_unicode_name(const char *p_name) const override;
@@ -51,6 +51,6 @@ protected:
virtual bool is_hidden(const String &p_name) override;
};
-#endif // UNIX ENABLED || LIBC_FILEIO_ENABLED
+#endif // UNIX ENABLED
#endif // DIR_ACCESS_MACOS_H
diff --git a/platform/macos/dir_access_macos.mm b/platform/macos/dir_access_macos.mm
index 3373cada1f..22ebac2db4 100644
--- a/platform/macos/dir_access_macos.mm
+++ b/platform/macos/dir_access_macos.mm
@@ -30,7 +30,7 @@
#include "dir_access_macos.h"
-#if defined(UNIX_ENABLED) || defined(LIBC_FILEIO_ENABLED)
+#if defined(UNIX_ENABLED)
#include <errno.h>
@@ -78,4 +78,4 @@ bool DirAccessMacOS::is_hidden(const String &p_name) {
return [hidden boolValue];
}
-#endif // UNIX_ENABLED || LIBC_FILEIO_ENABLED
+#endif // UNIX_ENABLED
diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h
index 769cba2de5..e72273a681 100644
--- a/platform/macos/display_server_macos.h
+++ b/platform/macos/display_server_macos.h
@@ -76,6 +76,7 @@ public:
id window_delegate;
id window_object;
id window_view;
+ id window_button_view;
Vector<Vector2> mpath;
@@ -84,6 +85,9 @@ public:
Size2i min_size;
Size2i max_size;
Size2i size;
+ Vector2i wb_offset = Vector2i(14, 14);
+
+ NSRect last_frame_rect;
bool im_active = false;
Size2i im_position;
@@ -175,6 +179,12 @@ private:
IOPMAssertionID screen_keep_on_assertion = kIOPMNullAssertionID;
+ struct MenuCall {
+ Variant tag;
+ Callable callback;
+ };
+ Vector<MenuCall> deferred_menu_calls;
+
const NSMenu *_get_menu_root(const String &p_menu_root) const;
NSMenu *_get_menu_root(const String &p_menu_root);
@@ -187,6 +197,8 @@ private:
Point2i _get_native_screen_position(int p_screen) const;
static void _displays_arrangement_changed(CGDirectDisplayID display_id, CGDisplayChangeSummaryFlags flags, void *user_info);
+ WindowID _get_focused_window_or_popup() const;
+
static void _dispatch_input_events(const Ref<InputEvent> &p_event);
void _dispatch_input_event(const Ref<InputEvent> &p_event);
void _push_input(const Ref<InputEvent> &p_event);
@@ -224,6 +236,7 @@ public:
void window_update(WindowID p_window);
void window_destroy(WindowID p_window);
void window_resize(WindowID p_window, int p_width, int p_height);
+ void window_set_custom_window_buttons(WindowData &p_wd, bool p_enabled);
virtual bool has_feature(Feature p_feature) const override;
virtual String get_name() const override;
@@ -387,6 +400,7 @@ public:
virtual bool window_maximize_on_title_dbl_click() const override;
virtual bool window_minimize_on_title_dbl_click() const override;
+ virtual void window_set_window_buttons_offset(const Vector2i &p_offset, WindowID p_window = MAIN_WINDOW_ID) override;
virtual Vector2i window_get_safe_title_margins(WindowID p_window = MAIN_WINDOW_ID) const override;
virtual Point2i ime_get_selection() const override;
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index b009007d73..f980129081 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -30,6 +30,7 @@
#include "display_server_macos.h"
+#include "godot_button_view.h"
#include "godot_content_view.h"
#include "godot_menu_delegate.h"
#include "godot_menu_item.h"
@@ -166,6 +167,7 @@ DisplayServerMacOS::WindowID DisplayServerMacOS::_create_window(WindowMode p_mod
ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Can't create an OpenGL context");
}
#endif
+ [wd.window_view updateLayerDelegate];
id = window_id_counter++;
windows[id] = wd;
}
@@ -316,6 +318,15 @@ void DisplayServerMacOS::_displays_arrangement_changed(CGDirectDisplayID display
}
}
+DisplayServer::WindowID DisplayServerMacOS::_get_focused_window_or_popup() const {
+ const List<WindowID>::Element *E = popup_list.back();
+ if (E) {
+ return E->get();
+ }
+
+ return last_focused_window;
+}
+
void DisplayServerMacOS::_dispatch_input_events(const Ref<InputEvent> &p_event) {
((DisplayServerMacOS *)(get_singleton()))->_dispatch_input_event(p_event);
}
@@ -554,11 +565,11 @@ void DisplayServerMacOS::menu_callback(id p_sender) {
}
if (value->callback != Callable()) {
- Variant tag = value->meta;
- Variant *tagp = &tag;
- Variant ret;
- Callable::CallError ce;
- value->callback.callp((const Variant **)&tagp, 1, ret, ce);
+ MenuCall mc;
+ mc.tag = value->meta;
+ mc.callback = value->callback;
+ deferred_menu_calls.push_back(mc);
+ // Do not run callback from here! If it is opening a new window or calling process_events, it will corrupt OS event queue and crash.
}
}
}
@@ -575,7 +586,7 @@ void DisplayServerMacOS::send_event(NSEvent *p_event) {
// Special case handling of command-period, which is traditionally a special
// shortcut in macOS and doesn't arrive at our regular keyDown handler.
if ([p_event type] == NSEventTypeKeyDown) {
- if (([p_event modifierFlags] & NSEventModifierFlagCommand) && [p_event keyCode] == 0x2f) {
+ if ((([p_event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask) == NSEventModifierFlagCommand) && [p_event keyCode] == 0x2f) {
Ref<InputEventKey> k;
k.instantiate();
@@ -1827,7 +1838,10 @@ void DisplayServerMacOS::mouse_set_mode(MouseMode p_mode) {
return;
}
- WindowID window_id = windows.has(last_focused_window) ? last_focused_window : MAIN_WINDOW_ID;
+ WindowID window_id = _get_focused_window_or_popup();
+ if (!windows.has(window_id)) {
+ window_id = MAIN_WINDOW_ID;
+ }
WindowData &wd = windows[window_id];
if (p_mode == MOUSE_MODE_CAPTURED) {
// Apple Docs state that the display parameter is not used.
@@ -1942,7 +1956,10 @@ void DisplayServerMacOS::warp_mouse(const Point2i &p_position) {
_THREAD_SAFE_METHOD_
if (mouse_mode != MOUSE_MODE_CAPTURED) {
- WindowID window_id = windows.has(last_focused_window) ? last_focused_window : MAIN_WINDOW_ID;
+ WindowID window_id = _get_focused_window_or_popup();
+ if (!windows.has(window_id)) {
+ window_id = MAIN_WINDOW_ID;
+ }
WindowData &wd = windows[window_id];
// Local point in window coords.
@@ -2196,7 +2213,9 @@ void DisplayServerMacOS::show_window(WindowID p_id) {
WindowData &wd = windows[p_id];
popup_open(p_id);
- if (wd.no_focus || wd.is_popup) {
+ if ([wd.window_object isMiniaturized]) {
+ return;
+ } else if (wd.no_focus || wd.is_popup) {
[wd.window_object orderFront:nil];
} else {
[wd.window_object makeKeyAndOrderFront:nil];
@@ -2353,6 +2372,10 @@ void DisplayServerMacOS::window_set_position(const Point2i &p_position, WindowID
ERR_FAIL_COND(!windows.has(p_window));
WindowData &wd = windows[p_window];
+ if ([wd.window_object isZoomed]) {
+ return;
+ }
+
Point2i position = p_position;
// OS X native y-coordinate relative to _get_screens_origin() is negative,
// Godot passes a positive value.
@@ -2477,6 +2500,10 @@ void DisplayServerMacOS::window_set_size(const Size2i p_size, WindowID p_window)
ERR_FAIL_COND(!windows.has(p_window));
WindowData &wd = windows[p_window];
+ if ([wd.window_object isZoomed]) {
+ return;
+ }
+
Size2i size = p_size / screen_get_max_scale();
NSPoint top_left;
@@ -2623,27 +2650,59 @@ bool DisplayServerMacOS::window_minimize_on_title_dbl_click() const {
return false;
}
+void DisplayServerMacOS::window_set_window_buttons_offset(const Vector2i &p_offset, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+ wd.wb_offset = p_offset;
+}
+
Vector2i DisplayServerMacOS::window_get_safe_title_margins(WindowID p_window) const {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V(!windows.has(p_window), Vector2i());
const WindowData &wd = windows[p_window];
- float max_x = 0.f;
- NSButton *cb = [wd.window_object standardWindowButton:NSWindowCloseButton];
- if (cb) {
- max_x = MAX(max_x, [cb frame].origin.x + [cb frame].size.width);
+ if (!wd.window_button_view) {
+ return Vector2i();
}
- NSButton *mb = [wd.window_object standardWindowButton:NSWindowMiniaturizeButton];
- if (mb) {
- max_x = MAX(max_x, [mb frame].origin.x + [mb frame].size.width);
+
+ float max_x = wd.wb_offset.x + [wd.window_button_view frame].size.width;
+
+ if ([wd.window_object windowTitlebarLayoutDirection] == NSUserInterfaceLayoutDirectionRightToLeft) {
+ return Vector2i(0, max_x * screen_get_max_scale());
+ } else {
+ return Vector2i(max_x * screen_get_max_scale(), 0);
}
- NSButton *zb = [wd.window_object standardWindowButton:NSWindowZoomButton];
- if (zb) {
- max_x = MAX(max_x, [zb frame].origin.x + [zb frame].size.width);
+}
+
+void DisplayServerMacOS::window_set_custom_window_buttons(WindowData &p_wd, bool p_enabled) {
+ if (p_wd.window_button_view) {
+ [p_wd.window_button_view removeFromSuperview];
+ p_wd.window_button_view = nil;
}
+ if (p_enabled) {
+ float cb_frame = NSMinX([[p_wd.window_object standardWindowButton:NSWindowCloseButton] frame]);
+ float mb_frame = NSMinX([[p_wd.window_object standardWindowButton:NSWindowMiniaturizeButton] frame]);
+ bool is_rtl = ([p_wd.window_object windowTitlebarLayoutDirection] == NSUserInterfaceLayoutDirectionRightToLeft);
+
+ float window_buttons_spacing = (is_rtl) ? (cb_frame - mb_frame) : (mb_frame - cb_frame);
+
+ [p_wd.window_object setTitleVisibility:NSWindowTitleHidden];
+ [[p_wd.window_object standardWindowButton:NSWindowZoomButton] setHidden:YES];
+ [[p_wd.window_object standardWindowButton:NSWindowMiniaturizeButton] setHidden:YES];
+ [[p_wd.window_object standardWindowButton:NSWindowCloseButton] setHidden:YES];
- return Vector2i(max_x * screen_get_max_scale(), 0);
+ p_wd.window_button_view = [[GodotButtonView alloc] initWithFrame:NSZeroRect];
+ [p_wd.window_button_view initButtons:window_buttons_spacing offset:NSMakePoint(p_wd.wb_offset.x, p_wd.wb_offset.y) rtl:is_rtl];
+ [p_wd.window_view addSubview:p_wd.window_button_view];
+ } else {
+ [p_wd.window_object setTitleVisibility:NSWindowTitleVisible];
+ [[p_wd.window_object standardWindowButton:NSWindowZoomButton] setHidden:NO];
+ [[p_wd.window_object standardWindowButton:NSWindowMiniaturizeButton] setHidden:NO];
+ [[p_wd.window_object standardWindowButton:NSWindowCloseButton] setHidden:NO];
+ }
}
void DisplayServerMacOS::window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window) {
@@ -2668,14 +2727,21 @@ void DisplayServerMacOS::window_set_flag(WindowFlags p_flag, bool p_enabled, Win
NSRect rect = [wd.window_object frame];
if (p_enabled) {
[wd.window_object setTitlebarAppearsTransparent:YES];
- [wd.window_object setTitleVisibility:NSWindowTitleHidden];
[wd.window_object setStyleMask:[wd.window_object styleMask] | NSWindowStyleMaskFullSizeContentView];
+
+ if (!wd.fullscreen) {
+ window_set_custom_window_buttons(wd, true);
+ }
} else {
[wd.window_object setTitlebarAppearsTransparent:NO];
- [wd.window_object setTitleVisibility:NSWindowTitleVisible];
[wd.window_object setStyleMask:[wd.window_object styleMask] & ~NSWindowStyleMaskFullSizeContentView];
+
+ if (!wd.fullscreen) {
+ window_set_custom_window_buttons(wd, false);
+ }
}
[wd.window_object setFrame:rect display:YES];
+ send_window_event(wd, DisplayServerMacOS::WINDOW_EVENT_TITLEBAR_CHANGE);
} break;
case WINDOW_FLAG_BORDERLESS: {
// OrderOut prevents a lose focus bug with the window.
@@ -2695,7 +2761,9 @@ void DisplayServerMacOS::window_set_flag(WindowFlags p_flag, bool p_enabled, Win
}
_update_window_style(wd);
if ([wd.window_object isVisible]) {
- if (wd.no_focus || wd.is_popup) {
+ if ([wd.window_object isMiniaturized]) {
+ return;
+ } else if (wd.no_focus || wd.is_popup) {
[wd.window_object orderFront:nil];
} else {
[wd.window_object makeKeyAndOrderFront:nil];
@@ -3212,6 +3280,16 @@ void DisplayServerMacOS::process_events() {
[NSApp sendEvent:event];
}
+ // Process "menu_callback"s.
+ for (MenuCall &E : deferred_menu_calls) {
+ Variant tag = E.tag;
+ Variant *tagp = &tag;
+ Variant ret;
+ Callable::CallError ce;
+ E.callback.callp((const Variant **)&tagp, 1, ret, ce);
+ }
+ deferred_menu_calls.clear();
+
if (!drop_events) {
_process_key_events();
Input::get_singleton()->flush_buffered_events();
@@ -3536,7 +3614,7 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM
[apple_menu addItem:[NSMenuItem separatorItem]];
- title = [NSString stringWithFormat:NSLocalizedString(@"\t\tQuit %@", nil), nsappname];
+ title = [NSString stringWithFormat:NSLocalizedString(@"Quit %@", nil), nsappname];
[apple_menu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
// Add items to the menu bar.
diff --git a/platform/macos/export/codesign.h b/platform/macos/export/codesign.h
index fea7b117d0..01e97bca81 100644
--- a/platform/macos/export/codesign.h
+++ b/platform/macos/export/codesign.h
@@ -28,6 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+#ifndef MACOS_CODESIGN_H
+#define MACOS_CODESIGN_H
+
// macOS code signature creation utility.
//
// Current implementation has the following limitation:
@@ -38,9 +41,6 @@
// - Requirements code generator is not implemented (only hard-coded requirements for the ad-hoc signing is supported).
// - RFC5652/CMS blob generation is not implemented, supports ad-hoc signing only.
-#ifndef MACOS_CODESIGN_H
-#define MACOS_CODESIGN_H
-
#include "core/crypto/crypto_core.h"
#include "core/io/dir_access.h"
#include "core/io/file_access.h"
diff --git a/platform/macos/export/export.cpp b/platform/macos/export/export.cpp
index f219616df4..5f9cf22ccf 100644
--- a/platform/macos/export/export.cpp
+++ b/platform/macos/export/export.cpp
@@ -33,12 +33,14 @@
#include "export_plugin.h"
void register_macos_exporter() {
+#ifndef ANDROID_ENABLED
EDITOR_DEF("export/macos/rcodesign", "");
#ifdef WINDOWS_ENABLED
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/macos/rcodesign", PROPERTY_HINT_GLOBAL_FILE, "*.exe"));
#else
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/macos/rcodesign", PROPERTY_HINT_GLOBAL_FILE));
#endif
+#endif
Ref<EditorExportPlatformMacOS> platform;
platform.instantiate();
diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp
index 50104aced5..070830c486 100644
--- a/platform/macos/export/export_plugin.cpp
+++ b/platform/macos/export/export_plugin.cpp
@@ -1641,16 +1641,15 @@ void EditorExportPlatformMacOS::_zip_folder_recursive(zipFile &p_zip, const Stri
continue;
}
if (da->is_link(f)) {
- OS::Time time = OS::get_singleton()->get_time();
- OS::Date date = OS::get_singleton()->get_date();
+ OS::DateTime dt = OS::get_singleton()->get_datetime();
zip_fileinfo zipfi;
- zipfi.tmz_date.tm_hour = time.hour;
- zipfi.tmz_date.tm_mday = date.day;
- zipfi.tmz_date.tm_min = time.minute;
- zipfi.tmz_date.tm_mon = date.month - 1; // Note: "tm" month range - 0..11, Godot month range - 1..12, https://www.cplusplus.com/reference/ctime/tm/
- zipfi.tmz_date.tm_sec = time.second;
- zipfi.tmz_date.tm_year = date.year;
+ zipfi.tmz_date.tm_year = dt.year;
+ zipfi.tmz_date.tm_mon = dt.month - 1; // Note: "tm" month range - 0..11, Godot month range - 1..12, https://www.cplusplus.com/reference/ctime/tm/
+ zipfi.tmz_date.tm_mday = dt.day;
+ zipfi.tmz_date.tm_hour = dt.hour;
+ zipfi.tmz_date.tm_min = dt.minute;
+ zipfi.tmz_date.tm_sec = dt.second;
zipfi.dosDate = 0;
// 0120000: symbolic link type
// 0000755: permissions rwxr-xr-x
@@ -1686,16 +1685,15 @@ void EditorExportPlatformMacOS::_zip_folder_recursive(zipFile &p_zip, const Stri
} else {
bool is_executable = (p_folder.ends_with("MacOS") && (f == p_pkg_name)) || p_folder.ends_with("Helpers") || f.ends_with(".command");
- OS::Time time = OS::get_singleton()->get_time();
- OS::Date date = OS::get_singleton()->get_date();
+ OS::DateTime dt = OS::get_singleton()->get_datetime();
zip_fileinfo zipfi;
- zipfi.tmz_date.tm_hour = time.hour;
- zipfi.tmz_date.tm_mday = date.day;
- zipfi.tmz_date.tm_min = time.minute;
- zipfi.tmz_date.tm_mon = date.month - 1; // Note: "tm" month range - 0..11, Godot month range - 1..12, https://www.cplusplus.com/reference/ctime/tm/
- zipfi.tmz_date.tm_sec = time.second;
- zipfi.tmz_date.tm_year = date.year;
+ zipfi.tmz_date.tm_year = dt.year;
+ zipfi.tmz_date.tm_mon = dt.month - 1; // Note: "tm" month range - 0..11, Godot month range - 1..12, https://www.cplusplus.com/reference/ctime/tm/
+ zipfi.tmz_date.tm_mday = dt.day;
+ zipfi.tmz_date.tm_hour = dt.hour;
+ zipfi.tmz_date.tm_min = dt.minute;
+ zipfi.tmz_date.tm_sec = dt.second;
zipfi.dosDate = 0;
// 0100000: regular file type
// 0000755: permissions rwxr-xr-x
diff --git a/platform/macos/export/lipo.h b/platform/macos/export/lipo.h
index 516ef99860..6378f9899c 100644
--- a/platform/macos/export/lipo.h
+++ b/platform/macos/export/lipo.h
@@ -28,11 +28,11 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-// Universal / Universal 2 fat binary file creator and extractor.
-
#ifndef MACOS_LIPO_H
#define MACOS_LIPO_H
+// Universal / Universal 2 fat binary file creator and extractor.
+
#include "core/io/file_access.h"
#include "core/object/ref_counted.h"
#include "modules/modules_enabled.gen.h" // For regex.
diff --git a/platform/macos/export/macho.h b/platform/macos/export/macho.h
index 7ef0d9067e..0d42a7013f 100644
--- a/platform/macos/export/macho.h
+++ b/platform/macos/export/macho.h
@@ -28,11 +28,11 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-// Mach-O binary object file format parser and editor.
-
#ifndef MACOS_MACHO_H
#define MACOS_MACHO_H
+// Mach-O binary object file format parser and editor.
+
#include "core/crypto/crypto.h"
#include "core/crypto/crypto_core.h"
#include "core/io/file_access.h"
diff --git a/platform/macos/export/plist.h b/platform/macos/export/plist.h
index 79cb928d0a..b3c51a9635 100644
--- a/platform/macos/export/plist.h
+++ b/platform/macos/export/plist.h
@@ -28,11 +28,11 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-// Property list file format (application/x-plist) parser, property list ASN-1 serialization.
-
#ifndef MACOS_PLIST_H
#define MACOS_PLIST_H
+// Property list file format (application/x-plist) parser, property list ASN-1 serialization.
+
#include "core/crypto/crypto_core.h"
#include "core/io/file_access.h"
#include "modules/modules_enabled.gen.h" // For regex.
diff --git a/platform/macos/gl_manager_macos_legacy.mm b/platform/macos/gl_manager_macos_legacy.mm
index e6bb7aaa85..dec4821b86 100644
--- a/platform/macos/gl_manager_macos_legacy.mm
+++ b/platform/macos/gl_manager_macos_legacy.mm
@@ -167,9 +167,8 @@ void GLManager_MacOS::make_current() {
}
void GLManager_MacOS::swap_buffers() {
- for (const KeyValue<DisplayServer::WindowID, GLWindow> &E : windows) {
- [E.value.context flushBuffer];
- }
+ GLWindow &win = windows[current_window];
+ [win.context flushBuffer];
}
void GLManager_MacOS::window_update(DisplayServer::WindowID p_window_id) {
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java b/platform/macos/godot_button_view.h
index 778efa914a..e7627a9e9b 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.java
+++ b/platform/macos/godot_button_view.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* GodotGestureHandler.java */
+/* godot_button_view.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,60 +28,25 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-package org.godotengine.godot.input;
+#ifndef GODOT_BUTTON_VIEW_H
+#define GODOT_BUTTON_VIEW_H
-import org.godotengine.godot.GodotLib;
-import org.godotengine.godot.GodotRenderView;
+#include "servers/display_server.h"
-import android.view.GestureDetector;
-import android.view.MotionEvent;
+#import <AppKit/AppKit.h>
+#import <Foundation/Foundation.h>
-/**
- * Handles gesture input related events for the {@link GodotRenderView} view.
- * https://developer.android.com/reference/android/view/GestureDetector.SimpleOnGestureListener
- */
-public class GodotGestureHandler extends GestureDetector.SimpleOnGestureListener {
- private final GodotRenderView mRenderView;
-
- public GodotGestureHandler(GodotRenderView godotView) {
- mRenderView = godotView;
- }
-
- private void queueEvent(Runnable task) {
- mRenderView.queueOnRenderThread(task);
- }
-
- @Override
- public boolean onDown(MotionEvent event) {
- super.onDown(event);
- //Log.i("GodotGesture", "onDown");
- return true;
- }
-
- @Override
- public boolean onSingleTapConfirmed(MotionEvent event) {
- super.onSingleTapConfirmed(event);
- return true;
- }
+@interface GodotButtonView : NSView {
+ NSTrackingArea *tracking_area;
+ NSPoint offset;
+ CGFloat spacing;
+ bool mouse_in_group;
+ bool rtl;
+}
- @Override
- public void onLongPress(MotionEvent event) {
- //Log.i("GodotGesture", "onLongPress");
- }
+- (void)initButtons:(CGFloat)button_spacing offset:(NSPoint)button_offset rtl:(bool)is_rtl;
+- (void)displayButtons;
- @Override
- public boolean onDoubleTap(MotionEvent event) {
- //Log.i("GodotGesture", "onDoubleTap");
- final int x = Math.round(event.getX());
- final int y = Math.round(event.getY());
- final int buttonMask = event.getButtonState();
- GodotLib.doubleTap(buttonMask, x, y);
- return true;
- }
+@end
- @Override
- public boolean onFling(MotionEvent event1, MotionEvent event2, float velocityX, float velocityY) {
- //Log.i("GodotGesture", "onFling");
- return true;
- }
-}
+#endif // GODOT_BUTTON_VIEW_H
diff --git a/platform/macos/godot_button_view.mm b/platform/macos/godot_button_view.mm
new file mode 100644
index 0000000000..9106f0b0db
--- /dev/null
+++ b/platform/macos/godot_button_view.mm
@@ -0,0 +1,123 @@
+/*************************************************************************/
+/* godot_button_view.mm */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "godot_button_view.h"
+
+@implementation GodotButtonView
+
+- (id)initWithFrame:(NSRect)frame {
+ self = [super initWithFrame:frame];
+
+ tracking_area = nil;
+ offset = NSMakePoint(8, 8);
+ spacing = 20;
+ mouse_in_group = false;
+ rtl = false;
+
+ return self;
+}
+
+- (void)initButtons:(CGFloat)button_spacing offset:(NSPoint)button_offset rtl:(bool)is_rtl {
+ spacing = button_spacing;
+ rtl = is_rtl;
+
+ NSButton *close_button = [NSWindow standardWindowButton:NSWindowCloseButton forStyleMask:NSWindowStyleMaskTitled];
+ [close_button setFrameOrigin:NSMakePoint(rtl ? spacing * 2 : 0, 0)];
+ [self addSubview:close_button];
+
+ NSButton *miniaturize_button = [NSWindow standardWindowButton:NSWindowMiniaturizeButton forStyleMask:NSWindowStyleMaskTitled];
+ [miniaturize_button setFrameOrigin:NSMakePoint(spacing, 0)];
+ [self addSubview:miniaturize_button];
+
+ NSButton *zoom_button = [NSWindow standardWindowButton:NSWindowZoomButton forStyleMask:NSWindowStyleMaskTitled];
+ [zoom_button setFrameOrigin:NSMakePoint(rtl ? 0 : spacing * 2, 0)];
+ [self addSubview:zoom_button];
+
+ offset.y = button_offset.y - zoom_button.frame.size.height / 2;
+ offset.x = button_offset.x - zoom_button.frame.size.width / 2;
+
+ if (rtl) {
+ [self setFrameSize:NSMakeSize(close_button.frame.origin.x + close_button.frame.size.width, close_button.frame.size.height)];
+ } else {
+ [self setFrameSize:NSMakeSize(zoom_button.frame.origin.x + zoom_button.frame.size.width, zoom_button.frame.size.height)];
+ }
+ [self displayButtons];
+}
+
+- (void)viewDidMoveToWindow {
+ if (!self.window) {
+ return;
+ }
+
+ if (rtl) {
+ [self setAutoresizingMask:NSViewMinXMargin | NSViewMinYMargin];
+ [self setFrameOrigin:NSMakePoint(self.window.frame.size.width - self.frame.size.width - offset.x, self.window.frame.size.height - self.frame.size.height - offset.y)];
+ } else {
+ [self setAutoresizingMask:NSViewMaxXMargin | NSViewMinYMargin];
+ [self setFrameOrigin:NSMakePoint(offset.x, self.window.frame.size.height - self.frame.size.height - offset.y)];
+ }
+}
+
+- (BOOL)_mouseInGroup:(NSButton *)button {
+ return mouse_in_group;
+}
+
+- (void)updateTrackingAreas {
+ if (tracking_area != nil) {
+ [self removeTrackingArea:tracking_area];
+ }
+
+ NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | NSTrackingInVisibleRect;
+ tracking_area = [[NSTrackingArea alloc] initWithRect:NSZeroRect options:options owner:self userInfo:nil];
+
+ [self addTrackingArea:tracking_area];
+}
+
+- (void)mouseEntered:(NSEvent *)event {
+ [super mouseEntered:event];
+
+ mouse_in_group = true;
+ [self displayButtons];
+}
+
+- (void)mouseExited:(NSEvent *)event {
+ [super mouseExited:event];
+
+ mouse_in_group = false;
+ [self displayButtons];
+}
+
+- (void)displayButtons {
+ for (NSView *subview in self.subviews) {
+ [subview setNeedsDisplay:YES];
+ }
+}
+
+@end
diff --git a/platform/macos/godot_content_view.h b/platform/macos/godot_content_view.h
index 353305aec1..a6318ab903 100644
--- a/platform/macos/godot_content_view.h
+++ b/platform/macos/godot_content_view.h
@@ -45,6 +45,14 @@
#import <QuartzCore/CAMetalLayer.h>
+@interface GodotContentLayerDelegate : NSObject <CALayerDelegate> {
+ DisplayServer::WindowID window_id;
+}
+
+- (void)setWindowID:(DisplayServer::WindowID)wid;
+
+@end
+
@interface GodotContentView : RootView <NSTextInputClient> {
DisplayServer::WindowID window_id;
NSTrackingArea *tracking_area;
@@ -53,12 +61,14 @@
bool mouse_down_control;
bool ignore_momentum_scroll;
bool last_pen_inverted;
+ id layer_delegate;
}
- (void)processScrollEvent:(NSEvent *)event button:(MouseButton)button factor:(double)factor;
- (void)processPanEvent:(NSEvent *)event dx:(double)dx dy:(double)dy;
- (void)processMouseEvent:(NSEvent *)event index:(MouseButton)index mask:(MouseButton)mask pressed:(bool)pressed;
- (void)setWindowID:(DisplayServer::WindowID)wid;
+- (void)updateLayerDelegate;
- (void)cancelComposition;
@end
diff --git a/platform/macos/godot_content_view.mm b/platform/macos/godot_content_view.mm
index dbed969901..cb70a5db86 100644
--- a/platform/macos/godot_content_view.mm
+++ b/platform/macos/godot_content_view.mm
@@ -32,11 +32,66 @@
#include "display_server_macos.h"
#include "key_mapping_macos.h"
+#include "main/main.h"
+
+@implementation GodotContentLayerDelegate
+
+- (id)init {
+ self = [super init];
+ window_id = DisplayServer::INVALID_WINDOW_ID;
+ return self;
+}
+
+- (void)setWindowID:(DisplayServerMacOS::WindowID)wid {
+ window_id = wid;
+}
+
+- (void)displayLayer:(CALayer *)layer {
+ DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
+ if (OS::get_singleton()->get_main_loop() && ds->get_is_resizing()) {
+ Main::force_redraw();
+ if (!Main::is_iterating()) { // Avoid cyclic loop.
+ Main::iteration();
+ }
+ }
+}
+
+@end
@implementation GodotContentView
+- (void)setFrameSize:(NSSize)newSize {
+ DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
+ if (ds && ds->has_window(window_id)) {
+ DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
+ NSRect frameRect = [wd.window_object frame];
+ bool left = (wd.last_frame_rect.origin.x != frameRect.origin.x);
+ bool top = (wd.last_frame_rect.origin.y == frameRect.origin.y);
+ if (left && top) {
+ self.layerContentsPlacement = NSViewLayerContentsPlacementBottomRight;
+ } else if (left && !top) {
+ self.layerContentsPlacement = NSViewLayerContentsPlacementTopRight;
+ } else if (!left && top) {
+ self.layerContentsPlacement = NSViewLayerContentsPlacementBottomLeft;
+ } else {
+ self.layerContentsPlacement = NSViewLayerContentsPlacementTopLeft;
+ }
+ wd.last_frame_rect = frameRect;
+ }
+
+ [super setFrameSize:newSize];
+ [self.layer setNeedsDisplay]; // Force "drawRect" call.
+}
+
+- (void)updateLayerDelegate {
+ self.layer.delegate = layer_delegate;
+ self.layer.autoresizingMask = kCALayerHeightSizable | kCALayerWidthSizable;
+ self.layer.needsDisplayOnBoundsChange = YES;
+}
+
- (id)init {
self = [super init];
+ layer_delegate = [[GodotContentLayerDelegate alloc] init];
window_id = DisplayServer::INVALID_WINDOW_ID;
tracking_area = nil;
ime_input_event_in_progress = false;
@@ -45,6 +100,9 @@
last_pen_inverted = false;
[self updateTrackingAreas];
+ self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize;
+ self.layerContentsPlacement = NSViewLayerContentsPlacementTopLeft;
+
if (@available(macOS 10.13, *)) {
[self registerForDraggedTypes:[NSArray arrayWithObject:NSPasteboardTypeFileURL]];
#if !defined(__aarch64__) // Do not build deprectead 10.13 code on ARM.
@@ -58,6 +116,7 @@
- (void)setWindowID:(DisplayServerMacOS::WindowID)wid {
window_id = wid;
+ [layer_delegate setWindowID:window_id];
}
// MARK: Backing Layer
diff --git a/platform/macos/godot_window.h b/platform/macos/godot_window.h
index 9fc5599e86..d3653fda82 100644
--- a/platform/macos/godot_window.h
+++ b/platform/macos/godot_window.h
@@ -38,9 +38,11 @@
@interface GodotWindow : NSWindow {
DisplayServer::WindowID window_id;
+ NSTimeInterval anim_duration;
}
- (void)setWindowID:(DisplayServer::WindowID)wid;
+- (void)setAnimDuration:(NSTimeInterval)duration;
@end
diff --git a/platform/macos/godot_window.mm b/platform/macos/godot_window.mm
index e205e7546d..bc51da4f72 100644
--- a/platform/macos/godot_window.mm
+++ b/platform/macos/godot_window.mm
@@ -37,9 +37,22 @@
- (id)init {
self = [super init];
window_id = DisplayServer::INVALID_WINDOW_ID;
+ anim_duration = -1.0f;
return self;
}
+- (void)setAnimDuration:(NSTimeInterval)duration {
+ anim_duration = duration;
+}
+
+- (NSTimeInterval)animationResizeTime:(NSRect)newFrame {
+ if (anim_duration > 0) {
+ return anim_duration;
+ } else {
+ return [super animationResizeTime:newFrame];
+ }
+}
+
- (void)setWindowID:(DisplayServerMacOS::WindowID)wid {
window_id = wid;
}
diff --git a/platform/macos/godot_window_delegate.h b/platform/macos/godot_window_delegate.h
index 98c226aa2f..01cc13a016 100644
--- a/platform/macos/godot_window_delegate.h
+++ b/platform/macos/godot_window_delegate.h
@@ -38,6 +38,8 @@
@interface GodotWindowDelegate : NSObject <NSWindowDelegate> {
DisplayServer::WindowID window_id;
+ NSRect old_frame;
+ NSWindowStyleMask old_style_mask;
}
- (void)setWindowID:(DisplayServer::WindowID)wid;
diff --git a/platform/macos/godot_window_delegate.mm b/platform/macos/godot_window_delegate.mm
index 2d9329ab3c..279fd2a359 100644
--- a/platform/macos/godot_window_delegate.mm
+++ b/platform/macos/godot_window_delegate.mm
@@ -31,6 +31,8 @@
#include "godot_window_delegate.h"
#include "display_server_macos.h"
+#include "godot_button_view.h"
+#include "godot_window.h"
@implementation GodotWindowDelegate
@@ -68,6 +70,26 @@
ds->window_destroy(window_id);
}
+- (NSArray<NSWindow *> *)customWindowsToEnterFullScreenForWindow:(NSWindow *)window {
+ DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
+ if (!ds || !ds->has_window(window_id)) {
+ return nullptr;
+ }
+
+ old_frame = [window frame];
+ old_style_mask = [window styleMask];
+
+ NSMutableArray<NSWindow *> *windows = [[NSMutableArray alloc] init];
+ [windows addObject:window];
+
+ return windows;
+}
+
+- (void)window:(NSWindow *)window startCustomAnimationToEnterFullScreenWithDuration:(NSTimeInterval)duration {
+ [(GodotWindow *)window setAnimDuration:duration];
+ [window setFrame:[[window screen] frame] display:YES animate:YES];
+}
+
- (void)windowDidEnterFullScreen:(NSNotification *)notification {
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
if (!ds || !ds->has_window(window_id)) {
@@ -76,12 +98,46 @@
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
wd.fullscreen = true;
+
// Reset window size limits.
[wd.window_object setContentMinSize:NSMakeSize(0, 0)];
[wd.window_object setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
+ [(GodotWindow *)wd.window_object setAnimDuration:-1.0f];
+
+ // Reset custom window buttons.
+ if ([wd.window_object styleMask] & NSWindowStyleMaskFullSizeContentView) {
+ ds->window_set_custom_window_buttons(wd, false);
+ }
// Force window resize event.
[self windowDidResize:notification];
+ ds->send_window_event(wd, DisplayServerMacOS::WINDOW_EVENT_TITLEBAR_CHANGE);
+}
+
+- (NSArray<NSWindow *> *)customWindowsToExitFullScreenForWindow:(NSWindow *)window {
+ DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
+ if (!ds || !ds->has_window(window_id)) {
+ return nullptr;
+ }
+
+ DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
+
+ // Restore custom window buttons.
+ if ([wd.window_object styleMask] & NSWindowStyleMaskFullSizeContentView) {
+ ds->window_set_custom_window_buttons(wd, true);
+ }
+
+ ds->send_window_event(wd, DisplayServerMacOS::WINDOW_EVENT_TITLEBAR_CHANGE);
+
+ NSMutableArray<NSWindow *> *windows = [[NSMutableArray alloc] init];
+ [windows addObject:wd.window_object];
+ return windows;
+}
+
+- (void)window:(NSWindow *)window startCustomAnimationToExitFullScreenWithDuration:(NSTimeInterval)duration {
+ [(GodotWindow *)window setAnimDuration:duration];
+ [window setStyleMask:old_style_mask];
+ [window setFrame:old_frame display:YES animate:YES];
}
- (void)windowDidExitFullScreen:(NSNotification *)notification {
@@ -93,6 +149,8 @@
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
wd.fullscreen = false;
+ [(GodotWindow *)wd.window_object setAnimDuration:-1.0f];
+
// Set window size limits.
const float scale = ds->screen_get_max_scale();
if (wd.min_size != Size2i()) {
@@ -151,7 +209,9 @@
- (void)windowWillStartLiveResize:(NSNotification *)notification {
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
- if (ds) {
+ if (ds && ds->has_window(window_id)) {
+ DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
+ wd.last_frame_rect = [wd.window_object frame];
ds->set_is_resizing(true);
}
}
@@ -217,6 +277,10 @@
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
+ if (wd.window_button_view) {
+ [(GodotButtonView *)wd.window_button_view displayButtons];
+ }
+
if (ds->mouse_get_mode() == DisplayServer::MOUSE_MODE_CAPTURED) {
const NSRect content_rect = [wd.window_view frame];
NSRect point_in_window_rect = NSMakeRect(content_rect.size.width / 2, content_rect.size.height / 2, 0, 0);
@@ -239,6 +303,10 @@
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
+ if (wd.window_button_view) {
+ [(GodotButtonView *)wd.window_button_view displayButtons];
+ }
+
ds->release_pressed_events();
ds->send_window_event(wd, DisplayServerMacOS::WINDOW_EVENT_FOCUS_OUT);
}
diff --git a/platform/macos/os_macos.h b/platform/macos/os_macos.h
index 61db99689c..46e7c17ebe 100644
--- a/platform/macos/os_macos.h
+++ b/platform/macos/os_macos.h
@@ -75,6 +75,8 @@ public:
virtual List<String> get_cmdline_platform_args() const override;
virtual String get_name() const override;
+ virtual String get_distribution_name() const override;
+ virtual String get_version() const override;
virtual void alert(const String &p_alert, const String &p_title = "ALERT!") override;
diff --git a/platform/macos/os_macos.mm b/platform/macos/os_macos.mm
index 35c4e4b03d..ae8534f6ab 100644
--- a/platform/macos/os_macos.mm
+++ b/platform/macos/os_macos.mm
@@ -56,10 +56,11 @@ _FORCE_INLINE_ String OS_MacOS::get_framework_executable(const String &p_path) {
}
void OS_MacOS::pre_wait_observer_cb(CFRunLoopObserverRef p_observer, CFRunLoopActivity p_activiy, void *p_context) {
- // Prevent main loop from sleeping and redraw window during resize / modal popups.
+ // Prevent main loop from sleeping and redraw window during modal popup display.
+ // Do not redraw when rendering is done from the separate thread, it will conflict with the OpenGL context updates.
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
- if (get_singleton()->get_main_loop() && ds && (get_singleton()->get_render_thread_mode() != RENDER_SEPARATE_THREAD || !ds->get_is_resizing())) {
+ if (get_singleton()->get_main_loop() && ds && (get_singleton()->get_render_thread_mode() != RENDER_SEPARATE_THREAD) && !ds->get_is_resizing()) {
Main::force_redraw();
if (!Main::is_iterating()) { // Avoid cyclic loop.
Main::iteration();
@@ -133,6 +134,15 @@ String OS_MacOS::get_name() const {
return "macOS";
}
+String OS_MacOS::get_distribution_name() const {
+ return get_name();
+}
+
+String OS_MacOS::get_version() const {
+ NSOperatingSystemVersion ver = [NSProcessInfo processInfo].operatingSystemVersion;
+ return vformat("%d.%d.%d", (int64_t)ver.majorVersion, (int64_t)ver.minorVersion, (int64_t)ver.patchVersion);
+}
+
void OS_MacOS::alert(const String &p_alert, const String &p_title) {
NSAlert *window = [[NSAlert alloc] init];
NSString *ns_title = [NSString stringWithUTF8String:p_title.utf8().get_data()];
diff --git a/platform/macos/tts_macos.mm b/platform/macos/tts_macos.mm
index 3c101b9531..56e15979c4 100644
--- a/platform/macos/tts_macos.mm
+++ b/platform/macos/tts_macos.mm
@@ -126,12 +126,12 @@
AVSpeechUtterance *new_utterance = [[AVSpeechUtterance alloc] initWithString:[NSString stringWithUTF8String:message.text.utf8().get_data()]];
[new_utterance setVoice:[AVSpeechSynthesisVoice voiceWithIdentifier:[NSString stringWithUTF8String:message.voice.utf8().get_data()]]];
if (message.rate > 1.f) {
- [new_utterance setRate:Math::range_lerp(message.rate, 1.f, 10.f, AVSpeechUtteranceDefaultSpeechRate, AVSpeechUtteranceMaximumSpeechRate)];
+ [new_utterance setRate:Math::remap(message.rate, 1.f, 10.f, AVSpeechUtteranceDefaultSpeechRate, AVSpeechUtteranceMaximumSpeechRate)];
} else if (message.rate < 1.f) {
- [new_utterance setRate:Math::range_lerp(message.rate, 0.1f, 1.f, AVSpeechUtteranceMinimumSpeechRate, AVSpeechUtteranceDefaultSpeechRate)];
+ [new_utterance setRate:Math::remap(message.rate, 0.1f, 1.f, AVSpeechUtteranceMinimumSpeechRate, AVSpeechUtteranceDefaultSpeechRate)];
}
[new_utterance setPitchMultiplier:message.pitch];
- [new_utterance setVolume:(Math::range_lerp(message.volume, 0.f, 100.f, 0.f, 1.f))];
+ [new_utterance setVolume:(Math::remap(message.volume, 0.f, 100.f, 0.f, 1.f))];
ids[new_utterance] = message.id;
[av_synth speakUtterance:new_utterance];
@@ -141,7 +141,7 @@
[ns_synth setVoice:[NSString stringWithUTF8String:message.voice.utf8().get_data()]];
int base_pitch = [[ns_synth objectForProperty:NSSpeechPitchBaseProperty error:nil] intValue];
[ns_synth setObject:[NSNumber numberWithInt:(base_pitch * (message.pitch / 2.f + 0.5f))] forProperty:NSSpeechPitchBaseProperty error:nullptr];
- [ns_synth setVolume:(Math::range_lerp(message.volume, 0.f, 100.f, 0.f, 1.f))];
+ [ns_synth setVolume:(Math::remap(message.volume, 0.f, 100.f, 0.f, 1.f))];
[ns_synth setRate:(message.rate * 200)];
last_utterance = message.id;
diff --git a/platform/uwp/detect.py b/platform/uwp/detect.py
index 2c5746cb06..64fe5bc4a2 100644
--- a/platform/uwp/detect.py
+++ b/platform/uwp/detect.py
@@ -3,6 +3,11 @@ import os
import sys
from platform_methods import detect_arch
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from SCons import Environment
+
def is_active():
return True
@@ -39,7 +44,7 @@ def get_flags():
]
-def configure(env):
+def configure(env: "Environment"):
# Validate arch.
supported_arches = ["x86_32", "x86_64", "arm32"]
if env["arch"] not in supported_arches:
@@ -83,7 +88,7 @@ def configure(env):
env.AppendUnique(CCFLAGS=["/utf-8"])
# ANGLE
- angle_root = os.getenv("ANGLE_SRC_PATH")
+ angle_root = os.environ["ANGLE_SRC_PATH"]
env.Prepend(CPPPATH=[angle_root + "/include"])
jobs = str(env.GetOption("num_jobs"))
angle_build_cmd = (
@@ -94,7 +99,7 @@ def configure(env):
+ " /p:Configuration=Release /p:Platform="
)
- if os.path.isfile(str(os.getenv("ANGLE_SRC_PATH")) + "/winrt/10/src/angle.sln"):
+ if os.path.isfile(f"{angle_root}/winrt/10/src/angle.sln"):
env["build_angle"] = True
## Architecture
diff --git a/platform/uwp/export/app_packager.cpp b/platform/uwp/export/app_packager.cpp
index 87224d38b8..6f8966b9ff 100644
--- a/platform/uwp/export/app_packager.cpp
+++ b/platform/uwp/export/app_packager.cpp
@@ -301,7 +301,7 @@ Error AppxPackager::add_file(String p_file_name, const uint8_t *p_buffer, size_t
Vector<uint8_t> file_buffer;
// Data for compression
- z_stream strm;
+ z_stream strm{};
Vector<uint8_t> strm_in;
strm_in.resize(BLOCK_SIZE);
Vector<uint8_t> strm_out;
diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp
index 494f5ec4b9..8050d299f0 100644
--- a/platform/uwp/os_uwp.cpp
+++ b/platform/uwp/os_uwp.cpp
@@ -444,24 +444,17 @@ String OS_UWP::get_name() const {
return "UWP";
}
-OS::Date OS_UWP::get_date(bool p_utc) const {
- SYSTEMTIME systemtime;
- if (p_utc) {
- GetSystemTime(&systemtime);
- } else {
- GetLocalTime(&systemtime);
- }
+String OS_UWP::get_distribution_name() const {
+ return get_name();
+}
- Date date;
- date.day = systemtime.wDay;
- date.month = Month(systemtime.wMonth);
- date.weekday = Weekday(systemtime.wDayOfWeek);
- date.year = systemtime.wYear;
- date.dst = false;
- return date;
+String OS_UWP::get_version() const {
+ winrt::hstring df_version = VersionInfo().DeviceFamilyVersion();
+ static String version = String(winrt::to_string(df_version).c_str());
+ return version;
}
-OS::Time OS_UWP::get_time(bool p_utc) const {
+OS::DateTime OS_UWP::get_datetime(bool p_utc) const {
SYSTEMTIME systemtime;
if (p_utc) {
GetSystemTime(&systemtime);
@@ -469,11 +462,23 @@ OS::Time OS_UWP::get_time(bool p_utc) const {
GetLocalTime(&systemtime);
}
- Time time;
- time.hour = systemtime.wHour;
- time.min = systemtime.wMinute;
- time.sec = systemtime.wSecond;
- return time;
+ //Get DST information from Windows, but only if p_utc is false.
+ TIME_ZONE_INFORMATION info;
+ bool daylight = false;
+ if (!p_utc && GetTimeZoneInformation(&info) == TIME_ZONE_ID_DAYLIGHT) {
+ daylight = true;
+ }
+
+ DateTime dt;
+ dt.year = systemtime.wYear;
+ dt.month = Month(systemtime.wMonth);
+ dt.day = systemtime.wDay;
+ dt.weekday = Weekday(systemtime.wDayOfWeek);
+ dt.hour = systemtime.wHour;
+ dt.minute = systemtime.wMinute;
+ dt.second = systemtime.wSecond;
+ dt.dst = daylight;
+ return dt;
}
OS::TimeZoneInfo OS_UWP::get_time_zone_info() const {
diff --git a/platform/uwp/os_uwp.h b/platform/uwp/os_uwp.h
index 5a58486ee7..c1df7827cd 100644
--- a/platform/uwp/os_uwp.h
+++ b/platform/uwp/os_uwp.h
@@ -42,9 +42,9 @@
#include "servers/rendering/renderer_compositor.h"
#include "servers/rendering_server.h"
-#include <fcntl.h>
#include <io.h>
#include <stdio.h>
+
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
@@ -183,9 +183,10 @@ public:
virtual MainLoop *get_main_loop() const;
virtual String get_name() const;
+ virtual String get_distribution_name() const;
+ virtual String get_version() const;
- virtual Date get_date(bool p_utc) const;
- virtual Time get_time(bool p_utc) const;
+ virtual DateTime get_datetime(bool p_utc) const;
virtual TimeZoneInfo get_time_zone_info() const;
virtual uint64_t get_unix_time() const;
diff --git a/platform/web/.eslintrc.engine.js b/platform/web/.eslintrc.engine.js
index 78df6d41d9..a76bd46b9e 100644
--- a/platform/web/.eslintrc.engine.js
+++ b/platform/web/.eslintrc.engine.js
@@ -5,6 +5,7 @@ module.exports = {
"globals": {
"InternalConfig": true,
"Godot": true,
+ "Features": true,
"Preloader": true,
},
};
diff --git a/platform/web/SCsub b/platform/web/SCsub
index ae9d628857..013b734be2 100644
--- a/platform/web/SCsub
+++ b/platform/web/SCsub
@@ -6,7 +6,7 @@ web_files = [
"audio_driver_web.cpp",
"display_server_web.cpp",
"http_client_web.cpp",
- "javascript_singleton.cpp",
+ "javascript_bridge_singleton.cpp",
"web_main.cpp",
"os_web.cpp",
"api/web_tools_editor_plugin.cpp",
@@ -66,6 +66,7 @@ sys_env.Depends(build[0], sys_env["JS_PRE"])
sys_env.Depends(build[0], sys_env["JS_EXTERNS"])
engine = [
+ "js/engine/features.js",
"js/engine/preloader.js",
"js/engine/config.js",
"js/engine/engine.js",
diff --git a/platform/web/api/api.cpp b/platform/web/api/api.cpp
index a724b0456d..e637f2aef2 100644
--- a/platform/web/api/api.cpp
+++ b/platform/web/api/api.cpp
@@ -30,66 +30,66 @@
#include "api.h"
#include "core/config/engine.h"
-#include "javascript_singleton.h"
+#include "javascript_bridge_singleton.h"
#include "web_tools_editor_plugin.h"
-static JavaScript *javascript_singleton;
+static JavaScriptBridge *javascript_bridge_singleton;
void register_web_api() {
WebToolsEditorPlugin::initialize();
GDREGISTER_ABSTRACT_CLASS(JavaScriptObject);
- GDREGISTER_ABSTRACT_CLASS(JavaScript);
- javascript_singleton = memnew(JavaScript);
- Engine::get_singleton()->add_singleton(Engine::Singleton("JavaScript", javascript_singleton));
+ GDREGISTER_ABSTRACT_CLASS(JavaScriptBridge);
+ javascript_bridge_singleton = memnew(JavaScriptBridge);
+ Engine::get_singleton()->add_singleton(Engine::Singleton("JavaScriptBridge", javascript_bridge_singleton));
}
void unregister_web_api() {
- memdelete(javascript_singleton);
+ memdelete(javascript_bridge_singleton);
}
-JavaScript *JavaScript::singleton = nullptr;
+JavaScriptBridge *JavaScriptBridge::singleton = nullptr;
-JavaScript *JavaScript::get_singleton() {
+JavaScriptBridge *JavaScriptBridge::get_singleton() {
return singleton;
}
-JavaScript::JavaScript() {
- ERR_FAIL_COND_MSG(singleton != nullptr, "JavaScript singleton already exist.");
+JavaScriptBridge::JavaScriptBridge() {
+ ERR_FAIL_COND_MSG(singleton != nullptr, "JavaScriptBridge singleton already exist.");
singleton = this;
}
-JavaScript::~JavaScript() {}
+JavaScriptBridge::~JavaScriptBridge() {}
-void JavaScript::_bind_methods() {
- ClassDB::bind_method(D_METHOD("eval", "code", "use_global_execution_context"), &JavaScript::eval, DEFVAL(false));
- ClassDB::bind_method(D_METHOD("get_interface", "interface"), &JavaScript::get_interface);
- ClassDB::bind_method(D_METHOD("create_callback", "callable"), &JavaScript::create_callback);
+void JavaScriptBridge::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("eval", "code", "use_global_execution_context"), &JavaScriptBridge::eval, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_interface", "interface"), &JavaScriptBridge::get_interface);
+ ClassDB::bind_method(D_METHOD("create_callback", "callable"), &JavaScriptBridge::create_callback);
{
MethodInfo mi;
mi.name = "create_object";
mi.arguments.push_back(PropertyInfo(Variant::STRING, "object"));
- ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "create_object", &JavaScript::_create_object_bind, mi);
+ ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "create_object", &JavaScriptBridge::_create_object_bind, mi);
}
- ClassDB::bind_method(D_METHOD("download_buffer", "buffer", "name", "mime"), &JavaScript::download_buffer, DEFVAL("application/octet-stream"));
- ClassDB::bind_method(D_METHOD("pwa_needs_update"), &JavaScript::pwa_needs_update);
- ClassDB::bind_method(D_METHOD("pwa_update"), &JavaScript::pwa_update);
+ ClassDB::bind_method(D_METHOD("download_buffer", "buffer", "name", "mime"), &JavaScriptBridge::download_buffer, DEFVAL("application/octet-stream"));
+ ClassDB::bind_method(D_METHOD("pwa_needs_update"), &JavaScriptBridge::pwa_needs_update);
+ ClassDB::bind_method(D_METHOD("pwa_update"), &JavaScriptBridge::pwa_update);
ADD_SIGNAL(MethodInfo("pwa_update_available"));
}
#if !defined(WEB_ENABLED) || !defined(JAVASCRIPT_EVAL_ENABLED)
-Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) {
+Variant JavaScriptBridge::eval(const String &p_code, bool p_use_global_exec_context) {
return Variant();
}
-Ref<JavaScriptObject> JavaScript::get_interface(const String &p_interface) {
+Ref<JavaScriptObject> JavaScriptBridge::get_interface(const String &p_interface) {
return Ref<JavaScriptObject>();
}
-Ref<JavaScriptObject> JavaScript::create_callback(const Callable &p_callable) {
+Ref<JavaScriptObject> JavaScriptBridge::create_callback(const Callable &p_callable) {
return Ref<JavaScriptObject>();
}
-Variant JavaScript::_create_object_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+Variant JavaScriptBridge::_create_object_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
if (p_argcount < 1) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
r_error.argument = 0;
@@ -105,12 +105,12 @@ Variant JavaScript::_create_object_bind(const Variant **p_args, int p_argcount,
}
#endif
#if !defined(WEB_ENABLED)
-bool JavaScript::pwa_needs_update() const {
+bool JavaScriptBridge::pwa_needs_update() const {
return false;
}
-Error JavaScript::pwa_update() {
+Error JavaScriptBridge::pwa_update() {
return ERR_UNAVAILABLE;
}
-void JavaScript::download_buffer(Vector<uint8_t> p_arr, const String &p_name, const String &p_mime) {
+void JavaScriptBridge::download_buffer(Vector<uint8_t> p_arr, const String &p_name, const String &p_mime) {
}
#endif
diff --git a/platform/web/api/javascript_singleton.h b/platform/web/api/javascript_bridge_singleton.h
index e93b0a18a1..1e7b5a1699 100644
--- a/platform/web/api/javascript_singleton.h
+++ b/platform/web/api/javascript_bridge_singleton.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* javascript_singleton.h */
+/* javascript_bridge_singleton.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef JAVASCRIPT_SINGLETON_H
-#define JAVASCRIPT_SINGLETON_H
+#ifndef JAVASCRIPT_BRIDGE_SINGLETON_H
+#define JAVASCRIPT_BRIDGE_SINGLETON_H
#include "core/object/class_db.h"
#include "core/object/ref_counted.h"
@@ -44,11 +44,11 @@ protected:
virtual void _get_property_list(List<PropertyInfo> *p_list) const {}
};
-class JavaScript : public Object {
+class JavaScriptBridge : public Object {
private:
- GDCLASS(JavaScript, Object);
+ GDCLASS(JavaScriptBridge, Object);
- static JavaScript *singleton;
+ static JavaScriptBridge *singleton;
protected:
static void _bind_methods();
@@ -62,9 +62,9 @@ public:
bool pwa_needs_update() const;
Error pwa_update();
- static JavaScript *get_singleton();
- JavaScript();
- ~JavaScript();
+ static JavaScriptBridge *get_singleton();
+ JavaScriptBridge();
+ ~JavaScriptBridge();
};
-#endif // JAVASCRIPT_SINGLETON_H
+#endif // JAVASCRIPT_BRIDGE_SINGLETON_H
diff --git a/platform/web/audio_driver_web.cpp b/platform/web/audio_driver_web.cpp
index 0e37afc2cc..c4b27c782d 100644
--- a/platform/web/audio_driver_web.cpp
+++ b/platform/web/audio_driver_web.cpp
@@ -184,51 +184,6 @@ Error AudioDriverWeb::capture_stop() {
return OK;
}
-#ifdef NO_THREADS
-/// ScriptProcessorNode implementation
-AudioDriverScriptProcessor *AudioDriverScriptProcessor::singleton = nullptr;
-
-void AudioDriverScriptProcessor::_process_callback() {
- AudioDriverScriptProcessor::singleton->_audio_driver_capture();
- AudioDriverScriptProcessor::singleton->_audio_driver_process();
-}
-
-Error AudioDriverScriptProcessor::create(int &p_buffer_samples, int p_channels) {
- if (!godot_audio_has_script_processor()) {
- return ERR_UNAVAILABLE;
- }
- return (Error)godot_audio_script_create(&p_buffer_samples, p_channels);
-}
-
-void AudioDriverScriptProcessor::start(float *p_out_buf, int p_out_buf_size, float *p_in_buf, int p_in_buf_size) {
- godot_audio_script_start(p_in_buf, p_in_buf_size, p_out_buf, p_out_buf_size, &_process_callback);
-}
-
-/// AudioWorkletNode implementation (no threads)
-AudioDriverWorklet *AudioDriverWorklet::singleton = nullptr;
-
-Error AudioDriverWorklet::create(int &p_buffer_size, int p_channels) {
- if (!godot_audio_has_worklet()) {
- return ERR_UNAVAILABLE;
- }
- return (Error)godot_audio_worklet_create(p_channels);
-}
-
-void AudioDriverWorklet::start(float *p_out_buf, int p_out_buf_size, float *p_in_buf, int p_in_buf_size) {
- _audio_driver_process();
- godot_audio_worklet_start_no_threads(p_out_buf, p_out_buf_size, &_process_callback, p_in_buf, p_in_buf_size, &_capture_callback);
-}
-
-void AudioDriverWorklet::_process_callback(int p_pos, int p_samples) {
- AudioDriverWorklet *driver = AudioDriverWorklet::singleton;
- driver->_audio_driver_process(p_pos, p_samples);
-}
-
-void AudioDriverWorklet::_capture_callback(int p_pos, int p_samples) {
- AudioDriverWorklet *driver = AudioDriverWorklet::singleton;
- driver->_audio_driver_capture(p_pos, p_samples);
-}
-#else
/// AudioWorkletNode implementation (threads)
void AudioDriverWorklet::_audio_thread_func(void *p_data) {
AudioDriverWorklet *driver = static_cast<AudioDriverWorklet *>(p_data);
@@ -290,4 +245,3 @@ void AudioDriverWorklet::finish_driver() {
quit = true; // Ask thread to quit.
thread.wait_to_finish();
}
-#endif
diff --git a/platform/web/audio_driver_web.h b/platform/web/audio_driver_web.h
index dfce277c0c..0a322d61b4 100644
--- a/platform/web/audio_driver_web.h
+++ b/platform/web/audio_driver_web.h
@@ -89,46 +89,6 @@ public:
AudioDriverWeb() {}
};
-#ifdef NO_THREADS
-class AudioDriverScriptProcessor : public AudioDriverWeb {
-private:
- static void _process_callback();
-
- static AudioDriverScriptProcessor *singleton;
-
-protected:
- Error create(int &p_buffer_samples, int p_channels) override;
- void start(float *p_out_buf, int p_out_buf_size, float *p_in_buf, int p_in_buf_size) override;
-
-public:
- virtual const char *get_name() const override { return "ScriptProcessor"; }
-
- virtual void lock() override {}
- virtual void unlock() override {}
-
- AudioDriverScriptProcessor() { singleton = this; }
-};
-
-class AudioDriverWorklet : public AudioDriverWeb {
-private:
- static void _process_callback(int p_pos, int p_samples);
- static void _capture_callback(int p_pos, int p_samples);
-
- static AudioDriverWorklet *singleton;
-
-protected:
- virtual Error create(int &p_buffer_size, int p_output_channels) override;
- virtual void start(float *p_out_buf, int p_out_buf_size, float *p_in_buf, int p_in_buf_size) override;
-
-public:
- virtual const char *get_name() const override { return "AudioWorklet"; }
-
- virtual void lock() override {}
- virtual void unlock() override {}
-
- AudioDriverWorklet() { singleton = this; }
-};
-#else
class AudioDriverWorklet : public AudioDriverWeb {
private:
enum {
@@ -156,6 +116,5 @@ public:
void lock() override;
void unlock() override;
};
-#endif
#endif // AUDIO_DRIVER_WEB_H
diff --git a/platform/web/detect.py b/platform/web/detect.py
index b1c1dd48a9..08c1ff7b4a 100644
--- a/platform/web/detect.py
+++ b/platform/web/detect.py
@@ -11,6 +11,10 @@ from emscripten_helpers import (
)
from methods import get_compiler_version
from SCons.Util import WhereIs
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from SCons import Environment
def is_active():
@@ -31,7 +35,6 @@ def get_opts():
return [
("initial_memory", "Initial WASM memory (in MiB)", 32),
BoolVariable("use_assertions", "Use Emscripten runtime assertions", False),
- BoolVariable("use_thinlto", "Use ThinLTO", False),
BoolVariable("use_ubsan", "Use Emscripten undefined behavior sanitizer (UBSAN)", False),
BoolVariable("use_asan", "Use Emscripten address sanitizer (ASAN)", False),
BoolVariable("use_lsan", "Use Emscripten leak sanitizer (LSAN)", False),
@@ -48,7 +51,7 @@ def get_opts():
def get_flags():
return [
("arch", "wasm32"),
- ("tools", False),
+ ("target", "template_debug"),
("builtin_pcre2_with_jit", False),
("vulkan", False),
# Use -Os to prioritize optimizing for reduced file size. This is
@@ -61,7 +64,7 @@ def get_flags():
]
-def configure(env):
+def configure(env: "Environment"):
# Validate arch.
supported_arches = ["wasm32"]
if env["arch"] not in supported_arches:
@@ -78,26 +81,17 @@ def configure(env):
sys.exit(255)
## Build type
- if env["target"].startswith("release"):
- if env["optimize"] == "size":
- env.Append(CCFLAGS=["-Os"])
- env.Append(LINKFLAGS=["-Os"])
- elif env["optimize"] == "speed":
- env.Append(CCFLAGS=["-O3"])
- env.Append(LINKFLAGS=["-O3"])
-
- if env["target"] == "release_debug":
- # Retain function names for backtraces at the cost of file size.
- env.Append(LINKFLAGS=["--profiling-funcs"])
- else: # "debug"
- env.Append(CCFLAGS=["-O1", "-g"])
- env.Append(LINKFLAGS=["-O1", "-g"])
+
+ if env.debug_features:
+ # Retain function names for backtraces at the cost of file size.
+ env.Append(LINKFLAGS=["--profiling-funcs"])
+ else:
env["use_assertions"] = True
if env["use_assertions"]:
env.Append(LINKFLAGS=["-s", "ASSERTIONS=1"])
- if env["tools"]:
+ if env.editor_build:
if env["initial_memory"] < 64:
print('Note: Forcing "initial_memory=64" as it is required for the web editor.')
env["initial_memory"] = 64
@@ -110,12 +104,17 @@ def configure(env):
env["ENV"] = os.environ
# LTO
- if env["use_thinlto"]:
- env.Append(CCFLAGS=["-flto=thin"])
- env.Append(LINKFLAGS=["-flto=thin"])
- elif env["use_lto"]:
- env.Append(CCFLAGS=["-flto=full"])
- env.Append(LINKFLAGS=["-flto=full"])
+
+ if env["lto"] == "auto": # Full LTO for production.
+ env["lto"] = "full"
+
+ if env["lto"] != "none":
+ if env["lto"] == "thin":
+ env.Append(CCFLAGS=["-flto=thin"])
+ env.Append(LINKFLAGS=["-flto=thin"])
+ else:
+ env.Append(CCFLAGS=["-flto"])
+ env.Append(LINKFLAGS=["-flto"])
# Sanitizers
if env["use_ubsan"]:
@@ -227,3 +226,7 @@ def configure(env):
# Add code that allow exiting runtime.
env.Append(LINKFLAGS=["-s", "EXIT_RUNTIME=1"])
+
+ # This workaround creates a closure that prevents the garbage collector from freeing the WebGL context.
+ # We also only use WebGL2, and changing context version is not widely supported anyway.
+ env.Append(LINKFLAGS=["-s", "GL_WORKAROUND_SAFARI_GETCONTEXT_BUG=0"])
diff --git a/platform/web/display_server_web.cpp b/platform/web/display_server_web.cpp
index b36f9d14a4..f6a61b18e4 100644
--- a/platform/web/display_server_web.cpp
+++ b/platform/web/display_server_web.cpp
@@ -764,10 +764,10 @@ DisplayServerWeb::DisplayServerWeb(const String &p_rendering_driver, WindowMode
if (wants_webgl2 && !webgl2_init_failed) {
EmscriptenWebGLContextAttributes attributes;
emscripten_webgl_init_context_attributes(&attributes);
- //attributes.alpha = GLOBAL_GET("display/window/per_pixel_transparency/allowed");
- attributes.alpha = true;
+ attributes.alpha = OS::get_singleton()->is_layered_allowed();
attributes.antialias = false;
attributes.majorVersion = 2;
+ attributes.explicitSwapControl = true;
webgl_ctx = emscripten_webgl_create_context(canvas_id, &attributes);
if (emscripten_webgl_make_context_current(webgl_ctx) != EMSCRIPTEN_RESULT_SUCCESS) {
@@ -997,7 +997,7 @@ void DisplayServerWeb::window_set_mode(WindowMode p_mode, WindowID p_window) {
} break;
case WINDOW_MODE_MAXIMIZED:
case WINDOW_MODE_MINIMIZED:
- WARN_PRINT("WindowMode MAXIMIZED and MINIMIZED are not supported in Web platform.");
+ // WindowMode MAXIMIZED and MINIMIZED are not supported in Web platform.
break;
default:
break;
diff --git a/platform/web/emscripten_helpers.py b/platform/web/emscripten_helpers.py
index 6045bc6fbd..ec33397842 100644
--- a/platform/web/emscripten_helpers.py
+++ b/platform/web/emscripten_helpers.py
@@ -38,7 +38,7 @@ def create_engine_file(env, target, source, externs):
def create_template_zip(env, js, wasm, worker, side):
- binary_name = "godot.tools" if env["tools"] else "godot"
+ binary_name = "godot.editor" if env.editor_build else "godot"
zip_dir = env.Dir("#bin/.web_zip")
in_files = [
js,
@@ -58,19 +58,19 @@ def create_template_zip(env, js, wasm, worker, side):
out_files.append(zip_dir.File(binary_name + ".side.wasm"))
service_worker = "#misc/dist/html/service-worker.js"
- if env["tools"]:
+ if env.editor_build:
# HTML
html = "#misc/dist/html/editor.html"
cache = [
- "godot.tools.html",
+ "godot.editor.html",
"offline.html",
- "godot.tools.js",
- "godot.tools.worker.js",
- "godot.tools.audio.worklet.js",
+ "godot.editor.js",
+ "godot.editor.worker.js",
+ "godot.editor.audio.worklet.js",
"logo.svg",
"favicon.png",
]
- opt_cache = ["godot.tools.wasm"]
+ opt_cache = ["godot.editor.wasm"]
subst_dict = {
"@GODOT_VERSION@": get_build_version(),
"@GODOT_NAME@": "GodotEngine",
diff --git a/platform/web/export/editor_http_server.h b/platform/web/export/editor_http_server.h
index 38b9a66d7e..fa0010ec8d 100644
--- a/platform/web/export/editor_http_server.h
+++ b/platform/web/export/editor_http_server.h
@@ -32,7 +32,7 @@
#define WEB_EDITOR_HTTP_SERVER_H
#include "core/io/image_loader.h"
-#include "core/io/stream_peer_ssl.h"
+#include "core/io/stream_peer_tls.h"
#include "core/io/tcp_server.h"
#include "core/io/zip_io.h"
#include "editor/editor_paths.h"
@@ -42,18 +42,18 @@ private:
Ref<TCPServer> server;
HashMap<String, String> mimes;
Ref<StreamPeerTCP> tcp;
- Ref<StreamPeerSSL> ssl;
+ Ref<StreamPeerTLS> tls;
Ref<StreamPeer> peer;
Ref<CryptoKey> key;
Ref<X509Certificate> cert;
- bool use_ssl = false;
+ bool use_tls = false;
uint64_t time = 0;
uint8_t req_buf[4096];
int req_pos = 0;
void _clear_client() {
peer = Ref<StreamPeer>();
- ssl = Ref<StreamPeerSSL>();
+ tls = Ref<StreamPeerTLS>();
tcp = Ref<StreamPeerTCP>();
memset(req_buf, 0, sizeof(req_buf));
time = 0;
@@ -98,19 +98,19 @@ public:
_clear_client();
}
- Error listen(int p_port, IPAddress p_address, bool p_use_ssl, String p_ssl_key, String p_ssl_cert) {
- use_ssl = p_use_ssl;
- if (use_ssl) {
+ Error listen(int p_port, IPAddress p_address, bool p_use_tls, String p_tls_key, String p_tls_cert) {
+ use_tls = p_use_tls;
+ if (use_tls) {
Ref<Crypto> crypto = Crypto::create();
if (crypto.is_null()) {
return ERR_UNAVAILABLE;
}
- if (!p_ssl_key.is_empty() && !p_ssl_cert.is_empty()) {
+ if (!p_tls_key.is_empty() && !p_tls_cert.is_empty()) {
key = Ref<CryptoKey>(CryptoKey::create());
- Error err = key->load(p_ssl_key);
+ Error err = key->load(p_tls_key);
ERR_FAIL_COND_V(err != OK, err);
cert = Ref<X509Certificate>(X509Certificate::create());
- err = cert->load(p_ssl_cert);
+ err = cert->load(p_tls_cert);
ERR_FAIL_COND_V(err != OK, err);
} else {
_set_internal_certs(crypto);
@@ -201,22 +201,22 @@ public:
return;
}
- if (use_ssl) {
- if (ssl.is_null()) {
- ssl = Ref<StreamPeerSSL>(StreamPeerSSL::create());
- peer = ssl;
- ssl->set_blocking_handshake_enabled(false);
- if (ssl->accept_stream(tcp, key, cert) != OK) {
+ if (use_tls) {
+ if (tls.is_null()) {
+ tls = Ref<StreamPeerTLS>(StreamPeerTLS::create());
+ peer = tls;
+ tls->set_blocking_handshake_enabled(false);
+ if (tls->accept_stream(tcp, key, cert) != OK) {
_clear_client();
return;
}
}
- ssl->poll();
- if (ssl->get_status() == StreamPeerSSL::STATUS_HANDSHAKING) {
+ tls->poll();
+ if (tls->get_status() == StreamPeerTLS::STATUS_HANDSHAKING) {
// Still handshaking, keep waiting.
return;
}
- if (ssl->get_status() != StreamPeerSSL::STATUS_CONNECTED) {
+ if (tls->get_status() != StreamPeerTLS::STATUS_CONNECTED) {
_clear_client();
return;
}
diff --git a/platform/web/export/export.cpp b/platform/web/export/export.cpp
index 3d40f2c10d..4b4e8b2705 100644
--- a/platform/web/export/export.cpp
+++ b/platform/web/export/export.cpp
@@ -34,14 +34,16 @@
#include "export_plugin.h"
void register_web_exporter() {
+#ifndef ANDROID_ENABLED
EDITOR_DEF("export/web/http_host", "localhost");
EDITOR_DEF("export/web/http_port", 8060);
- EDITOR_DEF("export/web/use_ssl", false);
- EDITOR_DEF("export/web/ssl_key", "");
- EDITOR_DEF("export/web/ssl_certificate", "");
+ EDITOR_DEF("export/web/use_tls", false);
+ EDITOR_DEF("export/web/tls_key", "");
+ EDITOR_DEF("export/web/tls_certificate", "");
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "export/web/http_port", PROPERTY_HINT_RANGE, "1,65535,1"));
- EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/web/ssl_key", PROPERTY_HINT_GLOBAL_FILE, "*.key"));
- EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/web/ssl_certificate", PROPERTY_HINT_GLOBAL_FILE, "*.crt,*.pem"));
+ EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/web/tls_key", PROPERTY_HINT_GLOBAL_FILE, "*.key"));
+ EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/web/tls_certificate", PROPERTY_HINT_GLOBAL_FILE, "*.crt,*.pem"));
+#endif
Ref<EditorExportPlatformWeb> platform;
platform.instantiate();
diff --git a/platform/web/export/export_plugin.cpp b/platform/web/export/export_plugin.cpp
index 9971481459..1c327fe4b2 100644
--- a/platform/web/export/export_plugin.cpp
+++ b/platform/web/export/export_plugin.cpp
@@ -307,13 +307,7 @@ void EditorExportPlatformWeb::get_preset_features(const Ref<EditorExportPreset>
}
if (p_preset->get("vram_texture_compression/for_mobile")) {
- String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name");
- if (driver == "opengl3") {
- r_features->push_back("etc");
- } else if (driver == "vulkan") {
- // FIXME: Review if this is correct.
- r_features->push_back("etc2");
- }
+ r_features->push_back("etc2");
}
r_features->push_back("wasm32");
}
@@ -355,15 +349,6 @@ Ref<Texture2D> EditorExportPlatformWeb::get_logo() const {
}
bool EditorExportPlatformWeb::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
-#ifndef DEV_ENABLED
- // We don't provide export templates for the Web platform currently as there
- // is no suitable renderer to use with them. So we forbid exporting and tell
- // users why. This is skipped in DEV_ENABLED so that contributors can still test
- // the pipeline once we start having WebGL or WebGPU support.
- r_error = "The Web platform is currently not supported in Godot 4.0, as there is no suitable renderer for it.\n";
- return false;
-#endif
-
String err;
bool valid = false;
bool extensions = (bool)p_preset->get("variant/extensions_support");
@@ -396,15 +381,6 @@ bool EditorExportPlatformWeb::has_valid_export_configuration(const Ref<EditorExp
}
bool EditorExportPlatformWeb::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const {
-#ifndef DEV_ENABLED
- // We don't provide export templates for the Web platform currently as there
- // is no suitable renderer to use with them. So we forbid exporting and tell
- // users why. This is skipped in DEV_ENABLED so that contributors can still test
- // the pipeline once we start having WebGL or WebGPU support.
- r_error = "The Web platform is currently not supported in Godot 4.0, as there is no suitable renderer for it.\n";
- return false;
-#endif
-
String err;
bool valid = true;
@@ -633,23 +609,23 @@ Error EditorExportPlatformWeb::run(const Ref<EditorExportPreset> &p_preset, int
}
ERR_FAIL_COND_V_MSG(!bind_ip.is_valid(), ERR_INVALID_PARAMETER, "Invalid editor setting 'export/web/http_host': '" + bind_host + "'. Try using '127.0.0.1'.");
- const bool use_ssl = EDITOR_GET("export/web/use_ssl");
- const String ssl_key = EDITOR_GET("export/web/ssl_key");
- const String ssl_cert = EDITOR_GET("export/web/ssl_certificate");
+ const bool use_tls = EDITOR_GET("export/web/use_tls");
+ const String tls_key = EDITOR_GET("export/web/tls_key");
+ const String tls_cert = EDITOR_GET("export/web/tls_certificate");
// Restart server.
{
MutexLock lock(server_lock);
server->stop();
- err = server->listen(bind_port, bind_ip, use_ssl, ssl_key, ssl_cert);
+ err = server->listen(bind_port, bind_ip, use_tls, tls_key, tls_cert);
}
if (err != OK) {
add_message(EXPORT_MESSAGE_ERROR, TTR("Run"), vformat(TTR("Error starting HTTP server: %d."), err));
return err;
}
- OS::get_singleton()->shell_open(String((use_ssl ? "https://" : "http://") + bind_host + ":" + itos(bind_port) + "/tmp_js_export.html"));
+ OS::get_singleton()->shell_open(String((use_tls ? "https://" : "http://") + bind_host + ":" + itos(bind_port) + "/tmp_js_export.html"));
// FIXME: Find out how to clean up export files after running the successfully
// exported game. Might not be trivial.
return OK;
diff --git a/platform/web/export/export_plugin.h b/platform/web/export/export_plugin.h
index 5b7ce5f708..f11e38df09 100644
--- a/platform/web/export/export_plugin.h
+++ b/platform/web/export/export_plugin.h
@@ -33,7 +33,7 @@
#include "core/config/project_settings.h"
#include "core/io/image_loader.h"
-#include "core/io/stream_peer_ssl.h"
+#include "core/io/stream_peer_tls.h"
#include "core/io/tcp_server.h"
#include "core/io/zip_io.h"
#include "editor/editor_node.h"
diff --git a/platform/web/http_client_web.cpp b/platform/web/http_client_web.cpp
index bfdea95f4a..d045275826 100644
--- a/platform/web/http_client_web.cpp
+++ b/platform/web/http_client_web.cpp
@@ -37,14 +37,14 @@ void HTTPClientWeb::_parse_headers(int p_len, const char **p_headers, void *p_re
}
}
-Error HTTPClientWeb::connect_to_host(const String &p_host, int p_port, bool p_ssl, bool p_verify_host) {
+Error HTTPClientWeb::connect_to_host(const String &p_host, int p_port, bool p_tls, bool p_verify_host) {
close();
- if (p_ssl && !p_verify_host) {
+ if (p_tls && !p_verify_host) {
WARN_PRINT("Disabling HTTPClientWeb's host verification is not supported for the Web platform, host will be verified");
}
port = p_port;
- use_tls = p_ssl;
+ use_tls = p_tls;
host = p_host;
diff --git a/platform/web/http_client_web.h b/platform/web/http_client_web.h
index ff776d72af..5059b4693e 100644
--- a/platform/web/http_client_web.h
+++ b/platform/web/http_client_web.h
@@ -86,7 +86,7 @@ public:
Error request(Method p_method, const String &p_url, const Vector<String> &p_headers, const uint8_t *p_body, int p_body_size) override;
- Error connect_to_host(const String &p_host, int p_port = -1, bool p_ssl = false, bool p_verify_host = true) override;
+ Error connect_to_host(const String &p_host, int p_port = -1, bool p_tls = false, bool p_verify_host = true) override;
void set_connection(const Ref<StreamPeer> &p_connection) override;
Ref<StreamPeer> get_connection() const override;
void close() override;
diff --git a/platform/web/javascript_singleton.cpp b/platform/web/javascript_bridge_singleton.cpp
index 36ab4db452..69cd0cece1 100644
--- a/platform/web/javascript_singleton.cpp
+++ b/platform/web/javascript_bridge_singleton.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* javascript_singleton.cpp */
+/* javascript_bridge_singleton.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "api/javascript_singleton.h"
+#include "api/javascript_bridge_singleton.h"
#include "emscripten.h"
#include "os_web.h"
@@ -62,7 +62,7 @@ extern int godot_js_wrapper_create_object(const char *p_method, void **p_args, i
class JavaScriptObjectImpl : public JavaScriptObject {
private:
- friend class JavaScript;
+ friend class JavaScriptBridge;
int _js_id = 0;
Callable _callable;
@@ -272,20 +272,20 @@ void JavaScriptObjectImpl::_callback(void *p_ref, int p_args_id, int p_argc) {
}
}
-Ref<JavaScriptObject> JavaScript::create_callback(const Callable &p_callable) {
+Ref<JavaScriptObject> JavaScriptBridge::create_callback(const Callable &p_callable) {
Ref<JavaScriptObjectImpl> out = memnew(JavaScriptObjectImpl);
out->_callable = p_callable;
out->_js_id = godot_js_wrapper_create_cb(out.ptr(), JavaScriptObjectImpl::_callback);
return out;
}
-Ref<JavaScriptObject> JavaScript::get_interface(const String &p_interface) {
+Ref<JavaScriptObject> JavaScriptBridge::get_interface(const String &p_interface) {
int js_id = godot_js_wrapper_interface_get(p_interface.utf8().get_data());
ERR_FAIL_COND_V_MSG(!js_id, Ref<JavaScriptObject>(), "No interface '" + p_interface + "' registered.");
return Ref<JavaScriptObject>(memnew(JavaScriptObjectImpl(js_id)));
}
-Variant JavaScript::_create_object_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+Variant JavaScriptBridge::_create_object_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
if (p_argcount < 1) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
r_error.argument = 0;
@@ -328,7 +328,7 @@ void *resize_PackedByteArray_and_open_write(void *p_arr, void *r_write, int p_le
return arr->ptrw();
}
-Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) {
+Variant JavaScriptBridge::eval(const String &p_code, bool p_use_global_exec_context) {
union js_eval_ret js_data;
PackedByteArray arr;
VectorWriteProxy<uint8_t> arr_write;
@@ -354,13 +354,13 @@ Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) {
}
#endif // JAVASCRIPT_EVAL_ENABLED
-void JavaScript::download_buffer(Vector<uint8_t> p_arr, const String &p_name, const String &p_mime) {
+void JavaScriptBridge::download_buffer(Vector<uint8_t> p_arr, const String &p_name, const String &p_mime) {
godot_js_os_download_buffer(p_arr.ptr(), p_arr.size(), p_name.utf8().get_data(), p_mime.utf8().get_data());
}
-bool JavaScript::pwa_needs_update() const {
+bool JavaScriptBridge::pwa_needs_update() const {
return OS_Web::get_singleton()->pwa_needs_update();
}
-Error JavaScript::pwa_update() {
+Error JavaScriptBridge::pwa_update() {
return OS_Web::get_singleton()->pwa_update();
}
diff --git a/platform/web/js/engine/config.js b/platform/web/js/engine/config.js
index 9c4b6c2012..41be7b2512 100644
--- a/platform/web/js/engine/config.js
+++ b/platform/web/js/engine/config.js
@@ -317,7 +317,8 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused-
if (!(this.canvas instanceof HTMLCanvasElement)) {
const nodes = document.getElementsByTagName('canvas');
if (nodes.length && nodes[0] instanceof HTMLCanvasElement) {
- this.canvas = nodes[0];
+ const first = nodes[0];
+ this.canvas = /** @type {!HTMLCanvasElement} */ (first);
}
if (!this.canvas) {
throw new Error('No canvas found in page');
diff --git a/platform/web/js/engine/engine.js b/platform/web/js/engine/engine.js
index 6f0d51b2be..9227aa1f05 100644
--- a/platform/web/js/engine/engine.js
+++ b/platform/web/js/engine/engine.js
@@ -61,20 +61,6 @@ const Engine = (function () {
};
/**
- * Check whether WebGL is available. Optionally, specify a particular version of WebGL to check for.
- *
- * @param {number=} [majorVersion=1] The major WebGL version to check for.
- * @returns {boolean} If the given major version of WebGL is available.
- * @function Engine.isWebGLAvailable
- */
- Engine.isWebGLAvailable = function (majorVersion = 1) {
- try {
- return !!document.createElement('canvas').getContext(['webgl', 'webgl2'][majorVersion - 1]);
- } catch (e) { /* Not available */ }
- return false;
- };
-
- /**
* Safe Engine constructor, creates a new prototype for every new instance to avoid prototype pollution.
* @ignore
* @constructor
@@ -265,14 +251,21 @@ const Engine = (function () {
// Also expose static methods as instance methods
Engine.prototype['load'] = Engine.load;
Engine.prototype['unload'] = Engine.unload;
- Engine.prototype['isWebGLAvailable'] = Engine.isWebGLAvailable;
return new Engine(initConfig);
}
// Closure compiler exported static methods.
SafeEngine['load'] = Engine.load;
SafeEngine['unload'] = Engine.unload;
- SafeEngine['isWebGLAvailable'] = Engine.isWebGLAvailable;
+
+ // Feature-detection utilities.
+ SafeEngine['isWebGLAvailable'] = Features.isWebGLAvailable;
+ SafeEngine['isFetchAvailable'] = Features.isFetchAvailable;
+ SafeEngine['isSecureContext'] = Features.isSecureContext;
+ SafeEngine['isCrossOriginIsolated'] = Features.isCrossOriginIsolated;
+ SafeEngine['isSharedArrayBufferAvailable'] = Features.isSharedArrayBufferAvailable;
+ SafeEngine['isAudioWorkletAvailable'] = Features.isAudioWorkletAvailable;
+ SafeEngine['getMissingFeatures'] = Features.getMissingFeatures;
return SafeEngine;
}());
diff --git a/platform/web/js/engine/features.js b/platform/web/js/engine/features.js
new file mode 100644
index 0000000000..f91a4eff81
--- /dev/null
+++ b/platform/web/js/engine/features.js
@@ -0,0 +1,96 @@
+const Features = { // eslint-disable-line no-unused-vars
+ /**
+ * Check whether WebGL is available. Optionally, specify a particular version of WebGL to check for.
+ *
+ * @param {number=} [majorVersion=1] The major WebGL version to check for.
+ * @returns {boolean} If the given major version of WebGL is available.
+ * @function Engine.isWebGLAvailable
+ */
+ isWebGLAvailable: function (majorVersion = 1) {
+ try {
+ return !!document.createElement('canvas').getContext(['webgl', 'webgl2'][majorVersion - 1]);
+ } catch (e) { /* Not available */ }
+ return false;
+ },
+
+ /**
+ * Check whether the Fetch API available and supports streaming responses.
+ *
+ * @returns {boolean} If the Fetch API is available and supports streaming responses.
+ * @function Engine.isFetchAvailable
+ */
+ isFetchAvailable: function () {
+ return 'fetch' in window && 'Response' in window && 'body' in window.Response.prototype;
+ },
+
+ /**
+ * Check whether the engine is running in a Secure Context.
+ *
+ * @returns {boolean} If the engine is running in a Secure Context.
+ * @function Engine.isSecureContext
+ */
+ isSecureContext: function () {
+ return window['isSecureContext'] === true;
+ },
+
+ /**
+ * Check whether the engine is cross origin isolated.
+ * This value is dependent on Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy headers sent by the server.
+ *
+ * @returns {boolean} If the engine is running in a Secure Context.
+ * @function Engine.isSecureContext
+ */
+ isCrossOriginIsolated: function () {
+ return window['crossOriginIsolated'] === true;
+ },
+
+ /**
+ * Check whether SharedBufferArray is available.
+ *
+ * Most browsers require the page to be running in a secure context, and the
+ * the server to provide specific CORS headers for SharedArrayBuffer to be available.
+ *
+ * @returns {boolean} If SharedArrayBuffer is available.
+ * @function Engine.isSharedArrayBufferAvailable
+ */
+ isSharedArrayBufferAvailable: function () {
+ return 'SharedArrayBuffer' in window;
+ },
+
+ /**
+ * Check whether the AudioContext supports AudioWorkletNodes.
+ *
+ * @returns {boolean} If AudioWorkletNode is available.
+ * @function Engine.isAudioWorkletAvailable
+ */
+ isAudioWorkletAvailable: function () {
+ return 'AudioContext' in window && 'audioWorklet' in AudioContext.prototype;
+ },
+
+ /**
+ * Return an array of missing required features (as string).
+ *
+ * @returns {Array<string>} A list of human-readable missing features.
+ * @function Engine.getMissingFeatures
+ */
+ getMissingFeatures: function () {
+ const missing = [];
+ if (!Features.isWebGLAvailable(2)) {
+ missing.push('WebGL2');
+ }
+ if (!Features.isFetchAvailable()) {
+ missing.push('Fetch');
+ }
+ if (!Features.isSecureContext()) {
+ missing.push('Secure Context');
+ }
+ if (!Features.isCrossOriginIsolated()) {
+ missing.push('Cross Origin Isolation');
+ }
+ if (!Features.isSharedArrayBufferAvailable()) {
+ missing.push('SharedArrayBuffer');
+ }
+ // Audio is normally optional since we have a dummy fallback.
+ return missing;
+ },
+};
diff --git a/platform/web/js/libs/audio.worklet.js b/platform/web/js/libs/audio.worklet.js
index ea4d8cb221..daf5c9ef12 100644
--- a/platform/web/js/libs/audio.worklet.js
+++ b/platform/web/js/libs/audio.worklet.js
@@ -133,6 +133,8 @@ class GodotProcessor extends AudioWorkletProcessor {
this.running = false;
this.output = null;
this.input = null;
+ this.lock = null;
+ this.notifier = null;
} else if (p_cmd === 'start_nothreads') {
this.output = new RingBuffer(p_data[0], p_data[0].length, false);
} else if (p_cmd === 'chunk') {
diff --git a/platform/web/js/libs/library_godot_audio.js b/platform/web/js/libs/library_godot_audio.js
index 756c1ac595..68e100cca0 100644
--- a/platform/web/js/libs/library_godot_audio.js
+++ b/platform/web/js/libs/library_godot_audio.js
@@ -339,16 +339,21 @@ const GodotAudioWorklet = {
if (GodotAudioWorklet.promise === null) {
return;
}
- GodotAudioWorklet.promise.then(function () {
+ const p = GodotAudioWorklet.promise;
+ p.then(function () {
GodotAudioWorklet.worklet.port.postMessage({
'cmd': 'stop',
'data': null,
});
GodotAudioWorklet.worklet.disconnect();
+ GodotAudioWorklet.worklet.port.onmessage = null;
GodotAudioWorklet.worklet = null;
GodotAudioWorklet.promise = null;
resolve();
- }).catch(function (err) { /* aborted? */ });
+ }).catch(function (err) {
+ // Aborted?
+ GodotRuntime.error(err);
+ });
});
},
},
diff --git a/platform/web/js/libs/library_godot_os.js b/platform/web/js/libs/library_godot_os.js
index 377eec3234..ce64fb98c0 100644
--- a/platform/web/js/libs/library_godot_os.js
+++ b/platform/web/js/libs/library_godot_os.js
@@ -106,12 +106,14 @@ autoAddDeps(GodotConfig, '$GodotConfig');
mergeInto(LibraryManager.library, GodotConfig);
const GodotFS = {
- $GodotFS__deps: ['$ERRNO_CODES', '$FS', '$IDBFS', '$GodotRuntime'],
+ $GodotFS__deps: ['$FS', '$IDBFS', '$GodotRuntime'],
$GodotFS__postset: [
'Module["initFS"] = GodotFS.init;',
'Module["copyToFS"] = GodotFS.copy_to_fs;',
].join(''),
$GodotFS: {
+ // ERRNO_CODES works every odd version of emscripten, but this will break too eventually.
+ ENOENT: 44,
_idbfs: false,
_syncing: false,
_mount_points: [],
@@ -138,8 +140,9 @@ const GodotFS = {
try {
FS.stat(dir);
} catch (e) {
- if (e.errno !== ERRNO_CODES.ENOENT) {
- throw e;
+ if (e.errno !== GodotFS.ENOENT) {
+ // Let mkdirTree throw in case, we cannot trust the above check.
+ GodotRuntime.error(e);
}
FS.mkdirTree(dir);
}
@@ -208,8 +211,9 @@ const GodotFS = {
try {
FS.stat(dir);
} catch (e) {
- if (e.errno !== ERRNO_CODES.ENOENT) {
- throw e;
+ if (e.errno !== GodotFS.ENOENT) {
+ // Let mkdirTree throw in case, we cannot trust the above check.
+ GodotRuntime.error(e);
}
FS.mkdirTree(dir);
}
diff --git a/platform/web/os_web.cpp b/platform/web/os_web.cpp
index f9714f25e7..c263ee094b 100644
--- a/platform/web/os_web.cpp
+++ b/platform/web/os_web.cpp
@@ -45,7 +45,7 @@
#include <emscripten.h>
#include <stdlib.h>
-#include "api/javascript_singleton.h"
+#include "api/javascript_bridge_singleton.h"
#include "godot_js.h"
void OS_Web::alert(const String &p_alert, const String &p_title) {
@@ -199,8 +199,8 @@ void OS_Web::update_pwa_state_callback() {
if (OS_Web::get_singleton()) {
OS_Web::get_singleton()->pwa_is_waiting = true;
}
- if (JavaScript::get_singleton()) {
- JavaScript::get_singleton()->emit_signal("pwa_update_available");
+ if (JavaScriptBridge::get_singleton()) {
+ JavaScriptBridge::get_singleton()->emit_signal("pwa_update_available");
}
}
@@ -239,9 +239,6 @@ OS_Web::OS_Web() {
godot_js_pwa_cb(&OS_Web::update_pwa_state_callback);
if (AudioDriverWeb::is_available()) {
-#ifdef NO_THREADS
- audio_drivers.push_back(memnew(AudioDriverScriptProcessor));
-#endif
audio_drivers.push_back(memnew(AudioDriverWorklet));
}
for (int i = 0; i < audio_drivers.size(); i++) {
diff --git a/platform/web/package-lock.json b/platform/web/package-lock.json
index f8c67b206f..9a7d871c64 100644
--- a/platform/web/package-lock.json
+++ b/platform/web/package-lock.json
@@ -12,8 +12,7 @@
"eslint": "^7.28.0",
"eslint-config-airbnb-base": "^14.2.1",
"eslint-plugin-import": "^2.23.4",
- "jsdoc": "^3.6.7",
- "serve": "^13.0.2"
+ "jsdoc": "^3.6.7"
}
},
"node_modules/@babel/code-frame": {
@@ -131,25 +130,6 @@
"integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==",
"dev": true
},
- "node_modules/@zeit/schemas": {
- "version": "2.6.0",
- "resolved": "https://registry.npmjs.org/@zeit/schemas/-/schemas-2.6.0.tgz",
- "integrity": "sha512-uUrgZ8AxS+Lio0fZKAipJjAh415JyrOZowliZAzmnJSsf7piVL5w+G0+gFJ0KSu3QRhvui/7zuvpLz03YjXAhg==",
- "dev": true
- },
- "node_modules/accepts": {
- "version": "1.3.7",
- "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
- "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
- "dev": true,
- "dependencies": {
- "mime-types": "~2.1.24",
- "negotiator": "0.6.2"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
"node_modules/acorn": {
"version": "7.4.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
@@ -187,15 +167,6 @@
"url": "https://github.com/sponsors/epoberezkin"
}
},
- "node_modules/ansi-align": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
- "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==",
- "dev": true,
- "dependencies": {
- "string-width": "^4.1.0"
- }
- },
"node_modules/ansi-colors": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
@@ -226,32 +197,6 @@
"node": ">=4"
}
},
- "node_modules/arch": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz",
- "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ]
- },
- "node_modules/arg": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/arg/-/arg-2.0.0.tgz",
- "integrity": "sha512-XxNTUzKnz1ctK3ZIcI2XUPlD96wbHP2nGqkPKpvk/HNRlPveYrXIVSTk9m3LcqOgDPg3B1nMvdV/K8wZd7PG4w==",
- "dev": true
- },
"node_modules/argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
@@ -318,28 +263,6 @@
"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
"dev": true
},
- "node_modules/boxen": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz",
- "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==",
- "dev": true,
- "dependencies": {
- "ansi-align": "^3.0.0",
- "camelcase": "^6.2.0",
- "chalk": "^4.1.0",
- "cli-boxes": "^2.2.1",
- "string-width": "^4.2.2",
- "type-fest": "^0.20.2",
- "widest-line": "^3.1.0",
- "wrap-ansi": "^7.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -350,15 +273,6 @@
"concat-map": "0.0.1"
}
},
- "node_modules/bytes": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
- "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=",
- "dev": true,
- "engines": {
- "node": ">= 0.8"
- }
- },
"node_modules/call-bind": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
@@ -381,18 +295,6 @@
"node": ">=6"
}
},
- "node_modules/camelcase": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
- "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
- "dev": true,
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/catharsis": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz",
@@ -475,32 +377,6 @@
"node": ">=8"
}
},
- "node_modules/cli-boxes": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz",
- "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==",
- "dev": true,
- "engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/clipboardy": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-2.3.0.tgz",
- "integrity": "sha512-mKhiIL2DrQIsuXMgBgnfEHOZOryC7kY7YO//TN6c63wlEm3NG5tz+YgY5rVi29KCmq/QQjKYvM7a19+MDOTHOQ==",
- "dev": true,
- "dependencies": {
- "arch": "^2.1.1",
- "execa": "^1.0.0",
- "is-wsl": "^2.1.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@@ -516,51 +392,6 @@
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
"dev": true
},
- "node_modules/compressible": {
- "version": "2.0.18",
- "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
- "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
- "dev": true,
- "dependencies": {
- "mime-db": ">= 1.43.0 < 2"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/compression": {
- "version": "1.7.3",
- "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.3.tgz",
- "integrity": "sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg==",
- "dev": true,
- "dependencies": {
- "accepts": "~1.3.5",
- "bytes": "3.0.0",
- "compressible": "~2.0.14",
- "debug": "2.6.9",
- "on-headers": "~1.0.1",
- "safe-buffer": "5.1.2",
- "vary": "~1.1.2"
- },
- "engines": {
- "node": ">= 0.8.0"
- }
- },
- "node_modules/compression/node_modules/debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "dev": true,
- "dependencies": {
- "ms": "2.0.0"
- }
- },
- "node_modules/compression/node_modules/ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
- "dev": true
- },
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -573,15 +404,6 @@
"integrity": "sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA==",
"dev": true
},
- "node_modules/content-disposition": {
- "version": "0.5.2",
- "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
- "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=",
- "dev": true,
- "engines": {
- "node": ">= 0.6"
- }
- },
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -613,15 +435,6 @@
}
}
},
- "node_modules/deep-extend": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
- "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
- "dev": true,
- "engines": {
- "node": ">=4.0.0"
- }
- },
"node_modules/deep-is": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
@@ -658,15 +471,6 @@
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true
},
- "node_modules/end-of-stream": {
- "version": "1.4.4",
- "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
- "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
- "dev": true,
- "dependencies": {
- "once": "^1.4.0"
- }
- },
"node_modules/enquirer": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz",
@@ -1075,91 +879,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/execa": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
- "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
- "dev": true,
- "dependencies": {
- "cross-spawn": "^6.0.0",
- "get-stream": "^4.0.0",
- "is-stream": "^1.1.0",
- "npm-run-path": "^2.0.0",
- "p-finally": "^1.0.0",
- "signal-exit": "^3.0.0",
- "strip-eof": "^1.0.0"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/execa/node_modules/cross-spawn": {
- "version": "6.0.5",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
- "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
- "dev": true,
- "dependencies": {
- "nice-try": "^1.0.4",
- "path-key": "^2.0.1",
- "semver": "^5.5.0",
- "shebang-command": "^1.2.0",
- "which": "^1.2.9"
- },
- "engines": {
- "node": ">=4.8"
- }
- },
- "node_modules/execa/node_modules/path-key": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
- "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
- "dev": true,
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/execa/node_modules/semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
- "dev": true,
- "bin": {
- "semver": "bin/semver"
- }
- },
- "node_modules/execa/node_modules/shebang-command": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
- "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
- "dev": true,
- "dependencies": {
- "shebang-regex": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/execa/node_modules/shebang-regex": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
- "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/execa/node_modules/which": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
- "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
- "dev": true,
- "dependencies": {
- "isexe": "^2.0.0"
- },
- "bin": {
- "which": "bin/which"
- }
- },
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -1178,21 +897,6 @@
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
"dev": true
},
- "node_modules/fast-url-parser": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz",
- "integrity": "sha1-9K8+qfNNiicc9YrSs3WfQx8LMY0=",
- "dev": true,
- "dependencies": {
- "punycode": "^1.3.2"
- }
- },
- "node_modules/fast-url-parser/node_modules/punycode": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
- "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
- "dev": true
- },
"node_modules/file-entry-cache": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
@@ -1268,18 +972,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/get-stream": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
- "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
- "dev": true,
- "dependencies": {
- "pump": "^3.0.0"
- },
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/glob": {
"version": "7.1.7",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
@@ -1431,12 +1123,6 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
},
- "node_modules/ini": {
- "version": "1.3.8",
- "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
- "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
- "dev": true
- },
"node_modules/is-arrayish": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
@@ -1503,21 +1189,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/is-docker": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
- "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
- "dev": true,
- "bin": {
- "is-docker": "cli.js"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -1588,15 +1259,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/is-stream": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
- "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/is-string": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.6.tgz",
@@ -1624,18 +1286,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/is-wsl": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
- "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
- "dev": true,
- "dependencies": {
- "is-docker": "^2.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -1883,27 +1533,6 @@
"integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=",
"dev": true
},
- "node_modules/mime-db": {
- "version": "1.51.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz",
- "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==",
- "dev": true,
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/mime-types": {
- "version": "2.1.34",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz",
- "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==",
- "dev": true,
- "dependencies": {
- "mime-db": "1.51.0"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
"node_modules/minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
@@ -1946,21 +1575,6 @@
"integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
"dev": true
},
- "node_modules/negotiator": {
- "version": "0.6.2",
- "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
- "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==",
- "dev": true,
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/nice-try": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
- "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
- "dev": true
- },
"node_modules/normalize-package-data": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
@@ -1982,27 +1596,6 @@
"semver": "bin/semver"
}
},
- "node_modules/npm-run-path": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
- "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
- "dev": true,
- "dependencies": {
- "path-key": "^2.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/npm-run-path/node_modules/path-key": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
- "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
- "dev": true,
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/object-inspect": {
"version": "1.10.3",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.3.tgz",
@@ -2070,15 +1663,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/on-headers": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
- "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
- "dev": true,
- "engines": {
- "node": ">= 0.8"
- }
- },
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -2105,15 +1689,6 @@
"node": ">= 0.8.0"
}
},
- "node_modules/p-finally": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
- "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
- "dev": true,
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/p-limit": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
@@ -2190,12 +1765,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/path-is-inside": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
- "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
- "dev": true
- },
"node_modules/path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
@@ -2211,12 +1780,6 @@
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true
},
- "node_modules/path-to-regexp": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.2.1.tgz",
- "integrity": "sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==",
- "dev": true
- },
"node_modules/path-type": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
@@ -2280,16 +1843,6 @@
"node": ">=0.4.0"
}
},
- "node_modules/pump": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
- "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
- "dev": true,
- "dependencies": {
- "end-of-stream": "^1.1.0",
- "once": "^1.3.1"
- }
- },
"node_modules/punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
@@ -2299,39 +1852,6 @@
"node": ">=6"
}
},
- "node_modules/range-parser": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
- "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=",
- "dev": true,
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/rc": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
- "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
- "dev": true,
- "dependencies": {
- "deep-extend": "^0.6.0",
- "ini": "~1.3.0",
- "minimist": "^1.2.0",
- "strip-json-comments": "~2.0.1"
- },
- "bin": {
- "rc": "cli.js"
- }
- },
- "node_modules/rc/node_modules/strip-json-comments": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
- "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/read-pkg": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
@@ -2371,28 +1891,6 @@
"url": "https://github.com/sponsors/mysticatea"
}
},
- "node_modules/registry-auth-token": {
- "version": "3.3.2",
- "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz",
- "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==",
- "dev": true,
- "dependencies": {
- "rc": "^1.1.6",
- "safe-buffer": "^5.0.1"
- }
- },
- "node_modules/registry-url": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz",
- "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=",
- "dev": true,
- "dependencies": {
- "rc": "^1.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/require-from-string": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
@@ -2448,12 +1946,6 @@
"url": "https://github.com/sponsors/isaacs"
}
},
- "node_modules/safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
- "dev": true
- },
"node_modules/semver": {
"version": "7.3.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
@@ -2469,86 +1961,6 @@
"node": ">=10"
}
},
- "node_modules/serve": {
- "version": "13.0.2",
- "resolved": "https://registry.npmjs.org/serve/-/serve-13.0.2.tgz",
- "integrity": "sha512-71R6fKvNgKrqARAag6lYJNnxDzpH7DCNrMuvPY5PLVaC2PDhJsGTj/34o4o4tPWhTuLgEXqvgnAWbATQ9zGZTQ==",
- "dev": true,
- "dependencies": {
- "@zeit/schemas": "2.6.0",
- "ajv": "6.12.6",
- "arg": "2.0.0",
- "boxen": "5.1.2",
- "chalk": "2.4.1",
- "clipboardy": "2.3.0",
- "compression": "1.7.3",
- "serve-handler": "6.1.3",
- "update-check": "1.5.2"
- },
- "bin": {
- "serve": "bin/serve.js"
- }
- },
- "node_modules/serve-handler": {
- "version": "6.1.3",
- "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.3.tgz",
- "integrity": "sha512-FosMqFBNrLyeiIDvP1zgO6YoTzFYHxLDEIavhlmQ+knB2Z7l1t+kGLHkZIDN7UVWqQAmKI3D20A6F6jo3nDd4w==",
- "dev": true,
- "dependencies": {
- "bytes": "3.0.0",
- "content-disposition": "0.5.2",
- "fast-url-parser": "1.1.3",
- "mime-types": "2.1.18",
- "minimatch": "3.0.4",
- "path-is-inside": "1.0.2",
- "path-to-regexp": "2.2.1",
- "range-parser": "1.2.0"
- }
- },
- "node_modules/serve-handler/node_modules/mime-db": {
- "version": "1.33.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz",
- "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==",
- "dev": true,
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/serve-handler/node_modules/mime-types": {
- "version": "2.1.18",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz",
- "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==",
- "dev": true,
- "dependencies": {
- "mime-db": "~1.33.0"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/serve/node_modules/chalk": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
- "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
- "dev": true,
- "dependencies": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/serve/node_modules/escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true,
- "engines": {
- "node": ">=0.8.0"
- }
- },
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@@ -2570,12 +1982,6 @@
"node": ">=8"
}
},
- "node_modules/signal-exit": {
- "version": "3.0.6",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz",
- "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==",
- "dev": true
- },
"node_modules/slice-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz",
@@ -2725,15 +2131,6 @@
"node": ">=4"
}
},
- "node_modules/strip-eof": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
- "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/strip-json-comments": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
@@ -2872,16 +2269,6 @@
"integrity": "sha512-QvjkYpiD+dJJraRA8+dGAU4i7aBbb2s0S3jA45TFOvg2VgqvdCDd/3N6CqA8gluk1W91GLoXg5enMUx560QzuA==",
"dev": true
},
- "node_modules/update-check": {
- "version": "1.5.2",
- "resolved": "https://registry.npmjs.org/update-check/-/update-check-1.5.2.tgz",
- "integrity": "sha512-1TrmYLuLj/5ZovwUS7fFd1jMH3NnFDN1y1A8dboedIDt7zs/zJMo6TwwlhYKkSeEwzleeiSBV5/3c9ufAQWDaQ==",
- "dev": true,
- "dependencies": {
- "registry-auth-token": "3.3.2",
- "registry-url": "3.1.0"
- }
- },
"node_modules/uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
@@ -2907,15 +2294,6 @@
"spdx-expression-parse": "^3.0.0"
}
},
- "node_modules/vary": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
- "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
- "dev": true,
- "engines": {
- "node": ">= 0.8"
- }
- },
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -2947,18 +2325,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/widest-line": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz",
- "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==",
- "dev": true,
- "dependencies": {
- "string-width": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/word-wrap": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
@@ -2968,56 +2334,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/wrap-ansi": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
- "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
- "dev": true,
- "dependencies": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
- }
- },
- "node_modules/wrap-ansi/node_modules/ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
- "dependencies": {
- "color-convert": "^2.0.1"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/wrap-ansi/node_modules/color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
- "dependencies": {
- "color-name": "~1.1.4"
- },
- "engines": {
- "node": ">=7.0.0"
- }
- },
- "node_modules/wrap-ansi/node_modules/color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true
- },
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
@@ -3134,22 +2450,6 @@
"integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==",
"dev": true
},
- "@zeit/schemas": {
- "version": "2.6.0",
- "resolved": "https://registry.npmjs.org/@zeit/schemas/-/schemas-2.6.0.tgz",
- "integrity": "sha512-uUrgZ8AxS+Lio0fZKAipJjAh415JyrOZowliZAzmnJSsf7piVL5w+G0+gFJ0KSu3QRhvui/7zuvpLz03YjXAhg==",
- "dev": true
- },
- "accepts": {
- "version": "1.3.7",
- "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
- "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
- "dev": true,
- "requires": {
- "mime-types": "~2.1.24",
- "negotiator": "0.6.2"
- }
- },
"acorn": {
"version": "7.4.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
@@ -3175,15 +2475,6 @@
"uri-js": "^4.2.2"
}
},
- "ansi-align": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
- "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==",
- "dev": true,
- "requires": {
- "string-width": "^4.1.0"
- }
- },
"ansi-colors": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
@@ -3205,18 +2496,6 @@
"color-convert": "^1.9.0"
}
},
- "arch": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz",
- "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==",
- "dev": true
- },
- "arg": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/arg/-/arg-2.0.0.tgz",
- "integrity": "sha512-XxNTUzKnz1ctK3ZIcI2XUPlD96wbHP2nGqkPKpvk/HNRlPveYrXIVSTk9m3LcqOgDPg3B1nMvdV/K8wZd7PG4w==",
- "dev": true
- },
"argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
@@ -3268,22 +2547,6 @@
"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
"dev": true
},
- "boxen": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz",
- "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==",
- "dev": true,
- "requires": {
- "ansi-align": "^3.0.0",
- "camelcase": "^6.2.0",
- "chalk": "^4.1.0",
- "cli-boxes": "^2.2.1",
- "string-width": "^4.2.2",
- "type-fest": "^0.20.2",
- "widest-line": "^3.1.0",
- "wrap-ansi": "^7.0.0"
- }
- },
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -3294,12 +2557,6 @@
"concat-map": "0.0.1"
}
},
- "bytes": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
- "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=",
- "dev": true
- },
"call-bind": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
@@ -3316,12 +2573,6 @@
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
"dev": true
},
- "camelcase": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
- "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
- "dev": true
- },
"catharsis": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz",
@@ -3382,23 +2633,6 @@
}
}
},
- "cli-boxes": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz",
- "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==",
- "dev": true
- },
- "clipboardy": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-2.3.0.tgz",
- "integrity": "sha512-mKhiIL2DrQIsuXMgBgnfEHOZOryC7kY7YO//TN6c63wlEm3NG5tz+YgY5rVi29KCmq/QQjKYvM7a19+MDOTHOQ==",
- "dev": true,
- "requires": {
- "arch": "^2.1.1",
- "execa": "^1.0.0",
- "is-wsl": "^2.1.1"
- }
- },
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@@ -3414,47 +2648,6 @@
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
"dev": true
},
- "compressible": {
- "version": "2.0.18",
- "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
- "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
- "dev": true,
- "requires": {
- "mime-db": ">= 1.43.0 < 2"
- }
- },
- "compression": {
- "version": "1.7.3",
- "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.3.tgz",
- "integrity": "sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg==",
- "dev": true,
- "requires": {
- "accepts": "~1.3.5",
- "bytes": "3.0.0",
- "compressible": "~2.0.14",
- "debug": "2.6.9",
- "on-headers": "~1.0.1",
- "safe-buffer": "5.1.2",
- "vary": "~1.1.2"
- },
- "dependencies": {
- "debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "dev": true,
- "requires": {
- "ms": "2.0.0"
- }
- },
- "ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
- "dev": true
- }
- }
- },
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -3467,12 +2660,6 @@
"integrity": "sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA==",
"dev": true
},
- "content-disposition": {
- "version": "0.5.2",
- "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
- "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=",
- "dev": true
- },
"cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -3493,12 +2680,6 @@
"ms": "2.1.2"
}
},
- "deep-extend": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
- "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
- "dev": true
- },
"deep-is": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
@@ -3529,15 +2710,6 @@
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true
},
- "end-of-stream": {
- "version": "1.4.4",
- "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
- "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
- "dev": true,
- "requires": {
- "once": "^1.4.0"
- }
- },
"enquirer": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz",
@@ -3862,72 +3034,6 @@
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
"dev": true
},
- "execa": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
- "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
- "dev": true,
- "requires": {
- "cross-spawn": "^6.0.0",
- "get-stream": "^4.0.0",
- "is-stream": "^1.1.0",
- "npm-run-path": "^2.0.0",
- "p-finally": "^1.0.0",
- "signal-exit": "^3.0.0",
- "strip-eof": "^1.0.0"
- },
- "dependencies": {
- "cross-spawn": {
- "version": "6.0.5",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
- "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
- "dev": true,
- "requires": {
- "nice-try": "^1.0.4",
- "path-key": "^2.0.1",
- "semver": "^5.5.0",
- "shebang-command": "^1.2.0",
- "which": "^1.2.9"
- }
- },
- "path-key": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
- "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
- "dev": true
- },
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
- "dev": true
- },
- "shebang-command": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
- "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
- "dev": true,
- "requires": {
- "shebang-regex": "^1.0.0"
- }
- },
- "shebang-regex": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
- "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
- "dev": true
- },
- "which": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
- "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
- "dev": true,
- "requires": {
- "isexe": "^2.0.0"
- }
- }
- }
- },
"fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -3946,23 +3052,6 @@
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
"dev": true
},
- "fast-url-parser": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz",
- "integrity": "sha1-9K8+qfNNiicc9YrSs3WfQx8LMY0=",
- "dev": true,
- "requires": {
- "punycode": "^1.3.2"
- },
- "dependencies": {
- "punycode": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
- "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
- "dev": true
- }
- }
- },
"file-entry-cache": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
@@ -4026,15 +3115,6 @@
"has-symbols": "^1.0.1"
}
},
- "get-stream": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
- "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
- "dev": true,
- "requires": {
- "pump": "^3.0.0"
- }
- },
"glob": {
"version": "7.1.7",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
@@ -4144,12 +3224,6 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
},
- "ini": {
- "version": "1.3.8",
- "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
- "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
- "dev": true
- },
"is-arrayish": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
@@ -4192,12 +3266,6 @@
"integrity": "sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A==",
"dev": true
},
- "is-docker": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
- "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
- "dev": true
- },
"is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -4241,12 +3309,6 @@
"has-symbols": "^1.0.2"
}
},
- "is-stream": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
- "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
- "dev": true
- },
"is-string": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.6.tgz",
@@ -4262,15 +3324,6 @@
"has-symbols": "^1.0.2"
}
},
- "is-wsl": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
- "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
- "dev": true,
- "requires": {
- "is-docker": "^2.0.0"
- }
- },
"isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -4480,21 +3533,6 @@
"integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=",
"dev": true
},
- "mime-db": {
- "version": "1.51.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz",
- "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==",
- "dev": true
- },
- "mime-types": {
- "version": "2.1.34",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz",
- "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==",
- "dev": true,
- "requires": {
- "mime-db": "1.51.0"
- }
- },
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
@@ -4528,18 +3566,6 @@
"integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
"dev": true
},
- "negotiator": {
- "version": "0.6.2",
- "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
- "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==",
- "dev": true
- },
- "nice-try": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
- "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
- "dev": true
- },
"normalize-package-data": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
@@ -4560,23 +3586,6 @@
}
}
},
- "npm-run-path": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
- "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
- "dev": true,
- "requires": {
- "path-key": "^2.0.0"
- },
- "dependencies": {
- "path-key": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
- "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
- "dev": true
- }
- }
- },
"object-inspect": {
"version": "1.10.3",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.3.tgz",
@@ -4623,12 +3632,6 @@
"es-abstract": "^1.18.2"
}
},
- "on-headers": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
- "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
- "dev": true
- },
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -4652,12 +3655,6 @@
"word-wrap": "^1.2.3"
}
},
- "p-finally": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
- "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
- "dev": true
- },
"p-limit": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
@@ -4713,12 +3710,6 @@
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"dev": true
},
- "path-is-inside": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
- "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
- "dev": true
- },
"path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
@@ -4731,12 +3722,6 @@
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true
},
- "path-to-regexp": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.2.1.tgz",
- "integrity": "sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==",
- "dev": true
- },
"path-type": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
@@ -4782,48 +3767,12 @@
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
"dev": true
},
- "pump": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
- "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
- "dev": true,
- "requires": {
- "end-of-stream": "^1.1.0",
- "once": "^1.3.1"
- }
- },
"punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
"dev": true
},
- "range-parser": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
- "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=",
- "dev": true
- },
- "rc": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
- "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
- "dev": true,
- "requires": {
- "deep-extend": "^0.6.0",
- "ini": "~1.3.0",
- "minimist": "^1.2.0",
- "strip-json-comments": "~2.0.1"
- },
- "dependencies": {
- "strip-json-comments": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
- "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
- "dev": true
- }
- }
- },
"read-pkg": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
@@ -4851,25 +3800,6 @@
"integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
"dev": true
},
- "registry-auth-token": {
- "version": "3.3.2",
- "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz",
- "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==",
- "dev": true,
- "requires": {
- "rc": "^1.1.6",
- "safe-buffer": "^5.0.1"
- }
- },
- "registry-url": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz",
- "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=",
- "dev": true,
- "requires": {
- "rc": "^1.0.1"
- }
- },
"require-from-string": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
@@ -4910,12 +3840,6 @@
"glob": "^7.1.3"
}
},
- "safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
- "dev": true
- },
"semver": {
"version": "7.3.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
@@ -4925,75 +3849,6 @@
"lru-cache": "^6.0.0"
}
},
- "serve": {
- "version": "13.0.2",
- "resolved": "https://registry.npmjs.org/serve/-/serve-13.0.2.tgz",
- "integrity": "sha512-71R6fKvNgKrqARAag6lYJNnxDzpH7DCNrMuvPY5PLVaC2PDhJsGTj/34o4o4tPWhTuLgEXqvgnAWbATQ9zGZTQ==",
- "dev": true,
- "requires": {
- "@zeit/schemas": "2.6.0",
- "ajv": "6.12.6",
- "arg": "2.0.0",
- "boxen": "5.1.2",
- "chalk": "2.4.1",
- "clipboardy": "2.3.0",
- "compression": "1.7.3",
- "serve-handler": "6.1.3",
- "update-check": "1.5.2"
- },
- "dependencies": {
- "chalk": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
- "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
- "dev": true,
- "requires": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- }
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- }
- }
- },
- "serve-handler": {
- "version": "6.1.3",
- "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.3.tgz",
- "integrity": "sha512-FosMqFBNrLyeiIDvP1zgO6YoTzFYHxLDEIavhlmQ+knB2Z7l1t+kGLHkZIDN7UVWqQAmKI3D20A6F6jo3nDd4w==",
- "dev": true,
- "requires": {
- "bytes": "3.0.0",
- "content-disposition": "0.5.2",
- "fast-url-parser": "1.1.3",
- "mime-types": "2.1.18",
- "minimatch": "3.0.4",
- "path-is-inside": "1.0.2",
- "path-to-regexp": "2.2.1",
- "range-parser": "1.2.0"
- },
- "dependencies": {
- "mime-db": {
- "version": "1.33.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz",
- "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==",
- "dev": true
- },
- "mime-types": {
- "version": "2.1.18",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz",
- "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==",
- "dev": true,
- "requires": {
- "mime-db": "~1.33.0"
- }
- }
- }
- },
"shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@@ -5009,12 +3864,6 @@
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
"dev": true
},
- "signal-exit": {
- "version": "3.0.6",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz",
- "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==",
- "dev": true
- },
"slice-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz",
@@ -5136,12 +3985,6 @@
"integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
"dev": true
},
- "strip-eof": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
- "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
- "dev": true
- },
"strip-json-comments": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
@@ -5254,16 +4097,6 @@
"integrity": "sha512-QvjkYpiD+dJJraRA8+dGAU4i7aBbb2s0S3jA45TFOvg2VgqvdCDd/3N6CqA8gluk1W91GLoXg5enMUx560QzuA==",
"dev": true
},
- "update-check": {
- "version": "1.5.2",
- "resolved": "https://registry.npmjs.org/update-check/-/update-check-1.5.2.tgz",
- "integrity": "sha512-1TrmYLuLj/5ZovwUS7fFd1jMH3NnFDN1y1A8dboedIDt7zs/zJMo6TwwlhYKkSeEwzleeiSBV5/3c9ufAQWDaQ==",
- "dev": true,
- "requires": {
- "registry-auth-token": "3.3.2",
- "registry-url": "3.1.0"
- }
- },
"uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
@@ -5289,12 +4122,6 @@
"spdx-expression-parse": "^3.0.0"
}
},
- "vary": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
- "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
- "dev": true
- },
"which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -5317,58 +4144,12 @@
"is-symbol": "^1.0.3"
}
},
- "widest-line": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz",
- "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==",
- "dev": true,
- "requires": {
- "string-width": "^4.0.0"
- }
- },
"word-wrap": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
"dev": true
},
- "wrap-ansi": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
- "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
- "dev": true,
- "requires": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true
- }
- }
- },
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
diff --git a/platform/web/package.json b/platform/web/package.json
index a57205415a..8d4983663b 100644
--- a/platform/web/package.json
+++ b/platform/web/package.json
@@ -4,7 +4,7 @@
"version": "1.0.0",
"description": "Development and linting setup for Godot's Web platform code",
"scripts": {
- "docs": "jsdoc --template js/jsdoc2rst/ js/engine/engine.js js/engine/config.js --destination ''",
+ "docs": "jsdoc --template js/jsdoc2rst/ js/engine/engine.js js/engine/config.js js/engine/features.js --destination ''",
"lint": "npm run lint:engine && npm run lint:libs && npm run lint:modules && npm run lint:tools",
"lint:engine": "eslint \"js/engine/*.js\" --no-eslintrc -c .eslintrc.engine.js",
"lint:libs": "eslint \"js/libs/*.js\" --no-eslintrc -c .eslintrc.libs.js",
@@ -14,8 +14,7 @@
"format:engine": "npm run lint:engine -- --fix",
"format:libs": "npm run lint:libs -- --fix",
"format:modules": "npm run lint:modules -- --fix",
- "format:tools": "npm run lint:tools -- --fix",
- "serve": "serve"
+ "format:tools": "npm run lint:tools -- --fix"
},
"author": "Godot Engine contributors",
"license": "MIT",
@@ -23,7 +22,6 @@
"eslint": "^7.28.0",
"eslint-config-airbnb-base": "^14.2.1",
"eslint-plugin-import": "^2.23.4",
- "jsdoc": "^3.6.7",
- "serve": "^13.0.2"
+ "jsdoc": "^3.6.7"
}
}
diff --git a/platform/web/serve.json b/platform/web/serve.json
deleted file mode 100644
index f2ef24751f..0000000000
--- a/platform/web/serve.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- "public": "../../bin",
- "headers": [{
- "source": "**/*",
- "headers": [
- {
- "key": "Cross-Origin-Embedder-Policy",
- "value": "require-corp"
- }, {
- "key": "Cross-Origin-Opener-Policy",
- "value": "same-origin"
- }, {
- "key": "Access-Control-Allow-Origin",
- "value": "*"
- }, {
- "key": "Cache-Control",
- "value": "no-store, max-age=0"
- }
- ]
- }]
-}
diff --git a/platform/web/serve.py b/platform/web/serve.py
new file mode 100755
index 0000000000..14e87e9ea1
--- /dev/null
+++ b/platform/web/serve.py
@@ -0,0 +1,52 @@
+#!/usr/bin/env python3
+
+from http.server import HTTPServer, SimpleHTTPRequestHandler, test # type: ignore
+from pathlib import Path
+import os
+import sys
+import argparse
+import subprocess
+
+
+class CORSRequestHandler(SimpleHTTPRequestHandler):
+ def end_headers(self):
+ self.send_header("Cross-Origin-Opener-Policy", "same-origin")
+ self.send_header("Cross-Origin-Embedder-Policy", "require-corp")
+ self.send_header("Access-Control-Allow-Origin", "*")
+ super().end_headers()
+
+
+def shell_open(url):
+ if sys.platform == "win32":
+ os.startfile(url)
+ else:
+ opener = "open" if sys.platform == "darwin" else "xdg-open"
+ subprocess.call([opener, url])
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser()
+ parser.add_argument("-p", "--port", help="port to listen on", default=8060, type=int)
+ parser.add_argument(
+ "-r", "--root", help="path to serve as root (relative to `platform/web/`)", default="../../bin", type=Path
+ )
+ browser_parser = parser.add_mutually_exclusive_group(required=False)
+ browser_parser.add_argument(
+ "-n", "--no-browser", help="don't open default web browser automatically", dest="browser", action="store_false"
+ )
+ parser.set_defaults(browser=True)
+ args = parser.parse_args()
+
+ # Change to the directory where the script is located,
+ # so that the script can be run from any location.
+ os.chdir(Path(__file__).resolve().parent)
+
+ if args.root:
+ os.chdir(args.root)
+
+ if args.browser:
+ # Open the served page in the user's default browser.
+ print("Opening the served URL in the default browser (use `--no-browser` or `-n` to disable this).")
+ shell_open(f"http://127.0.0.1:{args.port}")
+
+ test(CORSRequestHandler, HTTPServer, port=args.port)
diff --git a/platform/web/web_main.cpp b/platform/web/web_main.cpp
index 0f4411727a..a76b98f4e9 100644
--- a/platform/web/web_main.cpp
+++ b/platform/web/web_main.cpp
@@ -55,6 +55,18 @@ void cleanup_after_sync() {
emscripten_set_main_loop(exit_callback, -1, false);
}
+void early_cleanup() {
+ emscripten_cancel_main_loop(); // After this, we can exit!
+ int exit_code = OS_Web::get_singleton()->get_exit_code();
+ memdelete(os);
+ os = nullptr;
+ emscripten_force_exit(exit_code); // No matter that we call cancel_main_loop, regular "exit" will not work, forcing.
+}
+
+void early_cleanup_sync() {
+ emscripten_set_main_loop(early_cleanup, -1, false);
+}
+
void main_loop_callback() {
uint64_t current_ticks = os->get_ticks_usec();
@@ -65,14 +77,14 @@ void main_loop_callback() {
return; // Skip frame.
}
- int target_fps = Engine::get_singleton()->get_target_fps();
- if (target_fps > 0) {
+ int max_fps = Engine::get_singleton()->get_max_fps();
+ if (max_fps > 0) {
if (current_ticks - target_ticks > 1000000) {
// When the window loses focus, we stop getting updates and accumulate delay.
// For this reason, if the difference is too big, we reset target ticks to the current ticks.
target_ticks = current_ticks;
}
- target_ticks += (uint64_t)(1000000 / target_fps);
+ target_ticks += (uint64_t)(1000000 / max_fps);
}
if (os->main_loop_iterate()) {
emscripten_cancel_main_loop(); // Cancel current loop and wait for cleanup_after_sync.
@@ -87,7 +99,19 @@ extern EMSCRIPTEN_KEEPALIVE int godot_web_main(int argc, char *argv[]) {
// We must override main when testing is enabled
TEST_MAIN_OVERRIDE
- Main::setup(argv[0], argc - 1, &argv[1]);
+ Error err = Main::setup(argv[0], argc - 1, &argv[1]);
+
+ // Proper shutdown in case of setup failure.
+ if (err != OK) {
+ int exit_code = (int)err;
+ if (err == ERR_HELP) {
+ exit_code = 0; // Called with --help.
+ }
+ os->set_exit_code(exit_code);
+ // Will only exit after sync.
+ godot_js_os_finish_async(early_cleanup_sync);
+ return exit_code;
+ }
// Ease up compatibility.
ResourceLoader::set_abort_on_missing_resources(false);
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index 5607eab342..a5d8d0344b 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -4,6 +4,11 @@ import subprocess
import sys
from platform_methods import detect_arch
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from SCons import Environment
+
# To match other platforms
STACK_SIZE = 8388608
@@ -173,17 +178,7 @@ def get_opts():
"Targeted Windows version, >= 0x0601 (Windows 7)",
"0x0601",
),
- BoolVariable(
- "debug_symbols",
- "Add debugging symbols to release/release_debug builds",
- True,
- ),
EnumVariable("windows_subsystem", "Windows subsystem", "gui", ("gui", "console")),
- BoolVariable(
- "separate_debug_symbols",
- "Create a separate file containing debugging symbols",
- False,
- ),
(
"msvc_version",
"MSVC version to use. Ignored if VCINSTALLDIR is set in shell env.",
@@ -191,7 +186,6 @@ def get_opts():
),
BoolVariable("use_mingw", "Use the Mingw compiler, even if MSVC is installed.", False),
BoolVariable("use_llvm", "Use the LLVM compiler", False),
- BoolVariable("use_thinlto", "Use ThinLTO", False),
BoolVariable("use_static_cpp", "Link MinGW/MSVC C++ runtime libraries statically", True),
BoolVariable("use_asan", "Use address sanitizer (ASAN)", False),
]
@@ -331,32 +325,11 @@ def setup_mingw(env):
def configure_msvc(env, vcvars_msvc_config):
"""Configure env to work with MSVC"""
- # Build type
- if env["target"] == "release":
- if env["optimize"] == "speed": # optimize for speed (default)
- env.Append(CCFLAGS=["/O2"])
- env.Append(LINKFLAGS=["/OPT:REF"])
- elif env["optimize"] == "size": # optimize for size
- env.Append(CCFLAGS=["/O1"])
- env.Append(LINKFLAGS=["/OPT:REF"])
- env.Append(LINKFLAGS=["/ENTRY:mainCRTStartup"])
-
- elif env["target"] == "release_debug":
- if env["optimize"] == "speed": # optimize for speed (default)
- env.Append(CCFLAGS=["/O2"])
- env.Append(LINKFLAGS=["/OPT:REF"])
- elif env["optimize"] == "size": # optimize for size
- env.Append(CCFLAGS=["/O1"])
- env.Append(LINKFLAGS=["/OPT:REF"])
-
- elif env["target"] == "debug":
- env.AppendUnique(CCFLAGS=["/Zi", "/FS", "/Od", "/EHsc"])
- # Allow big objects. Only needed for debug, see MinGW branch for rationale.
- env.Append(LINKFLAGS=["/DEBUG"])
+ ## Build type
- if env["debug_symbols"]:
- env.AppendUnique(CCFLAGS=["/Zi", "/FS"])
- env.AppendUnique(LINKFLAGS=["/DEBUG"])
+ # TODO: Re-evaluate the need for this / streamline with common config.
+ if env["target"] == "template_release":
+ env.Append(LINKFLAGS=["/ENTRY:mainCRTStartup"])
if env["windows_subsystem"] == "gui":
env.Append(LINKFLAGS=["/SUBSYSTEM:WINDOWS"])
@@ -449,7 +422,13 @@ def configure_msvc(env, vcvars_msvc_config):
## LTO
- if env["use_lto"]:
+ if env["lto"] == "auto": # No LTO by default for MSVC, doesn't help.
+ env["lto"] = "none"
+
+ if env["lto"] != "none":
+ if env["lto"] == "thin":
+ print("ThinLTO is only compatible with LLVM, use `use_llvm=yes` or `lto=full`.")
+ sys.exit(255)
env.AppendUnique(CCFLAGS=["/GL"])
env.AppendUnique(ARFLAGS=["/LTCG"])
if env["progress"]:
@@ -487,31 +466,10 @@ def configure_mingw(env):
if env["use_llvm"] and not try_cmd("clang --version", env["mingw_prefix"], env["arch"]):
env["use_llvm"] = False
- if env["target"] == "release":
+ # TODO: Re-evaluate the need for this / streamline with common config.
+ if env["target"] == "template_release":
env.Append(CCFLAGS=["-msse2"])
-
- if env["optimize"] == "speed": # optimize for speed (default)
- if env["arch"] == "x86_32":
- env.Append(CCFLAGS=["-O2"])
- else:
- env.Append(CCFLAGS=["-O3"])
- else: # optimize for size
- env.Prepend(CCFLAGS=["-Os"])
-
- if env["debug_symbols"]:
- env.Prepend(CCFLAGS=["-g2"])
-
- elif env["target"] == "release_debug":
- env.Append(CCFLAGS=["-O2"])
- if env["debug_symbols"]:
- env.Prepend(CCFLAGS=["-g2"])
- if env["optimize"] == "speed": # optimize for speed (default)
- env.Append(CCFLAGS=["-O2"])
- else: # optimize for size
- env.Prepend(CCFLAGS=["-Os"])
-
- elif env["target"] == "debug":
- env.Append(CCFLAGS=["-g3"])
+ elif env.dev_build:
# Allow big objects. It's supposed not to have drawbacks but seems to break
# GCC LTO, so enabling for debug builds only (which are not built with LTO
# and are the only ones with too big objects).
@@ -562,17 +520,24 @@ def configure_mingw(env):
if try_cmd("gcc-ranlib --version", env["mingw_prefix"], env["arch"]):
env["RANLIB"] = mingw_bin_prefix + "gcc-ranlib"
- if env["use_lto"]:
- if not env["use_llvm"] and env.GetOption("num_jobs") > 1:
+ ## LTO
+
+ if env["lto"] == "auto": # Full LTO for production with MinGW.
+ env["lto"] = "full"
+
+ if env["lto"] != "none":
+ if env["lto"] == "thin":
+ if not env["use_llvm"]:
+ print("ThinLTO is only compatible with LLVM, use `use_llvm=yes` or `lto=full`.")
+ sys.exit(255)
+ env.Append(CCFLAGS=["-flto=thin"])
+ env.Append(LINKFLAGS=["-flto=thin"])
+ elif not env["use_llvm"] and env.GetOption("num_jobs") > 1:
env.Append(CCFLAGS=["-flto"])
env.Append(LINKFLAGS=["-flto=" + str(env.GetOption("num_jobs"))])
else:
- if env["use_thinlto"]:
- env.Append(CCFLAGS=["-flto=thin"])
- env.Append(LINKFLAGS=["-flto=thin"])
- else:
- env.Append(CCFLAGS=["-flto"])
- env.Append(LINKFLAGS=["-flto"])
+ env.Append(CCFLAGS=["-flto"])
+ env.Append(LINKFLAGS=["-flto"])
env.Append(LINKFLAGS=["-Wl,--stack," + str(STACK_SIZE)])
@@ -628,7 +593,7 @@ def configure_mingw(env):
env.Append(BUILDERS={"RES": env.Builder(action=build_res_file, suffix=".o", src_suffix=".rc")})
-def configure(env):
+def configure(env: "Environment"):
# Validate arch.
supported_arches = ["x86_32", "x86_64", "arm32", "arm64"]
if env["arch"] not in supported_arches:
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index b4949de3f7..22a562c21c 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -104,7 +104,10 @@ void DisplayServerWindows::_set_mouse_mode_impl(MouseMode p_mode) {
if (windows.has(MAIN_WINDOW_ID) && (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_CONFINED || p_mode == MOUSE_MODE_CONFINED_HIDDEN)) {
// Mouse is grabbed (captured or confined).
- WindowID window_id = windows.has(last_focused_window) ? last_focused_window : MAIN_WINDOW_ID;
+ WindowID window_id = _get_focused_window_or_popup();
+ if (!windows.has(window_id)) {
+ window_id = MAIN_WINDOW_ID;
+ }
WindowData &wd = windows[window_id];
@@ -119,11 +122,15 @@ void DisplayServerWindows::_set_mouse_mode_impl(MouseMode p_mode) {
ClientToScreen(wd.hWnd, &pos);
SetCursorPos(pos.x, pos.y);
SetCapture(wd.hWnd);
+
+ _register_raw_input_devices(window_id);
}
} else {
// Mouse is free to move around (not captured or confined).
ReleaseCapture();
ClipCursor(nullptr);
+
+ _register_raw_input_devices(INVALID_WINDOW_ID);
}
if (p_mode == MOUSE_MODE_HIDDEN || p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_CONFINED_HIDDEN) {
@@ -139,6 +146,37 @@ void DisplayServerWindows::_set_mouse_mode_impl(MouseMode p_mode) {
}
}
+DisplayServer::WindowID DisplayServerWindows::_get_focused_window_or_popup() const {
+ const List<WindowID>::Element *E = popup_list.back();
+ if (E) {
+ return E->get();
+ }
+
+ return last_focused_window;
+}
+
+void DisplayServerWindows::_register_raw_input_devices(WindowID p_target_window) {
+ use_raw_input = true;
+
+ RAWINPUTDEVICE rid[1] = {};
+ rid[0].usUsagePage = 0x01;
+ rid[0].usUsage = 0x02;
+ rid[0].dwFlags = 0;
+
+ if (p_target_window != INVALID_WINDOW_ID && windows.has(p_target_window)) {
+ // Follow the defined window
+ rid[0].hwndTarget = windows[p_target_window].hWnd;
+ } else {
+ // Follow the keyboard focus
+ rid[0].hwndTarget = 0;
+ }
+
+ if (RegisterRawInputDevices(rid, 1, sizeof(rid[0])) == FALSE) {
+ // Registration failed.
+ use_raw_input = false;
+ }
+}
+
bool DisplayServerWindows::tts_is_speaking() const {
ERR_FAIL_COND_V(!tts, false);
return tts->is_speaking();
@@ -194,7 +232,9 @@ DisplayServer::MouseMode DisplayServerWindows::mouse_get_mode() const {
void DisplayServerWindows::warp_mouse(const Point2i &p_position) {
_THREAD_SAFE_METHOD_
- if (!windows.has(last_focused_window)) {
+ WindowID window_id = _get_focused_window_or_popup();
+
+ if (!windows.has(window_id)) {
return; // No focused window?
}
@@ -205,7 +245,7 @@ void DisplayServerWindows::warp_mouse(const Point2i &p_position) {
POINT p;
p.x = p_position.x;
p.y = p_position.y;
- ClientToScreen(windows[last_focused_window].hWnd, &p);
+ ClientToScreen(windows[window_id].hWnd, &p);
SetCursorPos(p.x, p.y);
}
@@ -525,7 +565,7 @@ bool DisplayServerWindows::screen_is_touchscreen(int p_screen) const {
#ifndef _MSC_VER
#warning touchscreen not working
#endif
- return false;
+ return DisplayServer::screen_is_touchscreen(p_screen);
}
void DisplayServerWindows::screen_set_orientation(ScreenOrientation p_orientation, int p_screen) {
@@ -645,7 +685,13 @@ void DisplayServerWindows::show_window(WindowID p_id) {
_update_window_style(p_id);
}
- if (wd.no_focus || wd.is_popup) {
+ if (wd.maximized) {
+ ShowWindow(wd.hWnd, SW_SHOWMAXIMIZED);
+ SetForegroundWindow(wd.hWnd); // Slightly higher priority.
+ SetFocus(wd.hWnd); // Set keyboard focus.
+ } else if (wd.minimized) {
+ ShowWindow(wd.hWnd, SW_SHOWMINIMIZED);
+ } else if (wd.no_focus || wd.is_popup) {
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow
ShowWindow(wd.hWnd, SW_SHOWNA);
} else {
@@ -886,7 +932,7 @@ void DisplayServerWindows::window_set_position(const Point2i &p_position, Window
ERR_FAIL_COND(!windows.has(p_window));
WindowData &wd = windows[p_window];
- if (wd.fullscreen) {
+ if (wd.fullscreen || wd.maximized) {
return;
}
@@ -1019,6 +1065,10 @@ void DisplayServerWindows::window_set_size(const Size2i p_size, WindowID p_windo
ERR_FAIL_COND(!windows.has(p_window));
WindowData &wd = windows[p_window];
+ if (wd.fullscreen || wd.maximized) {
+ return;
+ }
+
int w = p_size.width;
int h = p_size.height;
@@ -1036,10 +1086,6 @@ void DisplayServerWindows::window_set_size(const Size2i p_size, WindowID p_windo
}
#endif
- if (wd.fullscreen) {
- return;
- }
-
RECT rect;
GetWindowRect(wd.hWnd, &rect);
@@ -1315,7 +1361,8 @@ void DisplayServerWindows::window_set_flag(WindowFlags p_flag, bool p_enabled, W
if (p_enabled) {
//enable per-pixel alpha
- DWM_BLURBEHIND bb = { 0 };
+ DWM_BLURBEHIND bb;
+ ZeroMemory(&bb, sizeof(bb));
HRGN hRgn = CreateRectRgn(0, 0, -1, -1);
bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
bb.hRgnBlur = hRgn;
@@ -1327,7 +1374,8 @@ void DisplayServerWindows::window_set_flag(WindowFlags p_flag, bool p_enabled, W
//disable per-pixel alpha
wd.layered_window = false;
- DWM_BLURBEHIND bb = { 0 };
+ DWM_BLURBEHIND bb;
+ ZeroMemory(&bb, sizeof(bb));
HRGN hRgn = CreateRectRgn(0, 0, -1, -1);
bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
bb.hRgnBlur = hRgn;
@@ -1344,7 +1392,7 @@ void DisplayServerWindows::window_set_flag(WindowFlags p_flag, bool p_enabled, W
ERR_FAIL_COND_MSG(IsWindowVisible(wd.hWnd) && (wd.is_popup != p_enabled), "Popup flag can't changed while window is opened.");
wd.is_popup = p_enabled;
} break;
- case WINDOW_FLAG_MAX:
+ default:
break;
}
}
@@ -1373,7 +1421,7 @@ bool DisplayServerWindows::window_get_flag(WindowFlags p_flag, WindowID p_window
case WINDOW_FLAG_POPUP: {
return wd.is_popup;
} break;
- case WINDOW_FLAG_MAX:
+ default:
break;
}
@@ -1496,7 +1544,7 @@ void DisplayServerWindows::cursor_set_shape(CursorShape p_shape) {
IDC_HELP
};
- if (cursors[p_shape] != nullptr) {
+ if (cursors_cache.has(p_shape)) {
SetCursor(cursors[p_shape]);
} else {
SetCursor(LoadCursor(hInstance, win_cursors[p_shape]));
@@ -1509,55 +1557,6 @@ DisplayServer::CursorShape DisplayServerWindows::cursor_get_shape() const {
return cursor_shape;
}
-void DisplayServerWindows::GetMaskBitmaps(HBITMAP hSourceBitmap, COLORREF clrTransparent, OUT HBITMAP &hAndMaskBitmap, OUT HBITMAP &hXorMaskBitmap) {
- // Get the system display DC.
- HDC hDC = GetDC(nullptr);
-
- // Create helper DC.
- HDC hMainDC = CreateCompatibleDC(hDC);
- HDC hAndMaskDC = CreateCompatibleDC(hDC);
- HDC hXorMaskDC = CreateCompatibleDC(hDC);
-
- // Get the dimensions of the source bitmap.
- BITMAP bm;
- GetObject(hSourceBitmap, sizeof(BITMAP), &bm);
-
- // Create the mask bitmaps.
- hAndMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight); // Color.
- hXorMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight); // Color.
-
- // Release the system display DC.
- ReleaseDC(nullptr, hDC);
-
- // Select the bitmaps to helper DC.
- HBITMAP hOldMainBitmap = (HBITMAP)SelectObject(hMainDC, hSourceBitmap);
- HBITMAP hOldAndMaskBitmap = (HBITMAP)SelectObject(hAndMaskDC, hAndMaskBitmap);
- HBITMAP hOldXorMaskBitmap = (HBITMAP)SelectObject(hXorMaskDC, hXorMaskBitmap);
-
- // Assign the monochrome AND mask bitmap pixels so that the pixels of the source bitmap
- // with 'clrTransparent' will be white pixels of the monochrome bitmap.
- SetBkColor(hMainDC, clrTransparent);
- BitBlt(hAndMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hMainDC, 0, 0, SRCCOPY);
-
- // Assign the color XOR mask bitmap pixels so that the pixels of the source bitmap
- // with 'clrTransparent' will be black and rest the pixels same as corresponding
- // pixels of the source bitmap.
- SetBkColor(hXorMaskDC, RGB(0, 0, 0));
- SetTextColor(hXorMaskDC, RGB(255, 255, 255));
- BitBlt(hXorMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hAndMaskDC, 0, 0, SRCCOPY);
- BitBlt(hXorMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hMainDC, 0, 0, SRCAND);
-
- // Deselect bitmaps from the helper DC.
- SelectObject(hMainDC, hOldMainBitmap);
- SelectObject(hAndMaskDC, hOldAndMaskBitmap);
- SelectObject(hXorMaskDC, hOldXorMaskBitmap);
-
- // Delete the helper DC.
- DeleteDC(hXorMaskDC);
- DeleteDC(hAndMaskDC);
- DeleteDC(hMainDC);
-}
-
void DisplayServerWindows::cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
_THREAD_SAFE_METHOD_
@@ -1610,8 +1609,26 @@ void DisplayServerWindows::cursor_set_custom_image(const Ref<Resource> &p_cursor
UINT image_size = texture_size.width * texture_size.height;
// Create the BITMAP with alpha channel.
- COLORREF *buffer = (COLORREF *)memalloc(sizeof(COLORREF) * image_size);
-
+ COLORREF *buffer = nullptr;
+
+ BITMAPV5HEADER bi;
+ ZeroMemory(&bi, sizeof(bi));
+ bi.bV5Size = sizeof(bi);
+ bi.bV5Width = texture_size.width;
+ bi.bV5Height = -texture_size.height;
+ bi.bV5Planes = 1;
+ bi.bV5BitCount = 32;
+ bi.bV5Compression = BI_BITFIELDS;
+ bi.bV5RedMask = 0x00ff0000;
+ bi.bV5GreenMask = 0x0000ff00;
+ bi.bV5BlueMask = 0x000000ff;
+ bi.bV5AlphaMask = 0xff000000;
+
+ HDC dc = GetDC(nullptr);
+ HBITMAP bitmap = CreateDIBSection(dc, reinterpret_cast<BITMAPINFO *>(&bi), DIB_RGB_COLORS, reinterpret_cast<void **>(&buffer), nullptr, 0);
+ HBITMAP mask = CreateBitmap(texture_size.width, texture_size.height, 1, 1, nullptr);
+
+ bool fully_transparent = true;
for (UINT index = 0; index < image_size; index++) {
int row_index = floor(index / texture_size.width) + atlas_rect.position.y;
int column_index = (index % int(texture_size.width)) + atlas_rect.position.x;
@@ -1620,39 +1637,28 @@ void DisplayServerWindows::cursor_set_custom_image(const Ref<Resource> &p_cursor
column_index = MIN(column_index, atlas_rect.size.width - 1);
row_index = MIN(row_index, atlas_rect.size.height - 1);
}
+ const Color &c = image->get_pixel(column_index, row_index);
+ fully_transparent = fully_transparent && (c.a == 0.f);
- *(buffer + index) = image->get_pixel(column_index, row_index).to_argb32();
- }
-
- // Using 4 channels, so 4 * 8 bits.
- HBITMAP bitmap = CreateBitmap(texture_size.width, texture_size.height, 1, 4 * 8, buffer);
- COLORREF clrTransparent = -1;
-
- // Create the AND and XOR masks for the bitmap.
- HBITMAP hAndMask = nullptr;
- HBITMAP hXorMask = nullptr;
-
- GetMaskBitmaps(bitmap, clrTransparent, hAndMask, hXorMask);
-
- if (nullptr == hAndMask || nullptr == hXorMask) {
- memfree(buffer);
- DeleteObject(bitmap);
- return;
+ *(buffer + index) = c.to_argb32();
}
// Finally, create the icon.
- ICONINFO iconinfo;
- iconinfo.fIcon = FALSE;
- iconinfo.xHotspot = p_hotspot.x;
- iconinfo.yHotspot = p_hotspot.y;
- iconinfo.hbmMask = hAndMask;
- iconinfo.hbmColor = hXorMask;
-
if (cursors[p_shape]) {
DestroyIcon(cursors[p_shape]);
}
- cursors[p_shape] = CreateIconIndirect(&iconinfo);
+ if (fully_transparent) {
+ cursors[p_shape] = nullptr;
+ } else {
+ ICONINFO iconinfo;
+ iconinfo.fIcon = FALSE;
+ iconinfo.xHotspot = p_hotspot.x;
+ iconinfo.yHotspot = p_hotspot.y;
+ iconinfo.hbmMask = mask;
+ iconinfo.hbmColor = bitmap;
+ cursors[p_shape] = CreateIconIndirect(&iconinfo);
+ }
Vector<Variant> params;
params.push_back(p_cursor);
@@ -1665,17 +1671,15 @@ void DisplayServerWindows::cursor_set_custom_image(const Ref<Resource> &p_cursor
}
}
- DeleteObject(hAndMask);
- DeleteObject(hXorMask);
-
- memfree(buffer);
+ DeleteObject(mask);
DeleteObject(bitmap);
+ ReleaseDC(nullptr, dc);
} else {
// Reset to default system cursor.
if (cursors[p_shape]) {
DestroyIcon(cursors[p_shape]);
- cursors[p_shape] = nullptr;
}
+ cursors[p_shape] = nullptr;
CursorShape c = cursor_shape;
cursor_shape = CURSOR_MAX;
@@ -2419,14 +2423,14 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
} break;
case WM_SETTINGCHANGE: {
if (lParam && CompareStringOrdinal(reinterpret_cast<LPCWCH>(lParam), -1, L"ImmersiveColorSet", -1, true) == CSTR_EQUAL) {
- if (is_dark_mode_supported()) {
+ if (is_dark_mode_supported() && dark_title_available) {
BOOL value = is_dark_mode();
::DwmSetWindowAttribute(windows[window_id].hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value));
}
}
} break;
case WM_THEMECHANGED: {
- if (is_dark_mode_supported()) {
+ if (is_dark_mode_supported() && dark_title_available) {
BOOL value = is_dark_mode();
::DwmSetWindowAttribute(windows[window_id].hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value));
}
@@ -2527,7 +2531,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
old_y = coords.y;
}
- if (windows[window_id].window_has_focus && mm->get_relative() != Vector2()) {
+ if ((windows[window_id].window_has_focus || windows[window_id].is_popup) && mm->get_relative() != Vector2()) {
Input::get_singleton()->parse_input_event(mm);
}
}
@@ -2552,24 +2556,27 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[window_id].wtctx) {
PACKET packet;
if (wintab_WTPacket(windows[window_id].wtctx, wParam, &packet)) {
- float pressure = float(packet.pkNormalPressure - windows[window_id].min_pressure) / float(windows[window_id].max_pressure - windows[window_id].min_pressure);
- windows[window_id].last_pressure = pressure;
+ POINT coords;
+ GetCursorPos(&coords);
+ ScreenToClient(windows[window_id].hWnd, &coords);
+
windows[window_id].last_pressure_update = 0;
+ float pressure = float(packet.pkNormalPressure - windows[window_id].min_pressure) / float(windows[window_id].max_pressure - windows[window_id].min_pressure);
double azim = (packet.pkOrientation.orAzimuth / 10.0f) * (Math_PI / 180);
double alt = Math::tan((Math::abs(packet.pkOrientation.orAltitude / 10.0f)) * (Math_PI / 180));
+ bool inverted = packet.pkStatus & TPS_INVERT;
- if (windows[window_id].tilt_supported) {
- windows[window_id].last_tilt = Vector2(Math::atan(Math::sin(azim) / alt), Math::atan(Math::cos(azim) / alt));
- } else {
- windows[window_id].last_tilt = Vector2();
- }
+ Vector2 tilt = (windows[window_id].tilt_supported) ? Vector2(Math::atan(Math::sin(azim) / alt), Math::atan(Math::cos(azim) / alt)) : Vector2();
- windows[window_id].last_pen_inverted = packet.pkStatus & TPS_INVERT;
+ // Nothing changed, ignore event.
+ if (!old_invalid && coords.x == old_x && coords.y == old_y && windows[window_id].last_pressure == pressure && windows[window_id].last_tilt == tilt && windows[window_id].last_pen_inverted == inverted) {
+ break;
+ }
- POINT coords;
- GetCursorPos(&coords);
- ScreenToClient(windows[window_id].hWnd, &coords);
+ windows[window_id].last_pressure = pressure;
+ windows[window_id].last_tilt = tilt;
+ windows[window_id].last_pen_inverted = inverted;
// Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.
if (!windows[window_id].window_has_focus && mouse_mode == MOUSE_MODE_CAPTURED) {
@@ -3350,7 +3357,7 @@ void DisplayServerWindows::_process_key_events() {
k->set_ctrl_pressed(ke.control);
k->set_meta_pressed(ke.meta);
k->set_pressed(true);
- k->set_keycode((Key)KeyMappingWindows::get_keysym(ke.wParam));
+ k->set_keycode((Key)KeyMappingWindows::get_keysym(MapVirtualKey((ke.lParam >> 16) & 0xFF, MAPVK_VSC_TO_VK)));
k->set_physical_keycode((Key)(KeyMappingWindows::get_scansym((ke.lParam >> 16) & 0xFF, ke.lParam & (1 << 24))));
k->set_unicode(unicode);
if (k->get_unicode() && gr_mem) {
@@ -3541,7 +3548,7 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
wd.pre_fs_valid = true;
}
- if (is_dark_mode_supported()) {
+ if (is_dark_mode_supported() && dark_title_available) {
BOOL value = is_dark_mode();
::DwmSetWindowAttribute(wd.hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value));
}
@@ -3603,6 +3610,16 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
wd.wtctx = 0;
}
+ if (p_mode == WINDOW_MODE_MAXIMIZED) {
+ wd.maximized = true;
+ wd.minimized = false;
+ }
+
+ if (p_mode == WINDOW_MODE_MINIMIZED) {
+ wd.maximized = false;
+ wd.minimized = true;
+ }
+
wd.last_pressure = 0;
wd.last_pressure_update = 0;
wd.last_tilt = Vector2();
@@ -3633,6 +3650,7 @@ WTPacketPtr DisplayServerWindows::wintab_WTPacket = nullptr;
WTEnablePtr DisplayServerWindows::wintab_WTEnable = nullptr;
// UXTheme API.
+bool DisplayServerWindows::dark_title_available = false;
bool DisplayServerWindows::ux_theme_available = false;
IsDarkModeAllowedForAppPtr DisplayServerWindows::IsDarkModeAllowedForApp = nullptr;
ShouldAppsUseDarkModePtr DisplayServerWindows::ShouldAppsUseDarkMode = nullptr;
@@ -3725,7 +3743,21 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
// Enforce default keep screen on value.
screen_set_keep_on(GLOBAL_GET("display/window/energy_saving/keep_screen_on"));
- // Load UXTheme
+ // Load Windows version info.
+ OSVERSIONINFOW os_ver;
+ ZeroMemory(&os_ver, sizeof(OSVERSIONINFOW));
+ os_ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
+
+ HMODULE nt_lib = LoadLibraryW(L"ntdll.dll");
+ if (nt_lib) {
+ RtlGetVersionPtr RtlGetVersion = (RtlGetVersionPtr)GetProcAddress(nt_lib, "RtlGetVersion");
+ if (RtlGetVersion) {
+ RtlGetVersion(&os_ver);
+ }
+ FreeLibrary(nt_lib);
+ }
+
+ // Load UXTheme.
HMODULE ux_theme_lib = LoadLibraryW(L"uxtheme.dll");
if (ux_theme_lib) {
IsDarkModeAllowedForApp = (IsDarkModeAllowedForAppPtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(136));
@@ -3735,6 +3767,9 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
GetImmersiveUserColorSetPreference = (GetImmersiveUserColorSetPreferencePtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(98));
ux_theme_available = IsDarkModeAllowedForApp && ShouldAppsUseDarkMode && GetImmersiveColorFromColorSetEx && GetImmersiveColorTypeFromName && GetImmersiveUserColorSetPreference;
+ if (os_ver.dwBuildNumber >= 22000) {
+ dark_title_available = true;
+ }
}
// Note: Wacom WinTab driver API for pen input, for devices incompatible with Windows Ink.
@@ -3799,19 +3834,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
return;
}
- use_raw_input = true;
-
- RAWINPUTDEVICE Rid[1];
-
- Rid[0].usUsagePage = 0x01;
- Rid[0].usUsage = 0x02;
- Rid[0].dwFlags = 0;
- Rid[0].hwndTarget = 0;
-
- if (RegisterRawInputDevices(Rid, 1, sizeof(Rid[0])) == FALSE) {
- // Registration failed.
- use_raw_input = false;
- }
+ _register_raw_input_devices(INVALID_WINDOW_ID);
#if defined(VULKAN_ENABLED)
if (rendering_driver == "vulkan") {
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index dbc9821970..3523e8b3d1 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -61,9 +61,9 @@
#include "gl_manager_windows.h"
#endif
-#include <fcntl.h>
#include <io.h>
#include <stdio.h>
+
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <windowsx.h>
@@ -157,6 +157,7 @@ typedef bool(WINAPI *ShouldAppsUseDarkModePtr)();
typedef DWORD(WINAPI *GetImmersiveColorFromColorSetExPtr)(UINT dwImmersiveColorSet, UINT dwImmersiveColorType, bool bIgnoreHighContrast, UINT dwHighContrastCacheMode);
typedef int(WINAPI *GetImmersiveColorTypeFromNamePtr)(const WCHAR *name);
typedef int(WINAPI *GetImmersiveUserColorSetPreferencePtr)(bool bForceCheckRegistry, bool bSkipCheckOnFail);
+typedef HRESULT(WINAPI *RtlGetVersionPtr)(OSVERSIONINFOW *lpVersionInformation);
// Windows Ink API
#ifndef POINTER_STRUCTURES
@@ -285,6 +286,7 @@ class DisplayServerWindows : public DisplayServer {
_THREAD_SAFE_CLASS_
// UXTheme API
+ static bool dark_title_available;
static bool ux_theme_available;
static IsDarkModeAllowedForAppPtr IsDarkModeAllowedForApp;
static ShouldAppsUseDarkModePtr ShouldAppsUseDarkMode;
@@ -309,8 +311,6 @@ class DisplayServerWindows : public DisplayServer {
String tablet_driver;
Vector<String> tablet_drivers;
- void GetMaskBitmaps(HBITMAP hSourceBitmap, COLORREF clrTransparent, OUT HBITMAP &hAndMaskBitmap, OUT HBITMAP &hXorMaskBitmap);
-
enum {
KEY_EVENT_BUFFER_SIZE = 512
};
@@ -466,6 +466,8 @@ class DisplayServerWindows : public DisplayServer {
void _update_real_mouse_position(WindowID p_window);
void _set_mouse_mode_impl(MouseMode p_mode);
+ WindowID _get_focused_window_or_popup() const;
+ void _register_raw_input_devices(WindowID p_target_window);
void _process_activate_event(WindowID p_window_id, WPARAM wParam, LPARAM lParam);
void _process_key_events();
diff --git a/platform/windows/export/export.cpp b/platform/windows/export/export.cpp
index 20320470b8..8f91756c02 100644
--- a/platform/windows/export/export.cpp
+++ b/platform/windows/export/export.cpp
@@ -34,6 +34,7 @@
#include "export_plugin.h"
void register_windows_exporter() {
+#ifndef ANDROID_ENABLED
EDITOR_DEF("export/windows/rcedit", "");
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/windows/rcedit", PROPERTY_HINT_GLOBAL_FILE, "*.exe"));
#ifdef WINDOWS_ENABLED
@@ -46,6 +47,7 @@ void register_windows_exporter() {
EDITOR_DEF("export/windows/wine", "");
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/windows/wine", PROPERTY_HINT_GLOBAL_FILE));
#endif
+#endif
Ref<EditorExportPlatformWindows> platform;
platform.instantiate();
diff --git a/platform/windows/gl_manager_windows.cpp b/platform/windows/gl_manager_windows.cpp
index d509ff8c51..7689751f1b 100644
--- a/platform/windows/gl_manager_windows.cpp
+++ b/platform/windows/gl_manager_windows.cpp
@@ -289,12 +289,7 @@ void GLManager_Windows::make_current() {
}
void GLManager_Windows::swap_buffers() {
- // on other platforms, OpenGL swaps buffers for all windows (on all displays, really?)
- // Windows swaps buffers on a per-window basis
- // REVISIT: this could be structurally bad, should we have "dirty" flags then?
- for (KeyValue<DisplayServer::WindowID, GLWindow> &entry : _windows) {
- SwapBuffers(entry.value.hDC);
- }
+ SwapBuffers(_current_window->hDC);
}
Error GLManager_Windows::initialize() {
diff --git a/platform/windows/godot.natvis b/platform/windows/godot.natvis
index cdd1c14978..36b0919185 100644
--- a/platform/windows/godot.natvis
+++ b/platform/windows/godot.natvis
@@ -32,6 +32,38 @@
</Expand>
</Type>
+ <Type Name="HashMap&lt;*,*&gt;">
+ <Expand>
+ <Item Name="[size]">num_elements</Item>
+ <LinkedListItems>
+ <Size>num_elements</Size>
+ <HeadPointer>head_element</HeadPointer>
+ <NextPointer>next</NextPointer>
+ <ValueNode>data</ValueNode>
+ </LinkedListItems>
+ </Expand>
+ </Type>
+
+ <Type Name="VMap&lt;*,*&gt;">
+ <Expand>
+ <Item Condition="_cowdata._ptr" Name="[size]">*(reinterpret_cast&lt;int*&gt;(_cowdata._ptr) - 1)</Item>
+ <ArrayItems Condition="_cowdata._ptr">
+ <Size>*(reinterpret_cast&lt;int*&gt;(_cowdata._ptr) - 1)</Size>
+ <ValuePointer>reinterpret_cast&lt;VMap&lt;$T1,$T2&gt;::Pair*&gt;(_cowdata._ptr)</ValuePointer>
+ </ArrayItems>
+ </Expand>
+ </Type>
+
+ <Type Name="VMap&lt;Callable,*&gt;::Pair">
+ <DisplayString Condition="dynamic_cast&lt;CallableCustomMethodPointerBase*&gt;(key.custom)">{dynamic_cast&lt;CallableCustomMethodPointerBase*&gt;(key.custom)->text}</DisplayString>
+ </Type>
+
+ <!-- requires PR 64364
+ <Type Name="GDScriptThreadContext">
+ <DisplayString Condition="_is_main == true">main thread {_debug_thread_id}</DisplayString>
+ </Type>
+ -->
+
<Type Name="Variant">
<DisplayString Condition="type == Variant::NIL">nil</DisplayString>
<DisplayString Condition="type == Variant::BOOL">{_data._bool}</DisplayString>
@@ -55,15 +87,17 @@
<DisplayString Condition="type == Variant::OBJECT">{*(Object *)_data._mem}</DisplayString>
<DisplayString Condition="type == Variant::DICTIONARY">{*(Dictionary *)_data._mem}</DisplayString>
<DisplayString Condition="type == Variant::ARRAY">{*(Array *)_data._mem}</DisplayString>
- <DisplayString Condition="type == Variant::PACKED_BYTE_ARRAY">{*(PackedByteArray *)_data._mem}</DisplayString>
- <DisplayString Condition="type == Variant::PACKED_INT32_ARRAY">{*(PackedInt32Array *)_data._mem}</DisplayString>
+ <DisplayString Condition="type == Variant::PACKED_BYTE_ARRAY">{reinterpret_cast&lt;const Variant::PackedArrayRef&lt;unsigned char&gt;*&gt;(_data.packed_array)->array}</DisplayString>
+ <DisplayString Condition="type == Variant::PACKED_INT32_ARRAY">{reinterpret_cast&lt;const Variant::PackedArrayRef&lt;int&gt;*&gt;(_data.packed_array)->array}</DisplayString>
+ <!-- broken, will show incorrect data
<DisplayString Condition="type == Variant::PACKED_INT64_ARRAY">{*(PackedInt64Array *)_data._mem}</DisplayString>
- <DisplayString Condition="type == Variant::PACKED_FLOAT32_ARRAY">{*(PackedFloat32Array *)_data._mem}</DisplayString>
- <DisplayString Condition="type == Variant::PACKED_FLOAT64_ARRAY">{*(PackedFloat64Array *)_data._mem}</DisplayString>
- <DisplayString Condition="type == Variant::PACKED_STRING_ARRAY">{*(PackedStringArray *)_data._mem}</DisplayString>
- <DisplayString Condition="type == Variant::PACKED_VECTOR2_ARRAY">{*(PackedVector2Array *)_data._mem}</DisplayString>
- <DisplayString Condition="type == Variant::PACKED_VECTOR3_ARRAY">{*(PackedVector3Array *)_data._mem}</DisplayString>
- <DisplayString Condition="type == Variant::PACKED_COLOR_ARRAY">{*(PackedColorArray *)_data._mem}</DisplayString>
+ -->
+ <DisplayString Condition="type == Variant::PACKED_FLOAT32_ARRAY">{reinterpret_cast&lt;const Variant::PackedArrayRef&lt;float&gt;*&gt;(_data.packed_array)->array}</DisplayString>
+ <DisplayString Condition="type == Variant::PACKED_FLOAT64_ARRAY">{reinterpret_cast&lt;const Variant::PackedArrayRef&lt;double&gt;*&gt;(_data.packed_array)->array}</DisplayString>
+ <DisplayString Condition="type == Variant::PACKED_STRING_ARRAY">{reinterpret_cast&lt;const Variant::PackedArrayRef&lt;String&gt;*&gt;(_data.packed_array)->array}</DisplayString>
+ <DisplayString Condition="type == Variant::PACKED_VECTOR2_ARRAY">{reinterpret_cast&lt;const Variant::PackedArrayRef&lt;Vector2&gt;*&gt;(_data.packed_array)->array}</DisplayString>
+ <DisplayString Condition="type == Variant::PACKED_VECTOR3_ARRAY">{reinterpret_cast&lt;const Variant::PackedArrayRef&lt;Vector3&gt;*&gt;(_data.packed_array)->array}</DisplayString>
+ <DisplayString Condition="type == Variant::PACKED_COLOR_ARRAY">{reinterpret_cast&lt;const Variant::PackedArrayRef&lt;Color&gt;*&gt;(_data.packed_array)->array}</DisplayString>
<StringView Condition="type == Variant::STRING &amp;&amp; ((String *)(_data._mem))->_cowdata._ptr">((String *)(_data._mem))->_cowdata._ptr,s32</StringView>
@@ -87,7 +121,7 @@
<Item Name="[value]" Condition="type == Variant::OBJECT">*(Object *)_data._mem</Item>
<Item Name="[value]" Condition="type == Variant::DICTIONARY">*(Dictionary *)_data._mem</Item>
<Item Name="[value]" Condition="type == Variant::ARRAY">*(Array *)_data._mem</Item>
- <Item Name="[value]" Condition="type == Variant::PACKED_BYTE_ARRAY">*(PackedByteArray *)_data._mem</Item>
+ <Item Name="[value]" Condition="type == Variant::PACKED_BYTE_ARRAY">reinterpret_cast&lt;const Variant::PackedArrayRef&lt;unsigned char&gt;*&gt;(_data.packed_array)->array</Item>
<Item Name="[value]" Condition="type == Variant::PACKED_INT32_ARRAY">*(PackedInt32Array *)_data._mem</Item>
<Item Name="[value]" Condition="type == Variant::PACKED_INT64_ARRAY">*(PackedInt64Array *)_data._mem</Item>
<Item Name="[value]" Condition="type == Variant::PACKED_FLOAT32_ARRAY">*(PackedFloat32Array *)_data._mem</Item>
@@ -105,6 +139,14 @@
<StringView Condition="_cowdata._ptr != 0">_cowdata._ptr,s32</StringView>
</Type>
+ <Type Name="godot::String">
+ <DisplayString>{*reinterpret_cast&lt;void**&gt;(opaque),s32}</DisplayString>
+ <Expand>
+ <Item Name="opaque_ptr">*reinterpret_cast&lt;void**&gt;(opaque)</Item>
+ <Item Name="string">*reinterpret_cast&lt;void**&gt;(opaque),s32</Item>
+ </Expand>
+ </Type>
+
<Type Name="StringName">
<DisplayString Condition="_data &amp;&amp; _data->cname">{_data->cname}</DisplayString>
<DisplayString Condition="_data &amp;&amp; !_data->cname">{_data->name,s32}</DisplayString>
@@ -113,6 +155,22 @@
<StringView Condition="_data &amp;&amp; !_data->cname">_data->name,s32</StringView>
</Type>
+ <!-- can't cast the opaque to ::StringName because Natvis does not support global namespace specifier? -->
+ <Type Name="godot::StringName">
+ <DisplayString Condition="(*reinterpret_cast&lt;const char***&gt;(opaque))[1]">{(*reinterpret_cast&lt;const char***&gt;(opaque))[1],s8}</DisplayString>
+ <DisplayString Condition="!(*reinterpret_cast&lt;const char***&gt;(opaque))[1]">{(*reinterpret_cast&lt;const char***&gt;(opaque))[2],s32}</DisplayString>
+ <Expand>
+ <Item Name="opaque_ptr">*reinterpret_cast&lt;void**&gt;(opaque)</Item>
+ <Item Name="&amp;cname">(*reinterpret_cast&lt;const char***&gt;(opaque))+1</Item>
+ <Item Name="cname">(*reinterpret_cast&lt;const char***&gt;(opaque))[1],s8</Item>
+ </Expand>
+ </Type>
+
+ <Type Name="Object::SignalData">
+ <DisplayString Condition="user.name._cowdata._ptr">"{user.name}" {slot_map}</DisplayString>
+ <DisplayString Condition="!user.name._cowdata._ptr">"{slot_map}</DisplayString>
+ </Type>
+
<Type Name="Vector2">
<DisplayString>{{{x},{y}}}</DisplayString>
<Expand>
@@ -149,12 +207,4 @@
<Item Name="alpha">a</Item>
</Expand>
</Type>
-
- <Type Name="Node" Inheritable="false">
- <Expand>
- <Item Name="Object">(Object*)this</Item>
- <Item Name="class_name">(StringName*)(((char*)this) + sizeof(Object))</Item>
- <Item Name="data">(Node::Data*)(((char*)this) + sizeof(Object) + sizeof(StringName))</Item>
- </Expand>
- </Type>
</AutoVisualizer>
diff --git a/platform/windows/godot_windows.cpp b/platform/windows/godot_windows.cpp
index 72920d2816..13602f7cbc 100644
--- a/platform/windows/godot_windows.cpp
+++ b/platform/windows/godot_windows.cpp
@@ -87,7 +87,8 @@ CommandLineToArgvA(
i = 0;
j = 0;
- while ((a = CmdLine[i])) {
+ a = CmdLine[i];
+ while (a) {
if (in_QM) {
if (a == '\"') {
in_QM = FALSE;
@@ -130,6 +131,7 @@ CommandLineToArgvA(
}
}
i++;
+ a = CmdLine[i];
}
_argv[j] = '\0';
argv[argc] = nullptr;
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index 403d53ae53..241e0b382e 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -290,7 +290,26 @@ String OS_Windows::get_name() const {
return "Windows";
}
-OS::Date OS_Windows::get_date(bool p_utc) const {
+String OS_Windows::get_distribution_name() const {
+ return get_name();
+}
+
+String OS_Windows::get_version() const {
+ typedef LONG NTSTATUS;
+ typedef NTSTATUS(WINAPI * RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);
+ RtlGetVersionPtr version_ptr = (RtlGetVersionPtr)GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlGetVersion");
+ if (version_ptr != nullptr) {
+ RTL_OSVERSIONINFOW fow;
+ ZeroMemory(&fow, sizeof(fow));
+ fow.dwOSVersionInfoSize = sizeof(fow);
+ if (version_ptr(&fow) == 0x00000000) {
+ return vformat("%d.%d.%d", (int64_t)fow.dwMajorVersion, (int64_t)fow.dwMinorVersion, (int64_t)fow.dwBuildNumber);
+ }
+ }
+ return "";
+}
+
+OS::DateTime OS_Windows::get_datetime(bool p_utc) const {
SYSTEMTIME systemtime;
if (p_utc) {
GetSystemTime(&systemtime);
@@ -305,28 +324,16 @@ OS::Date OS_Windows::get_date(bool p_utc) const {
daylight = true;
}
- Date date;
- date.day = systemtime.wDay;
- date.month = Month(systemtime.wMonth);
- date.weekday = Weekday(systemtime.wDayOfWeek);
- date.year = systemtime.wYear;
- date.dst = daylight;
- return date;
-}
-
-OS::Time OS_Windows::get_time(bool p_utc) const {
- SYSTEMTIME systemtime;
- if (p_utc) {
- GetSystemTime(&systemtime);
- } else {
- GetLocalTime(&systemtime);
- }
-
- Time time;
- time.hour = systemtime.wHour;
- time.minute = systemtime.wMinute;
- time.second = systemtime.wSecond;
- return time;
+ DateTime dt;
+ dt.year = systemtime.wYear;
+ dt.month = Month(systemtime.wMonth);
+ dt.day = systemtime.wDay;
+ dt.weekday = Weekday(systemtime.wDayOfWeek);
+ dt.hour = systemtime.wHour;
+ dt.minute = systemtime.wMinute;
+ dt.second = systemtime.wSecond;
+ dt.dst = daylight;
+ return dt;
}
OS::TimeZoneInfo OS_Windows::get_time_zone_info() const {
@@ -864,17 +871,6 @@ BOOL is_wow64() {
return wow64;
}
-int OS_Windows::get_processor_count() const {
- SYSTEM_INFO sysinfo;
- if (is_wow64()) {
- GetNativeSystemInfo(&sysinfo);
- } else {
- GetSystemInfo(&sysinfo);
- }
-
- return sysinfo.dwNumberOfProcessors;
-}
-
String OS_Windows::get_processor_name() const {
const String id = "Hardware\\Description\\System\\CentralProcessor\\0";
@@ -1146,6 +1142,21 @@ OS_Windows::OS_Windows(HINSTANCE _hInstance) {
DisplayServerWindows::register_windows_driver();
+ // Enable ANSI escape code support on Windows 10 v1607 (Anniversary Update) and later.
+ // This lets the engine and projects use ANSI escape codes to color text just like on macOS and Linux.
+ //
+ // NOTE: The engine does not use ANSI escape codes to color error/warning messages; it uses Windows API calls instead.
+ // Therefore, error/warning messages are still colored on Windows versions older than 10.
+ HANDLE stdoutHandle;
+ stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE);
+ DWORD outMode = 0;
+ outMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
+
+ if (!SetConsoleMode(stdoutHandle, outMode)) {
+ // Windows 8.1 or below, or Windows 10 prior to Anniversary Update.
+ print_verbose("Can't set the ENABLE_VIRTUAL_TERMINAL_PROCESSING Windows console mode. `print_rich()` will not work as expected.");
+ }
+
Vector<Logger *> loggers;
loggers.push_back(memnew(WindowsTerminalLogger));
_set_logger(memnew(CompositeLogger(loggers)));
diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h
index 3e054c068c..b792f6fa44 100644
--- a/platform/windows/os_windows.h
+++ b/platform/windows/os_windows.h
@@ -40,6 +40,7 @@
#include "drivers/winmidi/midi_driver_winmidi.h"
#include "key_mapping_windows.h"
#include "servers/audio_server.h"
+
#ifdef XAUDIO2_ENABLED
#include "drivers/xaudio2/audio_driver_xaudio2.h"
#endif
@@ -49,10 +50,10 @@
#include "platform/windows/vulkan_context_win.h"
#endif
-#include <fcntl.h>
#include <io.h>
#include <shellapi.h>
#include <stdio.h>
+
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <windowsx.h>
@@ -143,11 +144,12 @@ public:
virtual MainLoop *get_main_loop() const override;
virtual String get_name() const override;
+ virtual String get_distribution_name() const override;
+ virtual String get_version() const override;
virtual void initialize_joypads() override {}
- virtual Date get_date(bool p_utc) const override;
- virtual Time get_time(bool p_utc) const override;
+ virtual DateTime get_datetime(bool p_utc) const override;
virtual TimeZoneInfo get_time_zone_info() const override;
virtual double get_unix_time() const override;
@@ -173,7 +175,6 @@ public:
virtual String get_locale() const override;
- virtual int get_processor_count() const override;
virtual String get_processor_name() const override;
virtual uint64_t get_embedded_pck_offset() const override;