summaryrefslogtreecommitdiff
path: root/platform
diff options
context:
space:
mode:
Diffstat (limited to 'platform')
-rw-r--r--platform/android/README.md2
-rw-r--r--platform/android/android_input_handler.cpp40
-rw-r--r--platform/android/android_input_handler.h12
-rw-r--r--platform/android/display_server_android.cpp17
-rw-r--r--platform/android/display_server_android.h2
-rw-r--r--platform/android/export/export_plugin.cpp25
-rw-r--r--platform/android/java_godot_lib_jni.cpp10
-rw-r--r--platform/android/logo.pngbin968 -> 0 bytes
-rw-r--r--platform/android/logo.svg1
-rw-r--r--platform/android/run_icon.pngbin324 -> 0 bytes
-rw-r--r--platform/android/run_icon.svg1
-rw-r--r--platform/ios/README.md2
-rw-r--r--platform/ios/device_metrics.m242
-rw-r--r--platform/ios/display_server_ios.mm25
-rw-r--r--platform/ios/export/export_plugin.cpp39
-rw-r--r--platform/ios/export/export_plugin.h1
-rw-r--r--platform/ios/logo.pngbin1297 -> 0 bytes
-rw-r--r--platform/ios/logo.svg1
-rw-r--r--platform/linuxbsd/README.md2
-rw-r--r--platform/linuxbsd/crash_handler_linuxbsd.cpp40
-rw-r--r--platform/linuxbsd/detect.py11
-rw-r--r--platform/linuxbsd/export/export.cpp1
-rw-r--r--platform/linuxbsd/export/export_plugin.cpp344
-rw-r--r--platform/linuxbsd/export/export_plugin.h45
-rw-r--r--platform/linuxbsd/joypad_linux.cpp24
-rw-r--r--platform/linuxbsd/joypad_linux.h2
-rw-r--r--platform/linuxbsd/logo.pngbin1679 -> 0 bytes
-rw-r--r--platform/linuxbsd/logo.svg1
-rw-r--r--platform/linuxbsd/run_icon.svg1
-rw-r--r--platform/linuxbsd/x11/display_server_x11.cpp84
-rw-r--r--platform/linuxbsd/x11/display_server_x11.h10
-rw-r--r--platform/macos/README.md2
-rw-r--r--platform/macos/display_server_macos.h7
-rw-r--r--platform/macos/display_server_macos.mm58
-rw-r--r--platform/macos/export/export_plugin.cpp388
-rw-r--r--platform/macos/export/export_plugin.h59
-rw-r--r--platform/macos/godot_content_view.h2
-rw-r--r--platform/macos/godot_content_view.mm41
-rw-r--r--platform/macos/joypad_macos.cpp27
-rw-r--r--platform/macos/logo.pngbin7195 -> 0 bytes
-rw-r--r--platform/macos/logo.svg1
-rw-r--r--platform/macos/run_icon.svg1
-rw-r--r--platform/uwp/README.md2
-rw-r--r--platform/uwp/export/export_plugin.cpp18
-rw-r--r--platform/uwp/logo.pngbin1519 -> 0 bytes
-rw-r--r--platform/uwp/logo.svg1
-rw-r--r--platform/uwp/os_uwp.cpp2
-rw-r--r--platform/uwp/os_uwp.h2
-rw-r--r--platform/web/README.md2
-rw-r--r--platform/web/api/api.cpp3
-rw-r--r--platform/web/api/javascript_bridge_singleton.h1
-rw-r--r--platform/web/display_server_web.cpp28
-rw-r--r--platform/web/export/export_plugin.cpp25
-rw-r--r--platform/web/export/export_plugin.h5
-rw-r--r--platform/web/javascript_bridge_singleton.cpp5
-rw-r--r--platform/web/logo.pngbin1234 -> 0 bytes
-rw-r--r--platform/web/logo.svg1
-rw-r--r--platform/web/os_web.cpp6
-rw-r--r--platform/web/os_web.h1
-rw-r--r--platform/web/package-lock.json12
-rw-r--r--platform/web/run_icon.pngbin290 -> 0 bytes
-rw-r--r--platform/web/run_icon.svg1
-rw-r--r--platform/windows/README.md2
-rw-r--r--platform/windows/display_server_windows.cpp80
-rw-r--r--platform/windows/display_server_windows.h7
-rw-r--r--platform/windows/export/export.cpp1
-rw-r--r--platform/windows/export/export_plugin.cpp316
-rw-r--r--platform/windows/export/export_plugin.h36
-rw-r--r--platform/windows/joypad_windows.cpp25
-rw-r--r--platform/windows/logo.pngbin1536 -> 0 bytes
-rw-r--r--platform/windows/logo.svg1
-rw-r--r--platform/windows/os_windows.cpp45
-rw-r--r--platform/windows/os_windows.h5
-rw-r--r--platform/windows/run_icon.svg1
74 files changed, 1670 insertions, 535 deletions
diff --git a/platform/android/README.md b/platform/android/README.md
index f6aabab708..01eb9c03a6 100644
--- a/platform/android/README.md
+++ b/platform/android/README.md
@@ -5,7 +5,7 @@ using [Gradle](https://gradle.org/) as a build system.
## Documentation
-- [Compiling for Android](https://docs.godotengine.org/en/latest/development/compiling/compiling_for_android.html)
+- [Compiling for Android](https://docs.godotengine.org/en/latest/contributing/development/compiling/compiling_for_android.html)
- Instructions on building this platform port from source.
- [Exporting for Android](https://docs.godotengine.org/en/latest/tutorials/export/exporting_for_android.html)
- Instructions on using the compiled export templates to export a project.
diff --git a/platform/android/android_input_handler.cpp b/platform/android/android_input_handler.cpp
index cfa1046bb3..e3f93086dc 100644
--- a/platform/android/android_input_handler.cpp
+++ b/platform/android/android_input_handler.cpp
@@ -225,7 +225,7 @@ void AndroidInputHandler::process_touch_event(int p_event, int p_pointer, const
}
}
-void AndroidInputHandler::_parse_mouse_event_info(MouseButton event_buttons_mask, bool p_pressed, bool p_double_click, bool p_source_mouse_relative) {
+void AndroidInputHandler::_parse_mouse_event_info(BitField<MouseButtonMask> event_buttons_mask, bool p_pressed, bool p_double_click, bool p_source_mouse_relative) {
if (!mouse_event_info.valid) {
return;
}
@@ -242,7 +242,7 @@ void AndroidInputHandler::_parse_mouse_event_info(MouseButton event_buttons_mask
hover_prev_pos = mouse_event_info.pos;
}
ev->set_pressed(p_pressed);
- MouseButton changed_button_mask = MouseButton(buttons_state ^ event_buttons_mask);
+ BitField<MouseButtonMask> changed_button_mask = BitField<MouseButtonMask>(buttons_state.operator int64_t() ^ event_buttons_mask.operator int64_t());
buttons_state = event_buttons_mask;
@@ -253,12 +253,12 @@ void AndroidInputHandler::_parse_mouse_event_info(MouseButton event_buttons_mask
}
void AndroidInputHandler::_release_mouse_event_info(bool p_source_mouse_relative) {
- _parse_mouse_event_info(MouseButton::NONE, false, false, p_source_mouse_relative);
+ _parse_mouse_event_info(BitField<MouseButtonMask>(), 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);
+ BitField<MouseButtonMask> 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
@@ -342,11 +342,11 @@ void AndroidInputHandler::process_mouse_event(int p_event_action, int p_event_an
}
}
-void AndroidInputHandler::_wheel_button_click(MouseButton event_buttons_mask, const Ref<InputEventMouseButton> &ev, MouseButton wheel_button, float factor) {
+void AndroidInputHandler::_wheel_button_click(BitField<MouseButtonMask> event_buttons_mask, const Ref<InputEventMouseButton> &ev, MouseButton wheel_button, float factor) {
Ref<InputEventMouseButton> evd = ev->duplicate();
_set_key_modifier_state(evd);
evd->set_button_index(wheel_button);
- evd->set_button_mask(MouseButton(event_buttons_mask ^ mouse_button_to_mask(wheel_button)));
+ evd->set_button_mask(BitField<MouseButtonMask>(event_buttons_mask.operator int64_t() ^ int64_t(mouse_button_to_mask(wheel_button))));
evd->set_factor(factor);
Input::get_singleton()->parse_input_event(evd);
Ref<InputEventMouseButton> evdd = evd->duplicate();
@@ -373,39 +373,39 @@ void AndroidInputHandler::process_pan(Point2 p_pos, Vector2 p_delta) {
Input::get_singleton()->parse_input_event(pan_event);
}
-MouseButton AndroidInputHandler::_button_index_from_mask(MouseButton button_mask) {
- switch (button_mask) {
- case MouseButton::MASK_LEFT:
+MouseButton AndroidInputHandler::_button_index_from_mask(BitField<MouseButtonMask> button_mask) {
+ switch (MouseButtonMask(button_mask.operator int64_t())) {
+ case MouseButtonMask::LEFT:
return MouseButton::LEFT;
- case MouseButton::MASK_RIGHT:
+ case MouseButtonMask::RIGHT:
return MouseButton::RIGHT;
- case MouseButton::MASK_MIDDLE:
+ case MouseButtonMask::MIDDLE:
return MouseButton::MIDDLE;
- case MouseButton::MASK_XBUTTON1:
+ case MouseButtonMask::MB_XBUTTON1:
return MouseButton::MB_XBUTTON1;
- case MouseButton::MASK_XBUTTON2:
+ case MouseButtonMask::MB_XBUTTON2:
return MouseButton::MB_XBUTTON2;
default:
return MouseButton::NONE;
}
}
-MouseButton AndroidInputHandler::_android_button_mask_to_godot_button_mask(int android_button_mask) {
- MouseButton godot_button_mask = MouseButton::NONE;
+BitField<MouseButtonMask> AndroidInputHandler::_android_button_mask_to_godot_button_mask(int android_button_mask) {
+ BitField<MouseButtonMask> godot_button_mask;
if (android_button_mask & AMOTION_EVENT_BUTTON_PRIMARY) {
- godot_button_mask |= MouseButton::MASK_LEFT;
+ godot_button_mask.set_flag(MouseButtonMask::LEFT);
}
if (android_button_mask & AMOTION_EVENT_BUTTON_SECONDARY) {
- godot_button_mask |= MouseButton::MASK_RIGHT;
+ godot_button_mask.set_flag(MouseButtonMask::RIGHT);
}
if (android_button_mask & AMOTION_EVENT_BUTTON_TERTIARY) {
- godot_button_mask |= MouseButton::MASK_MIDDLE;
+ godot_button_mask.set_flag(MouseButtonMask::MIDDLE);
}
if (android_button_mask & AMOTION_EVENT_BUTTON_BACK) {
- godot_button_mask |= MouseButton::MASK_XBUTTON1;
+ godot_button_mask.set_flag(MouseButtonMask::MB_XBUTTON1);
}
if (android_button_mask & AMOTION_EVENT_BUTTON_FORWARD) {
- godot_button_mask |= MouseButton::MASK_XBUTTON2;
+ godot_button_mask.set_flag(MouseButtonMask::MB_XBUTTON2);
}
return godot_button_mask;
diff --git a/platform/android/android_input_handler.h b/platform/android/android_input_handler.h
index 1b89096057..34259efd81 100644
--- a/platform/android/android_input_handler.h
+++ b/platform/android/android_input_handler.h
@@ -61,7 +61,7 @@ public:
int index = 0; // Can be either JoyAxis or JoyButton.
bool pressed = false;
float value = 0;
- HatMask hat = HatMask::CENTER;
+ BitField<HatMask> hat;
};
private:
@@ -70,7 +70,7 @@ private:
bool control_mem = false;
bool meta_mem = false;
- MouseButton buttons_state = MouseButton::NONE;
+ BitField<MouseButtonMask> buttons_state;
Vector<TouchPos> touch;
MouseEventInfo mouse_event_info;
@@ -78,12 +78,12 @@ private:
void _set_key_modifier_state(Ref<InputEventWithModifiers> ev);
- static MouseButton _button_index_from_mask(MouseButton button_mask);
- static MouseButton _android_button_mask_to_godot_button_mask(int android_button_mask);
+ static MouseButton _button_index_from_mask(BitField<MouseButtonMask> button_mask);
+ static BitField<MouseButtonMask> _android_button_mask_to_godot_button_mask(int android_button_mask);
- void _wheel_button_click(MouseButton event_buttons_mask, const Ref<InputEventMouseButton> &ev, MouseButton wheel_button, float factor);
+ void _wheel_button_click(BitField<MouseButtonMask> 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 _parse_mouse_event_info(BitField<MouseButtonMask> 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);
diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp
index 70370764e2..6b3bdb7fe6 100644
--- a/platform/android/display_server_android.cpp
+++ b/platform/android/display_server_android.cpp
@@ -466,14 +466,15 @@ Vector<String> DisplayServerAndroid::get_rendering_drivers_func() {
DisplayServer *DisplayServerAndroid::create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error) {
DisplayServer *ds = memnew(DisplayServerAndroid(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, r_error));
if (r_error != OK) {
- OS::get_singleton()->alert("Your video card driver does not support any of the supported Vulkan versions.", "Unable to initialize Video driver");
if (p_rendering_driver == "vulkan") {
- OS::get_singleton()->alert("Your video card driver does not support the selected Vulkan version.\n"
- "Please try exporting your game using the gl_compatibility renderer.",
- "Unable to initialize Video driver");
+ OS::get_singleton()->alert(
+ "Your device seems not to support the required Vulkan version.\n\n"
+ "Please try exporting your game using the 'gl_compatibility' renderer.",
+ "Unable to initialize Vulkan video driver");
} else {
- OS::get_singleton()->alert("Your video card driver does not support OpenGL ES 3.0.",
- "Unable to initialize Video driver");
+ OS::get_singleton()->alert(
+ "Your device seems not to support the required OpenGL ES 3.0 version.",
+ "Unable to initialize OpenGL video driver");
}
}
return ds;
@@ -624,8 +625,8 @@ Point2i DisplayServerAndroid::mouse_get_position() const {
return Input::get_singleton()->get_mouse_position();
}
-MouseButton DisplayServerAndroid::mouse_get_button_state() const {
- return (MouseButton)Input::get_singleton()->get_mouse_button_mask();
+BitField<MouseButtonMask> DisplayServerAndroid::mouse_get_button_state() const {
+ return Input::get_singleton()->get_mouse_button_mask();
}
void DisplayServerAndroid::_cursor_set_shape_helper(CursorShape p_shape, bool force) {
diff --git a/platform/android/display_server_android.h b/platform/android/display_server_android.h
index faaa3dbd9c..b2400d81dc 100644
--- a/platform/android/display_server_android.h
+++ b/platform/android/display_server_android.h
@@ -207,7 +207,7 @@ public:
void notify_surface_changed(int p_width, int p_height);
virtual Point2i mouse_get_position() const override;
- virtual MouseButton mouse_get_button_state() const override;
+ virtual BitField<MouseButtonMask> mouse_get_button_state() const override;
void reset_swap_buffers_flag();
bool should_swap_buffers() const;
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index 0bb7b57a35..bb1ad3d83b 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -43,10 +43,16 @@
#include "editor/editor_log.h"
#include "editor/editor_node.h"
#include "editor/editor_paths.h"
+#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#include "main/splash.gen.h"
-#include "platform/android/logo.gen.h"
-#include "platform/android/run_icon.gen.h"
+#include "platform/android/logo_svg.gen.h"
+#include "platform/android/run_icon_svg.gen.h"
+
+#include "modules/modules_enabled.gen.h" // For svg.
+#ifdef MODULE_SVG_ENABLED
+#include "modules/svg/image_loader_svg.h"
+#endif
#include <string.h>
@@ -1025,7 +1031,7 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
string_table.write[attr_value] = "com.oculus.handtracking.version";
}
- if (tname == "meta-data" && attrname == "name" && value == "xr_hand_tracking_version_value") {
+ if (tname == "meta-data" && attrname == "value" && value == "xr_hand_tracking_version_value") {
string_table.write[attr_value] = "V2.0";
}
}
@@ -3238,8 +3244,17 @@ void EditorExportPlatformAndroid::resolve_platform_feature_priorities(const Ref<
}
EditorExportPlatformAndroid::EditorExportPlatformAndroid() {
- logo = ImageTexture::create_from_image(memnew(Image(_android_logo)));
- run_icon = ImageTexture::create_from_image(memnew(Image(_android_run_icon)));
+#ifdef MODULE_SVG_ENABLED
+ Ref<Image> img = memnew(Image);
+ const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE);
+
+ ImageLoaderSVG img_loader;
+ img_loader.create_image_from_string(img, _android_logo_svg, EDSCALE, upsample, false);
+ logo = ImageTexture::create_from_image(img);
+
+ img_loader.create_image_from_string(img, _android_run_icon_svg, EDSCALE, upsample, false);
+ run_icon = ImageTexture::create_from_image(img);
+#endif
devices_changed.set();
plugins_changed.set();
diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp
index b502e587a7..e6cdd7932a 100644
--- a/platform/android/java_godot_lib_jni.cpp
+++ b/platform/android/java_godot_lib_jni.cpp
@@ -353,19 +353,19 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, j
AndroidInputHandler::JoypadEvent jevent;
jevent.device = p_device;
jevent.type = AndroidInputHandler::JOY_EVENT_HAT;
- HatMask hat = HatMask::CENTER;
+ BitField<HatMask> hat;
if (p_hat_x != 0) {
if (p_hat_x < 0) {
- hat |= HatMask::LEFT;
+ hat.set_flag(HatMask::LEFT);
} else {
- hat |= HatMask::RIGHT;
+ hat.set_flag(HatMask::RIGHT);
}
}
if (p_hat_y != 0) {
if (p_hat_y < 0) {
- hat |= HatMask::UP;
+ hat.set_flag(HatMask::UP);
} else {
- hat |= HatMask::DOWN;
+ hat.set_flag(HatMask::DOWN);
}
}
jevent.hat = hat;
diff --git a/platform/android/logo.png b/platform/android/logo.png
deleted file mode 100644
index 9c8be93646..0000000000
--- a/platform/android/logo.png
+++ /dev/null
Binary files differ
diff --git a/platform/android/logo.svg b/platform/android/logo.svg
new file mode 100644
index 0000000000..f154e55d11
--- /dev/null
+++ b/platform/android/logo.svg
@@ -0,0 +1 @@
+<svg height="32" width="32" xmlns="http://www.w3.org/2000/svg"><path d="M22.904 20.192a1.25 1.25 0 1 1 1.25-1.25 1.25 1.25 0 0 1-1.25 1.25m-13.808 0a1.25 1.25 0 1 1 1.25-1.25 1.25 1.25 0 0 1-1.25 1.25m14.256-7.525 2.496-4.323a.52.52 0 1 0-.899-.52l-2.528 4.378a15.69 15.69 0 0 0-12.842 0L7.051 7.823a.52.52 0 1 0-.9.521l2.497 4.323C4.361 15 1.43 19.34 1 24.467h30c-.43-5.128-3.361-9.468-7.648-11.8" fill="#56d881"/></svg>
diff --git a/platform/android/run_icon.png b/platform/android/run_icon.png
deleted file mode 100644
index b687c9ac31..0000000000
--- a/platform/android/run_icon.png
+++ /dev/null
Binary files differ
diff --git a/platform/android/run_icon.svg b/platform/android/run_icon.svg
new file mode 100644
index 0000000000..24d930fece
--- /dev/null
+++ b/platform/android/run_icon.svg
@@ -0,0 +1 @@
+<svg height="16" width="16" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="M11.187 9.936a.577.577 0 1 1 .578-.578.577.577 0 0 1-.578.578m-6.374 0a.577.577 0 1 1 .577-.578.577.577 0 0 1-.577.578m6.581-3.475 1.153-1.996a.24.24 0 1 0-.415-.24l-1.167 2.021a7.244 7.244 0 0 0-5.93 0L3.868 4.225a.24.24 0 1 0-.415.24l1.153 1.996a6.806 6.806 0 0 0-3.532 5.448h13.852a6.807 6.807 0 0 0-3.532-5.448" fill="#56d881" style="fill:#e0e0e0;fill-opacity:1"/></svg>
diff --git a/platform/ios/README.md b/platform/ios/README.md
index 82c275ad31..0e1d6802cb 100644
--- a/platform/ios/README.md
+++ b/platform/ios/README.md
@@ -8,7 +8,7 @@ project template used for packaging the iOS export templates.
## Documentation
-- [Compiling for iOS](https://docs.godotengine.org/en/latest/development/compiling/compiling_for_ios.html)
+- [Compiling for iOS](https://docs.godotengine.org/en/latest/contributing/development/compiling/compiling_for_ios.html)
- Instructions on building this platform port from source.
- [Exporting for iOS](https://docs.godotengine.org/en/latest/tutorials/export/exporting_for_ios.html)
- Instructions on using the compiled export templates to export a project.
diff --git a/platform/ios/device_metrics.m b/platform/ios/device_metrics.m
index b51f45c602..d6f5ec8e42 100644
--- a/platform/ios/device_metrics.m
+++ b/platform/ios/device_metrics.m
@@ -35,117 +35,159 @@
+ (NSDictionary *)dpiList {
return @{
@[
- @"iPad1,1",
- @"iPad2,1",
- @"iPad2,2",
- @"iPad2,3",
- @"iPad2,4",
+ @"iPad1,1", // iPad 1th Gen
+ @"iPad1,2", // iPad 1th Gen (3G)
+ @"iPad2,1", // iPad 2nd Gen
+ @"iPad2,2", // iPad 2nd Gen (GSM)
+ @"iPad2,3", // iPad 2nd Gen (CDMA)
+ @"iPad2,4", // iPad 2nd Gen
] : @132,
@[
- @"iPhone1,1",
- @"iPhone1,2",
- @"iPhone2,1",
- @"iPad2,5",
- @"iPad2,6",
- @"iPad2,7",
- @"iPod1,1",
- @"iPod2,1",
- @"iPod3,1",
+ @"iPhone1,1", // iPhone 1st Gen
+ @"iPhone1,2", // iPhone 3G
+ @"iPhone2,1", // iPhone 3GS
+ @"iPad2,5", // iPad mini
+ @"iPad2,6", // iPad mini (GSM+LTE)
+ @"iPad2,7", // iPad mini (CDMA+LTE)
+ @"iPod1,1", // iPod 1st Gen
+ @"iPod2,1", // iPod 2nd Gen
+ @"iPod3,1", // iPod 3rd Gen
] : @163,
@[
- @"iPad3,1",
- @"iPad3,2",
- @"iPad3,3",
- @"iPad3,4",
- @"iPad3,5",
- @"iPad3,6",
- @"iPad4,1",
- @"iPad4,2",
- @"iPad4,3",
- @"iPad5,3",
- @"iPad5,4",
- @"iPad6,3",
- @"iPad6,4",
- @"iPad6,7",
- @"iPad6,8",
- @"iPad6,11",
- @"iPad6,12",
- @"iPad7,1",
- @"iPad7,2",
- @"iPad7,3",
- @"iPad7,4",
- @"iPad7,5",
- @"iPad7,6",
- @"iPad7,11",
- @"iPad7,12",
- @"iPad8,1",
- @"iPad8,2",
- @"iPad8,3",
- @"iPad8,4",
- @"iPad8,5",
- @"iPad8,6",
- @"iPad8,7",
- @"iPad8,8",
- @"iPad8,9",
- @"iPad8,10",
- @"iPad8,11",
- @"iPad8,12",
- @"iPad11,3",
- @"iPad11,4",
+ @"iPad3,1", // iPad 3rd Gen
+ @"iPad3,2", // iPad 3rd Gen (CDMA)
+ @"iPad3,3", // iPad 3rd Gen (GSM)
+ @"iPad3,4", // iPad 4th Gen
+ @"iPad3,5", // iPad 4th Gen (GSM+LTE)
+ @"iPad3,6", // iPad 4th Gen (CDMA+LTE)
+ @"iPad4,1", // iPad Air (WiFi)
+ @"iPad4,2", // iPad Air (GSM+CDMA)
+ @"iPad4,3", // iPad Air (China)
+ @"iPad4,7", // iPad mini 3 (WiFi)
+ @"iPad4,8", // iPad mini 3 (GSM+CDMA)
+ @"iPad4,9", // iPad Mini 3 (China)
+ @"iPad6,3", // iPad Pro (9.7 inch, WiFi)
+ @"iPad6,4", // iPad Pro (9.7 inch, WiFi+LTE)
+ @"iPad6,7", // iPad Pro (12.9 inch, WiFi)
+ @"iPad6,8", // iPad Pro (12.9 inch, WiFi+LTE)
+ @"iPad6,11", // iPad 5th Gen (2017)
+ @"iPad6,12", // iPad 5th Gen (2017)
+ @"iPad7,1", // iPad Pro 2nd Gen (WiFi)
+ @"iPad7,2", // iPad Pro 2nd Gen (WiFi+Cellular)
+ @"iPad7,3", // iPad Pro 10.5-inch 2nd Gen
+ @"iPad7,4", // iPad Pro 10.5-inch 2nd Gen
+ @"iPad7,5", // iPad 6th Gen (WiFi)
+ @"iPad7,6", // iPad 6th Gen (WiFi+Cellular)
+ @"iPad7,11", // iPad 7th Gen 10.2-inch (WiFi)
+ @"iPad7,12", // iPad 7th Gen 10.2-inch (WiFi+Cellular)
+ @"iPad8,1", // iPad Pro 11 inch 3rd Gen (WiFi)
+ @"iPad8,2", // iPad Pro 11 inch 3rd Gen (1TB, WiFi)
+ @"iPad8,3", // iPad Pro 11 inch 3rd Gen (WiFi+Cellular)
+ @"iPad8,4", // iPad Pro 11 inch 3rd Gen (1TB, WiFi+Cellular)
+ @"iPad8,5", // iPad Pro 12.9 inch 3rd Gen (WiFi)
+ @"iPad8,6", // iPad Pro 12.9 inch 3rd Gen (1TB, WiFi)
+ @"iPad8,7", // iPad Pro 12.9 inch 3rd Gen (WiFi+Cellular)
+ @"iPad8,8", // iPad Pro 12.9 inch 3rd Gen (1TB, WiFi+Cellular)
+ @"iPad8,9", // iPad Pro 11 inch 4th Gen (WiFi)
+ @"iPad8,10", // iPad Pro 11 inch 4th Gen (WiFi+Cellular)
+ @"iPad8,11", // iPad Pro 12.9 inch 4th Gen (WiFi)
+ @"iPad8,12", // iPad Pro 12.9 inch 4th Gen (WiFi+Cellular)
+ @"iPad11,3", // iPad Air 3rd Gen (WiFi)
+ @"iPad11,4", // iPad Air 3rd Gen
+ @"iPad11,6", // iPad 8th Gen (WiFi)
+ @"iPad11,7", // iPad 8th Gen (WiFi+Cellular)
+ @"iPad12,1", // iPad 9th Gen (WiFi)
+ @"iPad12,2", // iPad 9th Gen (WiFi+Cellular)
+ @"iPad13,1", // iPad Air 4th Gen (WiFi)
+ @"iPad13,2", // iPad Air 4th Gen (WiFi+Cellular)
+ @"iPad13,4", // iPad Pro 11 inch 5th Gen
+ @"iPad13,5", // iPad Pro 11 inch 5th Gen
+ @"iPad13,6", // iPad Pro 11 inch 5th Gen
+ @"iPad13,7", // iPad Pro 11 inch 5th Gen
+ @"iPad13,8", // iPad Pro 12.9 inch 5th Gen
+ @"iPad13,9", // iPad Pro 12.9 inch 5th Gen
+ @"iPad13,10", // iPad Pro 12.9 inch 5th Gen
+ @"iPad13,11", // iPad Pro 12.9 inch 5th Gen
+ @"iPad13,16", // iPad Air 5th Gen (WiFi)
+ @"iPad13,17", // iPad Air 5th Gen (WiFi+Cellular)
+ @"iPad13,18", // iPad 10th Gen
+ @"iPad13,19", // iPad 10th Gen
+ @"iPad14,3", // iPad Pro 11 inch 6th Gen
+ @"iPad14,4", // iPad Pro 11 inch 6th Gen
+ @"iPad14,5", // iPad Pro 12.9 inch 6th Gen
+ @"iPad14,6", // iPad Pro 12.9 inch 6th Gen
] : @264,
@[
- @"iPhone3,1",
- @"iPhone3,2",
- @"iPhone3,3",
- @"iPhone4,1",
- @"iPhone5,1",
- @"iPhone5,2",
- @"iPhone5,3",
- @"iPhone5,4",
- @"iPhone6,1",
- @"iPhone6,2",
- @"iPhone7,2",
- @"iPhone8,1",
- @"iPhone8,4",
- @"iPhone9,1",
- @"iPhone9,3",
- @"iPhone10,1",
- @"iPhone10,4",
- @"iPhone11,8",
- @"iPhone12,1",
- @"iPhone12,8",
- @"iPad4,4",
- @"iPad4,5",
- @"iPad4,6",
- @"iPad4,7",
- @"iPad4,8",
- @"iPad4,9",
- @"iPad5,1",
- @"iPad5,2",
- @"iPad11,1",
- @"iPad11,2",
- @"iPod4,1",
- @"iPod5,1",
- @"iPod7,1",
- @"iPod9,1",
+ @"iPhone3,1", // iPhone 4
+ @"iPhone3,2", // iPhone 4 (GSM)
+ @"iPhone3,3", // iPhone 4 (CDMA)
+ @"iPhone4,1", // iPhone 4S
+ @"iPhone5,1", // iPhone 5 (GSM)
+ @"iPhone5,2", // iPhone 5 (GSM+CDMA)
+ @"iPhone5,3", // iPhone 5C (GSM)
+ @"iPhone5,4", // iPhone 5C (Global)
+ @"iPhone6,1", // iPhone 5S (GSM)
+ @"iPhone6,2", // iPhone 5S (Global)
+ @"iPhone7,2", // iPhone 6
+ @"iPhone8,1", // iPhone 6s
+ @"iPhone8,4", // iPhone SE (GSM)
+ @"iPhone9,1", // iPhone 7
+ @"iPhone9,3", // iPhone 7
+ @"iPhone10,1", // iPhone 8
+ @"iPhone10,4", // iPhone 8
+ @"iPhone11,8", // iPhone XR
+ @"iPhone12,1", // iPhone 11
+ @"iPhone12,8", // iPhone SE 2nd gen
+ @"iPhone14,6", // iPhone SE 3rd gen
+ @"iPad4,4", // iPad mini Retina (WiFi)
+ @"iPad4,5", // iPad mini Retina (GSM+CDMA)
+ @"iPad4,6", // iPad mini Retina (China)
+ @"iPad5,1", // iPad mini 4th Gen (WiFi)
+ @"iPad5,2", // iPad mini 4th Gen
+ @"iPad5,3", // iPad Air 2 (WiFi)
+ @"iPad5,4", // iPad Air 2
+ @"iPad11,1", // iPad mini 5th Gen (WiFi)
+ @"iPad11,2", // iPad mini 5th Gen
+ @"iPad14,1", // iPad mini 6th Gen (WiFi)
+ @"iPad14,2", // iPad mini 6th Gen
+ @"iPod4,1", // iPod 4th Gen
+ @"iPod5,1", // iPod 5th Gen
+ @"iPod7,1", // iPod 6th Gen
+ @"iPod9,1", // iPod 7th Gen
] : @326,
@[
- @"iPhone7,1",
- @"iPhone8,2",
- @"iPhone9,2",
- @"iPhone9,4",
- @"iPhone10,2",
- @"iPhone10,5",
+ @"iPhone7,1", // iPhone 6 Plus
+ @"iPhone8,2", // iPhone 6s Plus
+ @"iPhone9,2", // iPhone 7 Plus
+ @"iPhone9,4", // iPhone 7 Plus
+ @"iPhone10,2", // iPhone 8 Plus
+ @"iPhone10,5", // iPhone 8 Plus
] : @401,
@[
- @"iPhone10,3",
- @"iPhone10,6",
- @"iPhone11,2",
- @"iPhone11,4",
- @"iPhone11,6",
- @"iPhone12,3",
- @"iPhone12,5",
+ @"iPhone10,3", // iPhone X Global
+ @"iPhone10,6", // iPhone X GSM
+ @"iPhone11,2", // iPhone XS
+ @"iPhone11,4", // iPhone XS Max
+ @"iPhone11,6", // iPhone XS Max Global
+ @"iPhone12,3", // iPhone 11 Pro
+ @"iPhone12,5", // iPhone 11 Pro Max
+ @"iPhone13,4", // iPhone 12 Pro Max
+ @"iPhone14,3", // iPhone 13 Pro Max
+ @"iPhone14,8", // iPhone 14 Plus
] : @458,
+ @[
+ @"iPhone13,2", // iPhone 12
+ @"iPhone13,3", // iPhone 12 Pro
+ @"iPhone14,2", // iPhone 13 Pro
+ @"iPhone14,5", // iPhone 13
+ @"iPhone14,7", // iPhone 14
+ @"iPhone15,2", // iPhone 14 Pro
+ @"iPhone15,3", // iPhone 14 Pro Max
+ ] : @460,
+ @[
+ @"iPhone13,1", // iPhone 12 Mini
+ @"iPhone14,4", // iPhone 13 Mini
+ ] : @476
};
}
diff --git a/platform/ios/display_server_ios.mm b/platform/ios/display_server_ios.mm
index bea88a7f9b..86ea602a98 100644
--- a/platform/ios/display_server_ios.mm
+++ b/platform/ios/display_server_ios.mm
@@ -56,16 +56,9 @@ DisplayServerIOS::DisplayServerIOS(const String &p_rendering_driver, WindowMode
tts = [[TTS_IOS alloc] init];
#if defined(GLES3_ENABLED)
- // FIXME: Add support for both OpenGL and Vulkan when OpenGL is implemented
- // again,
- // 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 == "opengl3") {
bool gl_initialization_error = false;
- // FIXME: Add Vulkan support via MoltenVK. Add fallback code back?
-
if (RasterizerGLES3::is_viable() == OK) {
RasterizerGLES3::register_config();
RasterizerGLES3::make_current();
@@ -74,22 +67,10 @@ DisplayServerIOS::DisplayServerIOS(const String &p_rendering_driver, WindowMode
}
if (gl_initialization_error) {
- OS::get_singleton()->alert("Your device does not support any of the supported OpenGL versions.", "Unable to initialize video driver");
- // return ERR_UNAVAILABLE;
+ OS::get_singleton()->alert(
+ "Your device seems not to support the required OpenGL ES 3.0 version.\n\n",
+ "Unable to initialize OpenGL video driver");
}
-
- // rendering_server = memnew(RenderingServerDefault);
- // // FIXME: Reimplement threaded rendering
- // if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) {
- // rendering_server = memnew(RenderingServerWrapMT(rendering_server,
- // false));
- // }
- // rendering_server->init();
- // rendering_server->cursor_set_visible(false, 0);
-
- // reset this to what it should be, it will have been set to 0 after
- // rendering_server->init() is called
- // RasterizerStorageGLES3system_fbo = gl_view_base_fb;
}
#endif
diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp
index 49b4852cbf..87b599bc81 100644
--- a/platform/ios/export/export_plugin.cpp
+++ b/platform/ios/export/export_plugin.cpp
@@ -32,6 +32,13 @@
#include "core/string/translation.h"
#include "editor/editor_node.h"
+#include "editor/editor_scale.h"
+#include "platform/ios/logo_svg.gen.h"
+
+#include "modules/modules_enabled.gen.h" // For svg.
+#ifdef MODULE_SVG_ENABLED
+#include "modules/svg/image_loader_svg.h"
+#endif
void EditorExportPlatformIOS::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const {
// Vulkan and OpenGL ES 3.0 both mandate ETC2 support.
@@ -1565,7 +1572,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
int ret = unzGoToFirstFile(src_pkg_zip);
Vector<uint8_t> project_file_data;
while (ret == UNZ_OK) {
-#if defined(MACOS_ENABLED) || defined(X11_ENABLED)
+#if defined(MACOS_ENABLED) || defined(LINUXBSD_ENABLED)
bool is_execute = false;
#endif
@@ -1598,7 +1605,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
continue; //ignore!
}
found_library = true;
-#if defined(MACOS_ENABLED) || defined(X11_ENABLED)
+#if defined(MACOS_ENABLED) || defined(LINUXBSD_ENABLED)
is_execute = true;
#endif
file = file.replace(library_to_use, binary_name + ".xcframework");
@@ -1641,7 +1648,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
f->store_buffer(data.ptr(), data.size());
}
-#if defined(MACOS_ENABLED) || defined(X11_ENABLED)
+#if defined(MACOS_ENABLED) || defined(LINUXBSD_ENABLED)
if (is_execute) {
// we need execute rights on this file
chmod(file.utf8().get_data(), 0755);
@@ -1799,7 +1806,10 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
ERR_FAIL_COND_V(dylibs_dir.is_null(), ERR_CANT_OPEN);
CodesignData codesign_data(p_preset, p_debug);
err = _walk_dir_recursive(dylibs_dir, _codesign, &codesign_data);
- ERR_FAIL_COND_V(err, err);
+ if (err != OK) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Code Signing"), TTR("Code signing failed, see editor log for details."));
+ return err;
+ }
}
if (ep.step("Making .xcarchive", 3)) {
@@ -1825,6 +1835,10 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
err = OS::get_singleton()->execute("xcodebuild", archive_args, &archive_str, nullptr, true);
ERR_FAIL_COND_V(err, err);
print_line("xcodebuild (.xcarchive):\n" + archive_str);
+ if (!archive_str.contains("** ARCHIVE SUCCEEDED **")) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Xcode Build"), TTR("Xcode project build failed, see editor log for details."));
+ return FAILED;
+ }
if (ep.step("Making .ipa", 4)) {
return ERR_SKIP;
@@ -1841,9 +1855,14 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
String export_str;
err = OS::get_singleton()->execute("xcodebuild", export_args, &export_str, nullptr, true);
ERR_FAIL_COND_V(err, err);
+
print_line("xcodebuild (.ipa):\n" + export_str);
+ if (!export_str.contains("** EXPORT SUCCEEDED **")) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Xcode Build"), TTR(".ipa export failed, see editor log for details."));
+ return FAILED;
+ }
#else
- print_line(".ipa can only be built on macOS. Leaving Xcode project without building the package.");
+ add_message(EXPORT_MESSAGE_WARNING, TTR("Xcode Build"), TTR(".ipa can only be built on macOS. Leaving Xcode project without building the package."));
#endif
return OK;
@@ -1914,7 +1933,15 @@ bool EditorExportPlatformIOS::has_valid_project_configuration(const Ref<EditorEx
}
EditorExportPlatformIOS::EditorExportPlatformIOS() {
- logo = ImageTexture::create_from_image(memnew(Image(_ios_logo)));
+#ifdef MODULE_SVG_ENABLED
+ Ref<Image> img = memnew(Image);
+ const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE);
+
+ ImageLoaderSVG img_loader;
+ img_loader.create_image_from_string(img, _ios_logo_svg, EDSCALE, upsample, false);
+ logo = ImageTexture::create_from_image(img);
+#endif
+
plugins_changed.set();
#ifndef ANDROID_ENABLED
check_for_changes_thread.start(_check_for_changes_poll_thread, this);
diff --git a/platform/ios/export/export_plugin.h b/platform/ios/export/export_plugin.h
index deadec4b43..628dae2e6f 100644
--- a/platform/ios/export/export_plugin.h
+++ b/platform/ios/export/export_plugin.h
@@ -43,7 +43,6 @@
#include "editor/editor_settings.h"
#include "editor/export/editor_export_platform.h"
#include "main/splash.gen.h"
-#include "platform/ios/logo.gen.h"
#include "string.h"
#include "godot_plugin_config.h"
diff --git a/platform/ios/logo.png b/platform/ios/logo.png
deleted file mode 100644
index 966d8aa70a..0000000000
--- a/platform/ios/logo.png
+++ /dev/null
Binary files differ
diff --git a/platform/ios/logo.svg b/platform/ios/logo.svg
new file mode 100644
index 0000000000..47a72bcf49
--- /dev/null
+++ b/platform/ios/logo.svg
@@ -0,0 +1 @@
+<svg height="32" width="32" xmlns="http://www.w3.org/2000/svg"><path d="M1 23.27h2.504V12.61H1zm1.247-12.057c.784 0 1.398-.603 1.398-1.358 0-.764-.614-1.367-1.398-1.367-.774 0-1.388.603-1.388 1.367 0 .755.614 1.358 1.388 1.358zm9.594-2.695c-4.233 0-6.888 2.886-6.888 7.502s2.654 7.492 6.888 7.492c4.224 0 6.88-2.876 6.88-7.492s-2.656-7.502-6.88-7.502zm0 2.212c2.585 0 4.234 2.052 4.234 5.29 0 3.228-1.649 5.28-4.234 5.28-2.594 0-4.233-2.052-4.233-5.28 0-3.238 1.639-5.29 4.233-5.29zm7.936 8.458c.11 2.675 2.303 4.324 5.641 4.324 3.51 0 5.723-1.73 5.723-4.485 0-2.162-1.247-3.379-4.194-4.053l-1.67-.382c-1.78-.422-2.513-.985-2.513-1.95 0-1.208 1.106-2.012 2.745-2.012 1.66 0 2.796.814 2.916 2.172H30.9c-.06-2.554-2.172-4.284-5.37-4.284-3.158 0-5.4 1.74-5.4 4.314 0 2.072 1.267 3.36 3.942 3.973l1.88.442c1.83.433 2.575 1.036 2.575 2.082 0 1.207-1.217 2.072-2.967 2.072-1.77 0-3.107-.875-3.268-2.213h-2.514z" fill="#bfbfbf"/></svg>
diff --git a/platform/linuxbsd/README.md b/platform/linuxbsd/README.md
index efa8682062..c4f287cc6c 100644
--- a/platform/linuxbsd/README.md
+++ b/platform/linuxbsd/README.md
@@ -7,7 +7,7 @@ used by this platform.
## Documentation
-- [Compiling for Linux/*BSD](https://docs.godotengine.org/en/latest/development/compiling/compiling_for_linuxbsd.html)
+- [Compiling for Linux/*BSD](https://docs.godotengine.org/en/latest/contributing/development/compiling/compiling_for_linuxbsd.html)
- Instructions on building this platform port from source.
- [Exporting for Linux/*BSD](https://docs.godotengine.org/en/latest/tutorials/export/exporting_for_linux.html)
- Instructions on using the compiled export templates to export a project.
diff --git a/platform/linuxbsd/crash_handler_linuxbsd.cpp b/platform/linuxbsd/crash_handler_linuxbsd.cpp
index add69c436f..8d03e3d31c 100644
--- a/platform/linuxbsd/crash_handler_linuxbsd.cpp
+++ b/platform/linuxbsd/crash_handler_linuxbsd.cpp
@@ -44,6 +44,7 @@
#include <cxxabi.h>
#include <dlfcn.h>
#include <execinfo.h>
+#include <link.h>
#include <signal.h>
#include <stdlib.h>
@@ -79,7 +80,27 @@ static void handle_crash(int sig) {
}
print_error(vformat("Dumping the backtrace. %s", msg));
char **strings = backtrace_symbols(bt_buffer, size);
+ // PIE executable relocation, zero for non-PIE executables
+ uintptr_t relocation = _r_debug.r_map->l_addr;
if (strings) {
+ List<String> args;
+ for (size_t i = 0; i < size; i++) {
+ char str[1024];
+ snprintf(str, 1024, "%p", (void *)((uintptr_t)bt_buffer[i] - relocation));
+ args.push_back(str);
+ }
+ args.push_back("-e");
+ args.push_back(_execpath);
+
+ // Try to get the file/line number using addr2line
+ int ret;
+ String output = "";
+ Error err = OS::get_singleton()->execute(String("addr2line"), args, &output, &ret);
+ Vector<String> addr2line_results;
+ if (err == OK) {
+ addr2line_results = output.substr(0, output.length() - 1).split("\n", false);
+ }
+
for (size_t i = 1; i < size; i++) {
char fname[1024];
Dl_info info;
@@ -102,24 +123,7 @@ static void handle_crash(int sig) {
}
}
- List<String> args;
-
- char str[1024];
- snprintf(str, 1024, "%p", bt_buffer[i]);
- args.push_back(str);
- args.push_back("-e");
- args.push_back(_execpath);
-
- String output = "";
-
- // Try to get the file/line number using addr2line
- int ret;
- Error err = OS::get_singleton()->execute(String("addr2line"), args, &output, &ret);
- if (err == OK) {
- output = output.substr(0, output.length() - 1);
- }
-
- print_error(vformat("[%d] %s (%s)", (int64_t)i, fname, output));
+ print_error(vformat("[%d] %s (%s)", (int64_t)i, fname, err == OK ? addr2line_results[i] : ""));
}
free(strings);
diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py
index 747dcbd76c..f4d36f1a87 100644
--- a/platform/linuxbsd/detect.py
+++ b/platform/linuxbsd/detect.py
@@ -338,12 +338,17 @@ def configure(env: "Environment"):
env.Prepend(CPPPATH=["#platform/linuxbsd"])
+ env.Append(
+ CPPDEFINES=[
+ "LINUXBSD_ENABLED",
+ "UNIX_ENABLED",
+ ("_FILE_OFFSET_BITS", 64),
+ ]
+ )
+
if env["x11"]:
env.Append(CPPDEFINES=["X11_ENABLED"])
- env.Append(CPPDEFINES=["UNIX_ENABLED"])
- env.Append(CPPDEFINES=[("_FILE_OFFSET_BITS", 64)])
-
if env["vulkan"]:
env.Append(CPPDEFINES=["VULKAN_ENABLED"])
if not env["use_volk"]:
diff --git a/platform/linuxbsd/export/export.cpp b/platform/linuxbsd/export/export.cpp
index e6925a2011..2c5a945b6c 100644
--- a/platform/linuxbsd/export/export.cpp
+++ b/platform/linuxbsd/export/export.cpp
@@ -36,7 +36,6 @@
void register_linuxbsd_exporter() {
Ref<EditorExportPlatformLinuxBSD> platform;
platform.instantiate();
- platform->set_logo(ImageTexture::create_from_image(memnew(Image(_linuxbsd_logo))));
platform->set_name("Linux/X11");
platform->set_os_name("Linux");
platform->set_chmod_flags(0755);
diff --git a/platform/linuxbsd/export/export_plugin.cpp b/platform/linuxbsd/export/export_plugin.cpp
index f6e59bf465..c900cad007 100644
--- a/platform/linuxbsd/export/export_plugin.cpp
+++ b/platform/linuxbsd/export/export_plugin.cpp
@@ -32,6 +32,15 @@
#include "core/config/project_settings.h"
#include "editor/editor_node.h"
+#include "editor/editor_paths.h"
+#include "editor/editor_scale.h"
+#include "platform/linuxbsd/logo_svg.gen.h"
+#include "platform/linuxbsd/run_icon_svg.gen.h"
+
+#include "modules/modules_enabled.gen.h" // For svg.
+#ifdef MODULE_SVG_ENABLED
+#include "modules/svg/image_loader_svg.h"
+#endif
Error EditorExportPlatformLinuxBSD::_export_debug_script(const Ref<EditorExportPreset> &p_preset, const String &p_app_name, const String &p_pkg_name, const String &p_path) {
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::WRITE);
@@ -49,26 +58,47 @@ Error EditorExportPlatformLinuxBSD::_export_debug_script(const Ref<EditorExportP
}
Error EditorExportPlatformLinuxBSD::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
- Error err = EditorExportPlatformPC::export_project(p_preset, p_debug, p_path, p_flags);
-
- if (err != OK) {
- return err;
- }
+ bool export_as_zip = p_path.ends_with("zip");
- String app_name;
+ String pkg_name;
if (String(GLOBAL_GET("application/config/name")) != "") {
- app_name = String(GLOBAL_GET("application/config/name"));
+ pkg_name = String(GLOBAL_GET("application/config/name"));
} else {
- app_name = "Unnamed";
+ pkg_name = "Unnamed";
+ }
+
+ pkg_name = OS::get_singleton()->get_safe_dir_name(pkg_name);
+
+ // Setup temp folder.
+ String path = p_path;
+ String tmp_dir_path = EditorPaths::get_singleton()->get_cache_dir().path_join(pkg_name);
+
+ Ref<DirAccess> tmp_app_dir = DirAccess::create_for_path(tmp_dir_path);
+ if (export_as_zip) {
+ if (tmp_app_dir.is_null()) {
+ return ERR_CANT_CREATE;
+ }
+ if (DirAccess::exists(tmp_dir_path)) {
+ if (tmp_app_dir->change_dir(tmp_dir_path) == OK) {
+ tmp_app_dir->erase_contents_recursive();
+ }
+ }
+ tmp_app_dir->make_dir_recursive(tmp_dir_path);
+ path = tmp_dir_path.path_join(p_path.get_file().get_basename());
+ }
+
+ // Export project.
+ Error err = EditorExportPlatformPC::export_project(p_preset, p_debug, path, p_flags);
+ if (err != OK) {
+ return err;
}
- app_name = OS::get_singleton()->get_safe_dir_name(app_name);
// Save console script.
if (err == OK) {
int con_scr = p_preset->get("debug/export_console_script");
if ((con_scr == 1 && p_debug) || (con_scr == 2)) {
- String scr_path = p_path.get_basename() + ".sh";
- err = _export_debug_script(p_preset, app_name, p_path.get_file(), scr_path);
+ String scr_path = path.get_basename() + ".sh";
+ err = _export_debug_script(p_preset, pkg_name, path.get_file(), scr_path);
FileAccess::set_unix_permissions(scr_path, 0755);
if (err != OK) {
add_message(EXPORT_MESSAGE_ERROR, TTR("Debug Script Export"), TTR("Could not create console script."));
@@ -76,6 +106,27 @@ Error EditorExportPlatformLinuxBSD::export_project(const Ref<EditorExportPreset>
}
}
+ // ZIP project.
+ if (export_as_zip) {
+ if (FileAccess::exists(p_path)) {
+ OS::get_singleton()->move_to_trash(p_path);
+ }
+
+ Ref<FileAccess> io_fa_dst;
+ zlib_filefunc_def io_dst = zipio_create_io(&io_fa_dst);
+ zipFile zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io_dst);
+
+ zip_folder_recursive(zip, tmp_dir_path, "", pkg_name);
+
+ zipClose(zip, nullptr);
+
+ if (tmp_app_dir->change_dir(tmp_dir_path) == OK) {
+ tmp_app_dir->erase_contents_recursive();
+ tmp_app_dir->change_dir("..");
+ tmp_app_dir->remove(pkg_name);
+ }
+ }
+
return err;
}
@@ -86,12 +137,51 @@ String EditorExportPlatformLinuxBSD::get_template_file_name(const String &p_targ
List<String> EditorExportPlatformLinuxBSD::get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const {
List<String> list;
list.push_back(p_preset->get("binary_format/architecture"));
+ list.push_back("zip");
+
return list;
}
void EditorExportPlatformLinuxBSD::get_export_options(List<ExportOption> *r_options) {
EditorExportPlatformPC::get_export_options(r_options);
+
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "binary_format/architecture", PROPERTY_HINT_ENUM, "x86_64,x86_32,arm64,arm32,rv64,ppc64,ppc32"), "x86_64"));
+
+ String run_script = "#!/usr/bin/env bash\n"
+ "export DISPLAY=:0\n"
+ "unzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\"\n"
+ "\"{temp_dir}/{exe_name}\" {cmd_args}";
+
+ String cleanup_script = "#!/usr/bin/env bash\n"
+ "kill $(pgrep -x -f \"{temp_dir}/{exe_name} {cmd_args}\")\n"
+ "rm -rf \"{temp_dir}\"";
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "ssh_remote_deploy/enabled"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/host"), "user@host_ip"));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/port"), "22"));
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/extra_args_ssh", PROPERTY_HINT_MULTILINE_TEXT), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/extra_args_scp", PROPERTY_HINT_MULTILINE_TEXT), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/run_script", PROPERTY_HINT_MULTILINE_TEXT), run_script));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/cleanup_script", PROPERTY_HINT_MULTILINE_TEXT), cleanup_script));
+}
+
+bool EditorExportPlatformLinuxBSD::is_elf(const String &p_path) const {
+ Ref<FileAccess> fb = FileAccess::open(p_path, FileAccess::READ);
+ ERR_FAIL_COND_V_MSG(fb.is_null(), false, vformat("Can't open file: \"%s\".", p_path));
+ uint32_t magic = fb->get_32();
+ return (magic == 0x464c457f);
+}
+
+bool EditorExportPlatformLinuxBSD::is_shebang(const String &p_path) const {
+ Ref<FileAccess> fb = FileAccess::open(p_path, FileAccess::READ);
+ ERR_FAIL_COND_V_MSG(fb.is_null(), false, vformat("Can't open file: \"%s\".", p_path));
+ uint16_t magic = fb->get_16();
+ return (magic == 0x2123);
+}
+
+bool EditorExportPlatformLinuxBSD::is_executable(const String &p_path) const {
+ return is_elf(p_path) || is_shebang(p_path);
}
Error EditorExportPlatformLinuxBSD::fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) {
@@ -200,3 +290,235 @@ Error EditorExportPlatformLinuxBSD::fixup_embedded_pck(const String &p_path, int
}
return OK;
}
+
+Ref<Texture2D> EditorExportPlatformLinuxBSD::get_run_icon() const {
+ return run_icon;
+}
+
+bool EditorExportPlatformLinuxBSD::poll_export() {
+ Ref<EditorExportPreset> preset;
+
+ for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
+ Ref<EditorExportPreset> ep = EditorExport::get_singleton()->get_export_preset(i);
+ if (ep->is_runnable() && ep->get_platform() == this) {
+ preset = ep;
+ break;
+ }
+ }
+
+ int prev = menu_options;
+ menu_options = (preset.is_valid() && preset->get("ssh_remote_deploy/enabled").operator bool());
+ if (ssh_pid != 0 || !cleanup_commands.is_empty()) {
+ if (menu_options == 0) {
+ cleanup();
+ } else {
+ menu_options += 1;
+ }
+ }
+ return menu_options != prev;
+}
+
+Ref<ImageTexture> EditorExportPlatformLinuxBSD::get_option_icon(int p_index) const {
+ return p_index == 1 ? stop_icon : EditorExportPlatform::get_option_icon(p_index);
+}
+
+int EditorExportPlatformLinuxBSD::get_options_count() const {
+ return menu_options;
+}
+
+String EditorExportPlatformLinuxBSD::get_option_label(int p_index) const {
+ return (p_index) ? TTR("Stop and uninstall") : TTR("Run on remote Linux/BSD system");
+}
+
+String EditorExportPlatformLinuxBSD::get_option_tooltip(int p_index) const {
+ return (p_index) ? TTR("Stop and uninstall running project from the remote system") : TTR("Run exported project on remote Linux/BSD system");
+}
+
+void EditorExportPlatformLinuxBSD::cleanup() {
+ if (ssh_pid != 0 && OS::get_singleton()->is_process_running(ssh_pid)) {
+ print_line("Terminating connection...");
+ OS::get_singleton()->kill(ssh_pid);
+ OS::get_singleton()->delay_usec(1000);
+ }
+
+ if (!cleanup_commands.is_empty()) {
+ print_line("Stopping and deleting previous version...");
+ for (const SSHCleanupCommand &cmd : cleanup_commands) {
+ if (cmd.wait) {
+ ssh_run_on_remote(cmd.host, cmd.port, cmd.ssh_args, cmd.cmd_args);
+ } else {
+ ssh_run_on_remote_no_wait(cmd.host, cmd.port, cmd.ssh_args, cmd.cmd_args);
+ }
+ }
+ }
+ ssh_pid = 0;
+ cleanup_commands.clear();
+}
+
+Error EditorExportPlatformLinuxBSD::run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) {
+ cleanup();
+ if (p_device) { // Stop command, cleanup only.
+ return OK;
+ }
+
+ EditorProgress ep("run", TTR("Running..."), 5);
+
+ const String dest = EditorPaths::get_singleton()->get_cache_dir().path_join("linuxbsd");
+ Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ if (!da->dir_exists(dest)) {
+ Error err = da->make_dir_recursive(dest);
+ if (err != OK) {
+ EditorNode::get_singleton()->show_warning(TTR("Could not create temp directory:") + "\n" + dest);
+ return err;
+ }
+ }
+
+ String host = p_preset->get("ssh_remote_deploy/host").operator String();
+ String port = p_preset->get("ssh_remote_deploy/port").operator String();
+ if (port.is_empty()) {
+ port = "22";
+ }
+ Vector<String> extra_args_ssh = p_preset->get("ssh_remote_deploy/extra_args_ssh").operator String().split(" ");
+ Vector<String> extra_args_scp = p_preset->get("ssh_remote_deploy/extra_args_scp").operator String().split(" ");
+
+ const String basepath = dest.path_join("tmp_linuxbsd_export");
+
+#define CLEANUP_AND_RETURN(m_err) \
+ { \
+ if (da->file_exists(basepath + ".zip")) { \
+ da->remove(basepath + ".zip"); \
+ } \
+ if (da->file_exists(basepath + "_start.sh")) { \
+ da->remove(basepath + "_start.sh"); \
+ } \
+ if (da->file_exists(basepath + "_clean.sh")) { \
+ da->remove(basepath + "_clean.sh"); \
+ } \
+ return m_err; \
+ } \
+ ((void)0)
+
+ if (ep.step(TTR("Exporting project..."), 1)) {
+ return ERR_SKIP;
+ }
+ Error err = export_project(p_preset, true, basepath + ".zip", p_debug_flags);
+ if (err != OK) {
+ DirAccess::remove_file_or_error(basepath + ".zip");
+ return err;
+ }
+
+ String cmd_args;
+ {
+ Vector<String> cmd_args_list;
+ gen_debug_flags(cmd_args_list, p_debug_flags);
+ for (int i = 0; i < cmd_args_list.size(); i++) {
+ if (i != 0) {
+ cmd_args += " ";
+ }
+ cmd_args += cmd_args_list[i];
+ }
+ }
+
+ const bool use_remote = (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) || (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT);
+ int dbg_port = EditorSettings::get_singleton()->get("network/debug/remote_port");
+
+ print_line("Creating temporary directory...");
+ ep.step(TTR("Creating temporary directory..."), 2);
+ String temp_dir;
+ err = ssh_run_on_remote(host, port, extra_args_ssh, "mktemp -d", &temp_dir);
+ if (err != OK || temp_dir.is_empty()) {
+ CLEANUP_AND_RETURN(err);
+ }
+
+ print_line("Uploading archive...");
+ ep.step(TTR("Uploading archive..."), 3);
+ err = ssh_push_to_remote(host, port, extra_args_scp, basepath + ".zip", temp_dir);
+ if (err != OK) {
+ CLEANUP_AND_RETURN(err);
+ }
+
+ {
+ String run_script = p_preset->get("ssh_remote_deploy/run_script");
+ run_script = run_script.replace("{temp_dir}", temp_dir);
+ run_script = run_script.replace("{archive_name}", basepath.get_file() + ".zip");
+ run_script = run_script.replace("{exe_name}", basepath.get_file());
+ run_script = run_script.replace("{cmd_args}", cmd_args);
+
+ Ref<FileAccess> f = FileAccess::open(basepath + "_start.sh", FileAccess::WRITE);
+ if (f.is_null()) {
+ CLEANUP_AND_RETURN(err);
+ }
+
+ f->store_string(run_script);
+ }
+
+ {
+ String clean_script = p_preset->get("ssh_remote_deploy/cleanup_script");
+ clean_script = clean_script.replace("{temp_dir}", temp_dir);
+ clean_script = clean_script.replace("{archive_name}", basepath.get_file() + ".zip");
+ clean_script = clean_script.replace("{exe_name}", basepath.get_file());
+ clean_script = clean_script.replace("{cmd_args}", cmd_args);
+
+ Ref<FileAccess> f = FileAccess::open(basepath + "_clean.sh", FileAccess::WRITE);
+ if (f.is_null()) {
+ CLEANUP_AND_RETURN(err);
+ }
+
+ f->store_string(clean_script);
+ }
+
+ print_line("Uploading scripts...");
+ ep.step(TTR("Uploading scripts..."), 4);
+ err = ssh_push_to_remote(host, port, extra_args_scp, basepath + "_start.sh", temp_dir);
+ if (err != OK) {
+ CLEANUP_AND_RETURN(err);
+ }
+ err = ssh_run_on_remote(host, port, extra_args_ssh, vformat("chmod +x \"%s/%s\"", temp_dir, basepath.get_file() + "_start.sh"));
+ if (err != OK || temp_dir.is_empty()) {
+ CLEANUP_AND_RETURN(err);
+ }
+ err = ssh_push_to_remote(host, port, extra_args_scp, basepath + "_clean.sh", temp_dir);
+ if (err != OK) {
+ CLEANUP_AND_RETURN(err);
+ }
+ err = ssh_run_on_remote(host, port, extra_args_ssh, vformat("chmod +x \"%s/%s\"", temp_dir, basepath.get_file() + "_clean.sh"));
+ if (err != OK || temp_dir.is_empty()) {
+ CLEANUP_AND_RETURN(err);
+ }
+
+ print_line("Starting project...");
+ ep.step(TTR("Starting project..."), 5);
+ err = ssh_run_on_remote_no_wait(host, port, extra_args_ssh, vformat("\"%s/%s\"", temp_dir, basepath.get_file() + "_start.sh"), &ssh_pid, (use_remote) ? dbg_port : -1);
+ if (err != OK) {
+ CLEANUP_AND_RETURN(err);
+ }
+
+ cleanup_commands.clear();
+ cleanup_commands.push_back(SSHCleanupCommand(host, port, extra_args_ssh, vformat("\"%s/%s\"", temp_dir, basepath.get_file() + "_clean.sh")));
+
+ print_line("Project started.");
+
+ CLEANUP_AND_RETURN(OK);
+#undef CLEANUP_AND_RETURN
+}
+
+EditorExportPlatformLinuxBSD::EditorExportPlatformLinuxBSD() {
+#ifdef MODULE_SVG_ENABLED
+ Ref<Image> img = memnew(Image);
+ const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE);
+
+ ImageLoaderSVG img_loader;
+ img_loader.create_image_from_string(img, _linuxbsd_logo_svg, EDSCALE, upsample, false);
+ set_logo(ImageTexture::create_from_image(img));
+
+ img_loader.create_image_from_string(img, _linuxbsd_run_icon_svg, EDSCALE, upsample, false);
+ run_icon = ImageTexture::create_from_image(img);
+#endif
+
+ Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme();
+ if (theme.is_valid()) {
+ stop_icon = theme->get_icon(SNAME("Stop"), SNAME("EditorIcons"));
+ } else {
+ stop_icon.instantiate();
+ }
+}
diff --git a/platform/linuxbsd/export/export_plugin.h b/platform/linuxbsd/export/export_plugin.h
index 71cf18ff3e..4f860c3fd0 100644
--- a/platform/linuxbsd/export/export_plugin.h
+++ b/platform/linuxbsd/export/export_plugin.h
@@ -34,19 +34,58 @@
#include "core/io/file_access.h"
#include "editor/editor_settings.h"
#include "editor/export/editor_export_platform_pc.h"
-#include "platform/linuxbsd/logo.gen.h"
#include "scene/resources/texture.h"
class EditorExportPlatformLinuxBSD : public EditorExportPlatformPC {
+ HashMap<String, String> extensions;
+
+ struct SSHCleanupCommand {
+ String host;
+ String port;
+ Vector<String> ssh_args;
+ String cmd_args;
+ bool wait = false;
+
+ SSHCleanupCommand(){};
+ SSHCleanupCommand(const String &p_host, const String &p_port, const Vector<String> &p_ssh_arg, const String &p_cmd_args, bool p_wait = false) {
+ host = p_host;
+ port = p_port;
+ ssh_args = p_ssh_arg;
+ cmd_args = p_cmd_args;
+ wait = p_wait;
+ };
+ };
+
+ Ref<ImageTexture> run_icon;
+ Ref<ImageTexture> stop_icon;
+
+ Vector<SSHCleanupCommand> cleanup_commands;
+ OS::ProcessID ssh_pid = 0;
+ int menu_options = 0;
+
+ bool is_elf(const String &p_path) const;
+ bool is_shebang(const String &p_path) const;
+
Error _export_debug_script(const Ref<EditorExportPreset> &p_preset, const String &p_app_name, const String &p_pkg_name, const String &p_path);
public:
- void set_extension(const String &p_extension, const String &p_feature_key = "default");
- virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override;
virtual void get_export_options(List<ExportOption> *r_options) override;
+ virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override;
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override;
virtual String get_template_file_name(const String &p_target, const String &p_arch) const override;
virtual Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) override;
+ virtual bool is_executable(const String &p_path) const override;
+
+ virtual Ref<Texture2D> get_run_icon() const override;
+ virtual bool poll_export() override;
+ virtual Ref<ImageTexture> get_option_icon(int p_index) const override;
+ virtual int get_options_count() const override;
+ virtual String get_option_label(int p_index) const override;
+ virtual String get_option_tooltip(int p_index) const override;
+ virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) override;
+ virtual void cleanup() override;
+
+ EditorExportPlatformLinuxBSD();
};
#endif // LINUXBSD_EXPORT_PLUGIN_H
diff --git a/platform/linuxbsd/joypad_linux.cpp b/platform/linuxbsd/joypad_linux.cpp
index 672c48addc..b77f989677 100644
--- a/platform/linuxbsd/joypad_linux.cpp
+++ b/platform/linuxbsd/joypad_linux.cpp
@@ -59,7 +59,7 @@ JoypadLinux::Joypad::~Joypad() {
}
void JoypadLinux::Joypad::reset() {
- dpad = HatMask::CENTER;
+ dpad = 0;
fd = -1;
for (int i = 0; i < MAX_ABS; i++) {
abs_map[i] = -1;
@@ -485,27 +485,33 @@ void JoypadLinux::process_joypads() {
case ABS_HAT0X:
if (joypad_event.value != 0) {
if (joypad_event.value < 0) {
- joypad.dpad = (HatMask)((joypad.dpad | HatMask::LEFT) & ~HatMask::RIGHT);
+ joypad.dpad.set_flag(HatMask::LEFT);
+ joypad.dpad.clear_flag(HatMask::RIGHT);
} else {
- joypad.dpad = (HatMask)((joypad.dpad | HatMask::RIGHT) & ~HatMask::LEFT);
+ joypad.dpad.set_flag(HatMask::RIGHT);
+ joypad.dpad.clear_flag(HatMask::LEFT);
}
} else {
- joypad.dpad &= ~(HatMask::LEFT | HatMask::RIGHT);
+ joypad.dpad.clear_flag(HatMask::LEFT);
+ joypad.dpad.clear_flag(HatMask::RIGHT);
}
- input->joy_hat(i, (HatMask)joypad.dpad);
+ input->joy_hat(i, joypad.dpad);
break;
case ABS_HAT0Y:
if (joypad_event.value != 0) {
if (joypad_event.value < 0) {
- joypad.dpad = (HatMask)((joypad.dpad | HatMask::UP) & ~HatMask::DOWN);
+ joypad.dpad.set_flag(HatMask::UP);
+ joypad.dpad.clear_flag(HatMask::DOWN);
} else {
- joypad.dpad = (HatMask)((joypad.dpad | HatMask::DOWN) & ~HatMask::UP);
+ joypad.dpad.set_flag(HatMask::DOWN);
+ joypad.dpad.clear_flag(HatMask::UP);
}
} else {
- joypad.dpad &= ~(HatMask::UP | HatMask::DOWN);
+ joypad.dpad.clear_flag(HatMask::UP);
+ joypad.dpad.clear_flag(HatMask::DOWN);
}
- input->joy_hat(i, (HatMask)joypad.dpad);
+ input->joy_hat(i, joypad.dpad);
break;
default:
diff --git a/platform/linuxbsd/joypad_linux.h b/platform/linuxbsd/joypad_linux.h
index 7926756cbe..6661abdb37 100644
--- a/platform/linuxbsd/joypad_linux.h
+++ b/platform/linuxbsd/joypad_linux.h
@@ -62,7 +62,7 @@ private:
float curr_axis[MAX_ABS];
int key_map[MAX_KEY];
int abs_map[MAX_ABS];
- HatMask dpad = HatMask::CENTER;
+ BitField<HatMask> dpad;
int fd = -1;
String devpath;
diff --git a/platform/linuxbsd/logo.png b/platform/linuxbsd/logo.png
deleted file mode 100644
index 078654b757..0000000000
--- a/platform/linuxbsd/logo.png
+++ /dev/null
Binary files differ
diff --git a/platform/linuxbsd/logo.svg b/platform/linuxbsd/logo.svg
new file mode 100644
index 0000000000..e5f9f03e0c
--- /dev/null
+++ b/platform/linuxbsd/logo.svg
@@ -0,0 +1 @@
+<svg height="32" width="32"><path d="M13 31h6s3 0 6-6c2.864-5.727-6-17-6-17h-6S3.775 18.55 7 25c3 6 6 6 6 6z" fill="#fff"/><path d="M15.876 28.636c-.05.322-.116.637-.204.941-.142.496-.35.993-.659 1.416.32.02.649.023.985.007a9.1 9.1 0 0 0 .985-.007c-.309-.423-.516-.92-.659-1.416a7.666 7.666 0 0 1-.203-.94l-.123.003c-.04 0-.081-.003-.122-.004z" fill="#333"/><path d="M21.693 21.916c-.629.01-.934.633-1.497.7-.694.08-1.128-.722-2.11-.123-.98.6-1.826 7.473.45 8.409 2.274.935 6.506-4.545 6.23-5.662-.275-1.116-1.146-.853-1.582-1.399-.436-.545.003-1.41-.995-1.82a1.246 1.246 0 0 0-.496-.105zm-11.461 0a1.315 1.315 0 0 0-.421.105c-.998.41-.56 1.275-.995 1.82-.436.546-1.31.283-1.586 1.4-.275 1.116 3.956 6.596 6.232 5.66 2.275-.935 1.429-7.808.448-8.408-.981-.6-1.415.204-2.11.122-.584-.068-.888-.739-1.568-.7z" fill="#f4bb37"/><path d="M15.998.99c-2.934 0-4.657 1.79-4.982 4.204-.324 2.414.198 2.856-.614 5.328-.813 2.472-4.456 6.71-4.37 10.62.026 1.217.166 2.27.41 3.192.3-.496.743-.846 1.066-.995.253-.117.375-.173.432-.194.008-.062.04-.205.098-.485.08-.386.387-.99.91-1.386-.005-.12-.01-.239-.013-.363-.06-3.033 3.073-6.318 3.65-8.236.577-1.917.326-2.114.421-2.59.096-.477.463-1.032.992-1.475a.23.23 0 0 1 .15-.06c.482-.005.965 1.75 1.898 1.752.933 0 1.419-2.141 1.956-1.692.529.443.896.998.992 1.474.095.477-.156.674.42 2.591.578 1.918 3.708 5.203 3.648 8.236-.003.123-.008.24-.014.36.526.396.834 1.002.914 1.389.058.28.09.423.098.485.057.021.18.08.432.197.323.15.764.499 1.063.995.244-.922.387-1.976.414-3.195.085-3.91-3.562-8.148-4.374-10.62-.813-2.472-.287-2.914-.611-5.328C20.659 2.78 18.933.99 15.998.99z" fill="#333"/></svg>
diff --git a/platform/linuxbsd/run_icon.svg b/platform/linuxbsd/run_icon.svg
new file mode 100644
index 0000000000..56465a0df3
--- /dev/null
+++ b/platform/linuxbsd/run_icon.svg
@@ -0,0 +1 @@
+<svg height="16" width="16" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="M7.941 13.966a3.62 3.62 0 0 1-.096.444 2.129 2.129 0 0 1-.31.668c.15.01.305.01.464.003.16.008.314.007.465-.003a2.129 2.129 0 0 1-.31-.668 3.62 3.62 0 0 1-.097-.444l-.058.001-.058-.001z" fill="#333" style="stroke-width:.472092;fill:#e0e0e0;fill-opacity:1"/><path d="M10.688 10.793c-.297.005-.441.299-.707.33-.328.038-.533-.34-.996-.058-.463.283-.862 3.528.212 3.97 1.074.442 3.072-2.146 2.942-2.673-.13-.527-.542-.403-.747-.66-.206-.258 0-.666-.47-.86a.588.588 0 0 0-.234-.05zm-5.411 0a.62.62 0 0 0-.199.05c-.47.193-.264.601-.47.859-.205.257-.618.133-.748.66s1.867 3.115 2.942 2.673c1.074-.442.674-3.687.211-3.97-.463-.283-.668.096-.995.058-.277-.032-.42-.349-.741-.33z" fill="#f4bb37" style="stroke-width:.472092;fill:#e0e0e0;fill-opacity:1"/><path d="M8 .914c-1.386 0-2.2.845-2.353 1.985-.153 1.14.094 1.348-.29 2.515s-2.103 3.168-2.063 5.013c.012.575.078 1.072.194 1.507a1.25 1.25 0 0 1 .503-.47 4.37 4.37 0 0 1 .204-.09c.004-.03.019-.098.046-.23.038-.182.183-.467.43-.654a4.773 4.773 0 0 1-.006-.172c-.029-1.431 1.45-2.982 1.723-3.888.272-.905.154-.998.199-1.223.045-.225.218-.487.468-.696a.11.11 0 0 1 .07-.028c.228-.003.456.826.897.827.44 0 .67-1.01.923-.799.25.21.423.471.468.696.045.225-.073.318.199 1.223.272.906 1.75 2.457 1.722 3.888-.001.058-.004.114-.007.17a1.2 1.2 0 0 1 .432.656c.027.132.042.2.046.23.027.01.085.037.204.092.153.07.36.236.502.47.115-.435.183-.933.195-1.509.04-1.845-1.681-3.846-2.065-5.013-.383-1.167-.135-1.376-.288-2.515C10.2 1.759 9.385.914 7.999.914Z" fill="#333" style="stroke-width:.472092;fill:#e0e0e0;fill-opacity:1"/></svg>
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index ea109a8816..d4f82cc81e 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -458,7 +458,7 @@ Point2i DisplayServerX11::mouse_get_position() const {
return Vector2i();
}
-MouseButton DisplayServerX11::mouse_get_button_state() const {
+BitField<MouseButtonMask> DisplayServerX11::mouse_get_button_state() const {
return last_button_state;
}
@@ -1420,31 +1420,35 @@ void DisplayServerX11::window_set_mouse_passthrough(const Vector<Vector2> &p_reg
_THREAD_SAFE_METHOD_
ERR_FAIL_COND(!windows.has(p_window));
- const WindowData &wd = windows[p_window];
+ windows[p_window].mpath = p_region;
+ _update_window_mouse_passthrough(p_window);
+}
+
+void DisplayServerX11::_update_window_mouse_passthrough(WindowID p_window) {
+ ERR_FAIL_COND(!windows.has(p_window));
+
+ const Vector<Vector2> region_path = windows[p_window].mpath;
int event_base, error_base;
const Bool ext_okay = XShapeQueryExtension(x11_display, &event_base, &error_base);
if (ext_okay) {
- Region region;
- if (p_region.size() == 0) {
- region = XCreateRegion();
- XRectangle rect;
- rect.x = 0;
- rect.y = 0;
- rect.width = window_get_size_with_decorations(p_window).x;
- rect.height = window_get_size_with_decorations(p_window).y;
- XUnionRectWithRegion(&rect, region, region);
+ if (windows[p_window].mpass) {
+ Region region = XCreateRegion();
+ XShapeCombineRegion(x11_display, windows[p_window].x11_window, ShapeInput, 0, 0, region, ShapeSet);
+ XDestroyRegion(region);
+ } else if (region_path.size() == 0) {
+ XShapeCombineMask(x11_display, windows[p_window].x11_window, ShapeInput, 0, 0, None, ShapeSet);
} else {
- XPoint *points = (XPoint *)memalloc(sizeof(XPoint) * p_region.size());
- for (int i = 0; i < p_region.size(); i++) {
- points[i].x = p_region[i].x;
- points[i].y = p_region[i].y;
+ XPoint *points = (XPoint *)memalloc(sizeof(XPoint) * region_path.size());
+ for (int i = 0; i < region_path.size(); i++) {
+ points[i].x = region_path[i].x;
+ points[i].y = region_path[i].y;
}
- region = XPolygonRegion(points, p_region.size(), EvenOddRule);
+ Region region = XPolygonRegion(points, region_path.size(), EvenOddRule);
memfree(points);
+ XShapeCombineRegion(x11_display, windows[p_window].x11_window, ShapeInput, 0, 0, region, ShapeSet);
+ XDestroyRegion(region);
}
- XShapeCombineRegion(x11_display, wd.x11_window, ShapeInput, 0, 0, region, ShapeSet);
- XDestroyRegion(region);
}
}
@@ -2312,6 +2316,7 @@ void DisplayServerX11::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo
window_set_size(window_get_size(p_window), p_window);
wd.borderless = p_enabled;
+ _update_window_mouse_passthrough(p_window);
} break;
case WINDOW_FLAG_ALWAYS_ON_TOP: {
ERR_FAIL_COND_MSG(wd.transient_parent != INVALID_WINDOW_ID, "Can't make a window transient if the 'on top' flag is active.");
@@ -2345,6 +2350,10 @@ void DisplayServerX11::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo
case WINDOW_FLAG_NO_FOCUS: {
wd.no_focus = p_enabled;
} break;
+ case WINDOW_FLAG_MOUSE_PASSTHROUGH: {
+ wd.mpass = p_enabled;
+ _update_window_mouse_passthrough(p_window);
+ } break;
case WINDOW_FLAG_POPUP: {
XWindowAttributes xwa;
XSync(x11_display, False);
@@ -2398,6 +2407,9 @@ bool DisplayServerX11::window_get_flag(WindowFlags p_flag, WindowID p_window) co
case WINDOW_FLAG_NO_FOCUS: {
return wd.no_focus;
} break;
+ case WINDOW_FLAG_MOUSE_PASSTHROUGH: {
+ return wd.mpass;
+ } break;
case WINDOW_FLAG_POPUP: {
return wd.is_popup;
} break;
@@ -2843,13 +2855,13 @@ void DisplayServerX11::_get_key_modifier_state(unsigned int p_x11_state, Ref<Inp
state->set_meta_pressed((p_x11_state & Mod4Mask));
}
-MouseButton DisplayServerX11::_get_mouse_button_state(MouseButton p_x11_button, int p_x11_type) {
- MouseButton mask = mouse_button_to_mask(p_x11_button);
+BitField<MouseButtonMask> DisplayServerX11::_get_mouse_button_state(MouseButton p_x11_button, int p_x11_type) {
+ MouseButtonMask mask = mouse_button_to_mask(p_x11_button);
if (p_x11_type == ButtonPress) {
- last_button_state |= mask;
+ last_button_state.set_flag(mask);
} else {
- last_button_state &= ~mask;
+ last_button_state.clear_flag(mask);
}
return last_button_state;
@@ -4171,13 +4183,13 @@ void DisplayServerX11::process_events() {
if (xi.pressure_supported) {
mm->set_pressure(xi.pressure);
} else {
- mm->set_pressure(bool(mouse_get_button_state() & MouseButton::MASK_LEFT) ? 1.0f : 0.0f);
+ mm->set_pressure(bool(mouse_get_button_state().has_flag(MouseButtonMask::LEFT)) ? 1.0f : 0.0f);
}
mm->set_tilt(xi.tilt);
mm->set_pen_inverted(xi.pen_inverted);
_get_key_modifier_state(event.xmotion.state, mm);
- mm->set_button_mask((MouseButton)mouse_get_button_state());
+ mm->set_button_mask(mouse_get_button_state());
mm->set_position(pos);
mm->set_global_position(pos);
mm->set_velocity(Input::get_singleton()->get_last_mouse_velocity());
@@ -4564,18 +4576,20 @@ DisplayServer *DisplayServerX11::create_func(const String &p_rendering_driver, W
if (r_error != OK) {
if (p_rendering_driver == "vulkan") {
String executable_name = OS::get_singleton()->get_executable_path().get_file();
- OS::get_singleton()->alert("Your video card driver does not support the selected Vulkan version.\n"
- "Please try updating your GPU driver or try using the OpenGL 3 driver.\n"
- "You can enable the OpenGL 3 driver by starting the engine from the\n"
- "command line with the command:\n'./" +
- executable_name + " --rendering-driver opengl3'.\n "
- "If you have updated your graphics drivers recently, try rebooting.",
- "Unable to initialize Video driver");
+ OS::get_singleton()->alert(
+ vformat("Your video card drivers seem not to support the required Vulkan version.\n\n"
+ "If possible, consider updating your video card drivers or using the OpenGL 3 driver.\n\n"
+ "You can enable the OpenGL 3 driver by starting the engine from the\n"
+ "command line with the command:\n'%s --rendering-driver opengl3'\n\n"
+ "If you recently updated your video card drivers, try rebooting.",
+ executable_name),
+ "Unable to initialize Vulkan video driver");
} else {
- OS::get_singleton()->alert("Your video card driver does not support the selected OpenGL version.\n"
- "Please try updating your GPU driver.\n"
- "If you have updated your graphics drivers recently, try rebooting.",
- "Unable to initialize Video driver");
+ OS::get_singleton()->alert(
+ "Your video card drivers seem not to support the required OpenGL 3.3 version.\n\n"
+ "If possible, consider updating your video card drivers.\n\n"
+ "If you recently updated your video card drivers, try rebooting.",
+ "Unable to initialize OpenGL video driver");
}
}
return ds;
diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h
index 2fac446fcf..437766d953 100644
--- a/platform/linuxbsd/x11/display_server_x11.h
+++ b/platform/linuxbsd/x11/display_server_x11.h
@@ -149,6 +149,8 @@ class DisplayServerX11 : public DisplayServer {
Callable input_text_callback;
Callable drop_files_callback;
+ Vector<Vector2> mpath;
+
WindowID transient_parent = INVALID_WINDOW_ID;
HashSet<WindowID> transient_children;
@@ -169,6 +171,7 @@ class DisplayServerX11 : public DisplayServer {
bool maximized = false;
bool is_popup = false;
bool layered_window = false;
+ bool mpass = false;
Rect2i parent_safe_rect;
@@ -205,7 +208,7 @@ class DisplayServerX11 : public DisplayServer {
Point2i last_click_pos = Point2i(-100, -100);
uint64_t last_click_ms = 0;
MouseButton last_click_button_index = MouseButton::NONE;
- MouseButton last_button_state = MouseButton::NONE;
+ BitField<MouseButtonMask> last_button_state;
bool app_focused = false;
uint64_t time_since_no_focus = 0;
@@ -234,7 +237,7 @@ class DisplayServerX11 : public DisplayServer {
Rect2i _screen_get_rect(int p_screen) const;
- MouseButton _get_mouse_button_state(MouseButton p_x11_button, int p_x11_type);
+ BitField<MouseButtonMask> _get_mouse_button_state(MouseButton p_x11_button, int p_x11_type);
void _get_key_modifier_state(unsigned int p_x11_state, Ref<InputEventWithModifiers> state);
void _flush_mouse_motion();
@@ -245,6 +248,7 @@ class DisplayServerX11 : public DisplayServer {
Atom _process_selection_request_target(Atom p_target, Window p_requestor, Atom p_property, Atom p_selection) const;
void _handle_selection_request_event(XSelectionRequestEvent *p_event) const;
+ void _update_window_mouse_passthrough(WindowID p_window);
String _clipboard_get_impl(Atom p_source, Window x11_window, Atom target) const;
String _clipboard_get(Atom p_source, Window x11_window) const;
@@ -344,7 +348,7 @@ public:
virtual void warp_mouse(const Point2i &p_position) override;
virtual Point2i mouse_get_position() const override;
- virtual MouseButton mouse_get_button_state() const override;
+ virtual BitField<MouseButtonMask> mouse_get_button_state() const override;
virtual void clipboard_set(const String &p_text) override;
virtual String clipboard_get() const override;
diff --git a/platform/macos/README.md b/platform/macos/README.md
index feead80736..205c59e05d 100644
--- a/platform/macos/README.md
+++ b/platform/macos/README.md
@@ -11,7 +11,7 @@ packaging macOS export templates.
## Documentation
-- [Compiling for macOS](https://docs.godotengine.org/en/latest/development/compiling/compiling_for_macos.html)
+- [Compiling for macOS](https://docs.godotengine.org/en/latest/contributing/development/compiling/compiling_for_macos.html)
- Instructions on building this platform port from source.
- [Exporting for macOS](https://docs.godotengine.org/en/latest/tutorials/export/exporting_for_macos.html)
- Instructions on using the compiled export templates to export a project.
diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h
index 2927c67890..4873db4b64 100644
--- a/platform/macos/display_server_macos.h
+++ b/platform/macos/display_server_macos.h
@@ -114,6 +114,7 @@ public:
bool resize_disabled = false;
bool no_focus = false;
bool is_popup = false;
+ bool mpass = false;
Rect2i parent_safe_rect;
};
@@ -154,7 +155,7 @@ private:
CGEventSourceRef event_source;
MouseMode mouse_mode = MOUSE_MODE_VISIBLE;
- MouseButton last_button_state = MouseButton::NONE;
+ BitField<MouseButtonMask> last_button_state;
bool drop_events = false;
bool in_dispatch_input_event = false;
@@ -317,8 +318,8 @@ public:
bool update_mouse_wrap(WindowData &p_wd, NSPoint &r_delta, NSPoint &r_mpos, NSTimeInterval p_timestamp);
virtual void warp_mouse(const Point2i &p_position) override;
virtual Point2i mouse_get_position() const override;
- void mouse_set_button_state(MouseButton p_state);
- virtual MouseButton mouse_get_button_state() const override;
+ void mouse_set_button_state(BitField<MouseButtonMask> p_state);
+ virtual BitField<MouseButtonMask> mouse_get_button_state() const override;
virtual void clipboard_set(const String &p_text) override;
virtual String clipboard_get() const override;
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index 3bbb3e35fb..d992467042 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -166,6 +166,17 @@ DisplayServerMacOS::WindowID DisplayServerMacOS::_create_window(WindowMode p_mod
layer.contentsScale = scale;
}
+ NSColor *bg_color = [NSColor windowBackgroundColor];
+ Color _bg_color;
+ if (_get_window_early_clear_override(_bg_color)) {
+ bg_color = [NSColor colorWithCalibratedRed:_bg_color.r green:_bg_color.g blue:_bg_color.b alpha:1.f];
+ }
+
+ [wd.window_object setBackgroundColor:bg_color];
+ if (layer) {
+ [layer setBackgroundColor:bg_color.CGColor];
+ }
+
#if defined(VULKAN_ENABLED)
if (context_vulkan) {
Error err = context_vulkan->window_create(window_id_counter, p_vsync_mode, wd.window_view, p_rect.size.width, p_rect.size.height);
@@ -273,12 +284,17 @@ void DisplayServerMacOS::_set_window_per_pixel_transparency_enabled(bool p_enabl
#endif
wd.layered_window = true;
} else {
- [wd.window_object setBackgroundColor:[NSColor colorWithCalibratedWhite:1 alpha:1]];
+ NSColor *bg_color = [NSColor windowBackgroundColor];
+ Color _bg_color;
+ if (_get_window_early_clear_override(_bg_color)) {
+ bg_color = [NSColor colorWithCalibratedRed:_bg_color.r green:_bg_color.g blue:_bg_color.b alpha:1.f];
+ }
+ [wd.window_object setBackgroundColor:bg_color];
[wd.window_object setOpaque:YES];
[wd.window_object setHasShadow:YES];
CALayer *layer = [(NSView *)wd.window_view layer];
if (layer) {
- [layer setBackgroundColor:[NSColor colorWithCalibratedWhite:1 alpha:1].CGColor];
+ [layer setBackgroundColor:bg_color.CGColor];
[layer setOpaque:YES];
}
#if defined(GLES3_ENABLED)
@@ -2034,11 +2050,11 @@ Point2i DisplayServerMacOS::mouse_get_position() const {
return Vector2i();
}
-void DisplayServerMacOS::mouse_set_button_state(MouseButton p_state) {
+void DisplayServerMacOS::mouse_set_button_state(BitField<MouseButtonMask> p_state) {
last_button_state = p_state;
}
-MouseButton DisplayServerMacOS::mouse_get_button_state() const {
+BitField<MouseButtonMask> DisplayServerMacOS::mouse_get_button_state() const {
return last_button_state;
}
@@ -2958,6 +2974,9 @@ void DisplayServerMacOS::window_set_flag(WindowFlags p_flag, bool p_enabled, Win
case WINDOW_FLAG_NO_FOCUS: {
wd.no_focus = p_enabled;
} break;
+ case WINDOW_FLAG_MOUSE_PASSTHROUGH: {
+ wd.mpass = p_enabled;
+ } break;
case WINDOW_FLAG_POPUP: {
ERR_FAIL_COND_MSG(p_window == MAIN_WINDOW_ID, "Main window can't be popup.");
ERR_FAIL_COND_MSG([wd.window_object isVisible] && (wd.is_popup != p_enabled), "Popup flag can't changed while window is opened.");
@@ -2997,6 +3016,9 @@ bool DisplayServerMacOS::window_get_flag(WindowFlags p_flag, WindowID p_window)
case WINDOW_FLAG_NO_FOCUS: {
return wd.no_focus;
} break;
+ case WINDOW_FLAG_MOUSE_PASSTHROUGH: {
+ return wd.mpass;
+ } break;
case WINDOW_FLAG_POPUP: {
return wd.is_popup;
} break;
@@ -3471,7 +3493,11 @@ void DisplayServerMacOS::process_events() {
for (KeyValue<WindowID, WindowData> &E : windows) {
WindowData &wd = E.value;
- if (wd.mpath.size() > 0) {
+ if (wd.mpass) {
+ if (![wd.window_object ignoresMouseEvents]) {
+ [wd.window_object setIgnoresMouseEvents:YES];
+ }
+ } else if (wd.mpath.size() > 0) {
update_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream]);
if (Geometry2D::is_point_in_polygon(wd.mouse_pos, wd.mpath)) {
if ([wd.window_object ignoresMouseEvents]) {
@@ -3581,18 +3607,18 @@ DisplayServer *DisplayServerMacOS::create_func(const String &p_rendering_driver,
} else {
executable_command = vformat("open %s --args --rendering-driver opengl3", OS::get_singleton()->get_bundle_resource_dir().path_join("../..").simplify_path());
}
- OS::get_singleton()->alert("Your video card driver does not support the selected Vulkan version.\n"
- "Please try updating your GPU driver or try using the OpenGL 3 driver.\n"
- "You can enable the OpenGL 3 driver by starting the engine from the\n"
- "command line with the command: '" +
- executable_command + "'.\n"
- "If you have updated your graphics drivers recently, try rebooting.",
- "Unable to initialize Video driver");
+ OS::get_singleton()->alert(
+ vformat("Your video card drivers seem not to support the required Vulkan version.\n\n"
+ "If possible, consider updating your macOS version or using the OpenGL 3 driver.\n\n"
+ "You can enable the OpenGL 3 driver by starting the engine from the\n"
+ "command line with the command:\n'%s'",
+ executable_command),
+ "Unable to initialize Vulkan video driver");
} else {
- OS::get_singleton()->alert("Your video card driver does not support the selected OpenGL version.\n"
- "Please try updating your GPU driver.\n"
- "If you have updated your graphics drivers recently, try rebooting.",
- "Unable to initialize Video driver");
+ OS::get_singleton()->alert(
+ "Your video card drivers seem not to support the required OpenGL 3.3 version.\n\n"
+ "If possible, consider updating your macOS version.",
+ "Unable to initialize OpenGL video driver");
}
}
return ds;
diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp
index 46485da783..73fbd45400 100644
--- a/platform/macos/export/export_plugin.cpp
+++ b/platform/macos/export/export_plugin.cpp
@@ -38,8 +38,14 @@
#include "core/string/translation.h"
#include "editor/editor_node.h"
#include "editor/editor_paths.h"
+#include "editor/editor_scale.h"
+#include "platform/macos/logo_svg.gen.h"
+#include "platform/macos/run_icon_svg.gen.h"
-#include "modules/modules_enabled.gen.h" // For regex.
+#include "modules/modules_enabled.gen.h" // For svg and regex.
+#ifdef MODULE_SVG_ENABLED
+#include "modules/svg/image_loader_svg.h"
+#endif
void EditorExportPlatformMacOS::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const {
if (p_preset->get("texture_format/s3tc")) {
@@ -207,6 +213,23 @@ void EditorExportPlatformMacOS::get_export_options(List<ExportOption> *r_options
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc2"), false));
+
+ String run_script = "#!/usr/bin/env bash\n"
+ "unzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\"\n"
+ "open \"{temp_dir}/{exe_name}.app\" --args {cmd_args}";
+
+ String cleanup_script = "#!/usr/bin/env bash\n"
+ "kill $(pgrep -x -f \"{temp_dir}/{exe_name}.app/Contents/MacOS/{exe_name} {cmd_args}\")\n"
+ "rm -rf \"{temp_dir}\"";
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "ssh_remote_deploy/enabled"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/host"), "user@host_ip"));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/port"), "22"));
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/extra_args_ssh", PROPERTY_HINT_MULTILINE_TEXT), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/extra_args_scp", PROPERTY_HINT_MULTILINE_TEXT), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/run_script", PROPERTY_HINT_MULTILINE_TEXT), run_script));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/cleanup_script", PROPERTY_HINT_MULTILINE_TEXT), cleanup_script));
}
void _rgba8_to_packbits_encode(int p_ch, int p_size, Vector<uint8_t> &p_source, Vector<uint8_t> &p_dest) {
@@ -993,7 +1016,7 @@ Error EditorExportPlatformMacOS::_create_dmg(const String &p_dmg_path, const Str
return OK;
}
-bool EditorExportPlatformMacOS::is_shbang(const String &p_path) const {
+bool EditorExportPlatformMacOS::is_shebang(const String &p_path) const {
Ref<FileAccess> fb = FileAccess::open(p_path, FileAccess::READ);
ERR_FAIL_COND_V_MSG(fb.is_null(), false, vformat("Can't open file: \"%s\".", p_path));
uint16_t magic = fb->get_16();
@@ -1001,7 +1024,7 @@ bool EditorExportPlatformMacOS::is_shbang(const String &p_path) const {
}
bool EditorExportPlatformMacOS::is_executable(const String &p_path) const {
- return MachO::is_macho(p_path) || LipO::is_lipo(p_path) || is_shbang(p_path);
+ return MachO::is_macho(p_path) || LipO::is_lipo(p_path) || is_shebang(p_path);
}
Error EditorExportPlatformMacOS::_export_debug_script(const Ref<EditorExportPreset> &p_preset, const String &p_app_name, const String &p_pkg_name, const String &p_path) {
@@ -1082,7 +1105,6 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
} else {
pkg_name = "Unnamed";
}
-
pkg_name = OS::get_singleton()->get_safe_dir_name(pkg_name);
String export_format;
@@ -1684,7 +1706,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
zlib_filefunc_def io_dst = zipio_create_io(&io_fa_dst);
zipFile zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io_dst);
- _zip_folder_recursive(zip, tmp_base_path_name, "", pkg_name);
+ zip_folder_recursive(zip, tmp_base_path_name, "", pkg_name);
zipClose(zip, nullptr);
}
@@ -1723,119 +1745,6 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
return err;
}
-void EditorExportPlatformMacOS::_zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name) {
- String dir = p_folder.is_empty() ? p_root_path : p_root_path.path_join(p_folder);
-
- Ref<DirAccess> da = DirAccess::open(dir);
- da->list_dir_begin();
- String f = da->get_next();
- while (!f.is_empty()) {
- if (f == "." || f == "..") {
- f = da->get_next();
- continue;
- }
- if (da->is_link(f)) {
- OS::DateTime dt = OS::get_singleton()->get_datetime();
-
- zip_fileinfo zipfi;
- 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
- // 0000644: permissions rw-r--r--
- uint32_t _mode = 0120644;
- zipfi.external_fa = (_mode << 16L) | !(_mode & 0200);
- zipfi.internal_fa = 0;
-
- zipOpenNewFileInZip4(p_zip,
- p_folder.path_join(f).utf8().get_data(),
- &zipfi,
- nullptr,
- 0,
- nullptr,
- 0,
- nullptr,
- Z_DEFLATED,
- Z_DEFAULT_COMPRESSION,
- 0,
- -MAX_WBITS,
- DEF_MEM_LEVEL,
- Z_DEFAULT_STRATEGY,
- nullptr,
- 0,
- 0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions
- 0);
-
- String target = da->read_link(f);
- zipWriteInFileInZip(p_zip, target.utf8().get_data(), target.utf8().size());
- zipCloseFileInZip(p_zip);
- } else if (da->current_is_dir()) {
- _zip_folder_recursive(p_zip, p_root_path, p_folder.path_join(f), p_pkg_name);
- } else {
- OS::DateTime dt = OS::get_singleton()->get_datetime();
-
- zip_fileinfo zipfi;
- 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
- // 0000644: permissions rw-r--r--
- uint32_t _mode = (is_executable(dir.path_join(f)) ? 0100755 : 0100644);
- zipfi.external_fa = (_mode << 16L) | !(_mode & 0200);
- zipfi.internal_fa = 0;
-
- zipOpenNewFileInZip4(p_zip,
- p_folder.path_join(f).utf8().get_data(),
- &zipfi,
- nullptr,
- 0,
- nullptr,
- 0,
- nullptr,
- Z_DEFLATED,
- Z_DEFAULT_COMPRESSION,
- 0,
- -MAX_WBITS,
- DEF_MEM_LEVEL,
- Z_DEFAULT_STRATEGY,
- nullptr,
- 0,
- 0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions
- 0);
-
- Ref<FileAccess> fa = FileAccess::open(dir.path_join(f), FileAccess::READ);
- if (fa.is_null()) {
- add_message(EXPORT_MESSAGE_ERROR, TTR("ZIP Creation"), vformat(TTR("Could not open file to read from path \"%s\"."), dir.path_join(f)));
- return;
- }
- const int bufsize = 16384;
- uint8_t buf[bufsize];
-
- while (true) {
- uint64_t got = fa->get_buffer(buf, bufsize);
- if (got == 0) {
- break;
- }
- zipWriteInFileInZip(p_zip, buf, got);
- }
-
- zipCloseFileInZip(p_zip);
- }
- f = da->get_next();
- }
- da->list_dir_end();
-}
-
bool EditorExportPlatformMacOS::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
String err;
bool valid = false;
@@ -1937,6 +1846,10 @@ bool EditorExportPlatformMacOS::has_valid_project_configuration(const Ref<Editor
}
}
}
+ if (notary_tool == 2 && p_preset->get("notarization/apple_team_id") == "") {
+ err += TTR("Notarization: Apple Team ID not specified.") + "\n";
+ valid = false;
+ }
} else if (notary_tool == 1) {
if (p_preset->get("notarization/api_uuid") == "") {
err += TTR("Notarization: App Store Connect issuer ID name not specified.") + "\n";
@@ -2008,9 +1921,242 @@ bool EditorExportPlatformMacOS::has_valid_project_configuration(const Ref<Editor
return valid;
}
-EditorExportPlatformMacOS::EditorExportPlatformMacOS() {
- logo = ImageTexture::create_from_image(memnew(Image(_macos_logo)));
+Ref<Texture2D> EditorExportPlatformMacOS::get_run_icon() const {
+ return run_icon;
+}
+
+bool EditorExportPlatformMacOS::poll_export() {
+ Ref<EditorExportPreset> preset;
+
+ for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
+ Ref<EditorExportPreset> ep = EditorExport::get_singleton()->get_export_preset(i);
+ if (ep->is_runnable() && ep->get_platform() == this) {
+ preset = ep;
+ break;
+ }
+ }
+
+ int prev = menu_options;
+ menu_options = (preset.is_valid() && preset->get("ssh_remote_deploy/enabled").operator bool());
+ if (ssh_pid != 0 || !cleanup_commands.is_empty()) {
+ if (menu_options == 0) {
+ cleanup();
+ } else {
+ menu_options += 1;
+ }
+ }
+ return menu_options != prev;
+}
+
+Ref<ImageTexture> EditorExportPlatformMacOS::get_option_icon(int p_index) const {
+ return p_index == 1 ? stop_icon : EditorExportPlatform::get_option_icon(p_index);
+}
+
+int EditorExportPlatformMacOS::get_options_count() const {
+ return menu_options;
+}
+
+String EditorExportPlatformMacOS::get_option_label(int p_index) const {
+ return (p_index) ? TTR("Stop and uninstall") : TTR("Run on remote macOS system");
+}
+
+String EditorExportPlatformMacOS::get_option_tooltip(int p_index) const {
+ return (p_index) ? TTR("Stop and uninstall running project from the remote system") : TTR("Run exported project on remote macOS system");
+}
+
+void EditorExportPlatformMacOS::cleanup() {
+ if (ssh_pid != 0 && OS::get_singleton()->is_process_running(ssh_pid)) {
+ print_line("Terminating connection...");
+ OS::get_singleton()->kill(ssh_pid);
+ OS::get_singleton()->delay_usec(1000);
+ }
+
+ if (!cleanup_commands.is_empty()) {
+ print_line("Stopping and deleting previous version...");
+ for (const SSHCleanupCommand &cmd : cleanup_commands) {
+ if (cmd.wait) {
+ ssh_run_on_remote(cmd.host, cmd.port, cmd.ssh_args, cmd.cmd_args);
+ } else {
+ ssh_run_on_remote_no_wait(cmd.host, cmd.port, cmd.ssh_args, cmd.cmd_args);
+ }
+ }
+ }
+ ssh_pid = 0;
+ cleanup_commands.clear();
+}
+
+Error EditorExportPlatformMacOS::run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) {
+ cleanup();
+ if (p_device) { // Stop command, cleanup only.
+ return OK;
+ }
+
+ EditorProgress ep("run", TTR("Running..."), 5);
+
+ const String dest = EditorPaths::get_singleton()->get_cache_dir().path_join("macos");
+ Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ if (!da->dir_exists(dest)) {
+ Error err = da->make_dir_recursive(dest);
+ if (err != OK) {
+ EditorNode::get_singleton()->show_warning(TTR("Could not create temp directory:") + "\n" + dest);
+ return err;
+ }
+ }
+
+ String pkg_name;
+ if (String(ProjectSettings::get_singleton()->get("application/config/name")) != "") {
+ pkg_name = String(ProjectSettings::get_singleton()->get("application/config/name"));
+ } else {
+ pkg_name = "Unnamed";
+ }
+ pkg_name = OS::get_singleton()->get_safe_dir_name(pkg_name);
+
+ String host = p_preset->get("ssh_remote_deploy/host").operator String();
+ String port = p_preset->get("ssh_remote_deploy/port").operator String();
+ if (port.is_empty()) {
+ port = "22";
+ }
+ Vector<String> extra_args_ssh = p_preset->get("ssh_remote_deploy/extra_args_ssh").operator String().split(" ");
+ Vector<String> extra_args_scp = p_preset->get("ssh_remote_deploy/extra_args_scp").operator String().split(" ");
+
+ const String basepath = dest.path_join("tmp_macos_export");
+
+#define CLEANUP_AND_RETURN(m_err) \
+ { \
+ if (da->file_exists(basepath + ".zip")) { \
+ da->remove(basepath + ".zip"); \
+ } \
+ if (da->file_exists(basepath + "_start.sh")) { \
+ da->remove(basepath + "_start.sh"); \
+ } \
+ if (da->file_exists(basepath + "_clean.sh")) { \
+ da->remove(basepath + "_clean.sh"); \
+ } \
+ return m_err; \
+ } \
+ ((void)0)
+
+ if (ep.step(TTR("Exporting project..."), 1)) {
+ return ERR_SKIP;
+ }
+ Error err = export_project(p_preset, true, basepath + ".zip", p_debug_flags);
+ if (err != OK) {
+ DirAccess::remove_file_or_error(basepath + ".zip");
+ return err;
+ }
+
+ String cmd_args;
+ {
+ Vector<String> cmd_args_list;
+ gen_debug_flags(cmd_args_list, p_debug_flags);
+ for (int i = 0; i < cmd_args_list.size(); i++) {
+ if (i != 0) {
+ cmd_args += " ";
+ }
+ cmd_args += cmd_args_list[i];
+ }
+ }
+
+ const bool use_remote = (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) || (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT);
+ int dbg_port = EditorSettings::get_singleton()->get("network/debug/remote_port");
+
+ print_line("Creating temporary directory...");
+ ep.step(TTR("Creating temporary directory..."), 2);
+ String temp_dir;
+ err = ssh_run_on_remote(host, port, extra_args_ssh, "mktemp -d", &temp_dir);
+ if (err != OK || temp_dir.is_empty()) {
+ CLEANUP_AND_RETURN(err);
+ }
+
+ print_line("Uploading archive...");
+ ep.step(TTR("Uploading archive..."), 3);
+ err = ssh_push_to_remote(host, port, extra_args_scp, basepath + ".zip", temp_dir);
+ if (err != OK) {
+ CLEANUP_AND_RETURN(err);
+ }
+
+ {
+ String run_script = p_preset->get("ssh_remote_deploy/run_script");
+ run_script = run_script.replace("{temp_dir}", temp_dir);
+ run_script = run_script.replace("{archive_name}", basepath.get_file() + ".zip");
+ run_script = run_script.replace("{exe_name}", pkg_name);
+ run_script = run_script.replace("{cmd_args}", cmd_args);
+
+ Ref<FileAccess> f = FileAccess::open(basepath + "_start.sh", FileAccess::WRITE);
+ if (f.is_null()) {
+ CLEANUP_AND_RETURN(err);
+ }
+
+ f->store_string(run_script);
+ }
+
+ {
+ String clean_script = p_preset->get("ssh_remote_deploy/cleanup_script");
+ clean_script = clean_script.replace("{temp_dir}", temp_dir);
+ clean_script = clean_script.replace("{archive_name}", basepath.get_file() + ".zip");
+ clean_script = clean_script.replace("{exe_name}", pkg_name);
+ clean_script = clean_script.replace("{cmd_args}", cmd_args);
+
+ Ref<FileAccess> f = FileAccess::open(basepath + "_clean.sh", FileAccess::WRITE);
+ if (f.is_null()) {
+ CLEANUP_AND_RETURN(err);
+ }
+
+ f->store_string(clean_script);
+ }
+
+ print_line("Uploading scripts...");
+ ep.step(TTR("Uploading scripts..."), 4);
+ err = ssh_push_to_remote(host, port, extra_args_scp, basepath + "_start.sh", temp_dir);
+ if (err != OK) {
+ CLEANUP_AND_RETURN(err);
+ }
+ err = ssh_run_on_remote(host, port, extra_args_ssh, vformat("chmod +x \"%s/%s\"", temp_dir, basepath.get_file() + "_start.sh"));
+ if (err != OK || temp_dir.is_empty()) {
+ CLEANUP_AND_RETURN(err);
+ }
+ err = ssh_push_to_remote(host, port, extra_args_scp, basepath + "_clean.sh", temp_dir);
+ if (err != OK) {
+ CLEANUP_AND_RETURN(err);
+ }
+ err = ssh_run_on_remote(host, port, extra_args_ssh, vformat("chmod +x \"%s/%s\"", temp_dir, basepath.get_file() + "_clean.sh"));
+ if (err != OK || temp_dir.is_empty()) {
+ CLEANUP_AND_RETURN(err);
+ }
+
+ print_line("Starting project...");
+ ep.step(TTR("Starting project..."), 5);
+ err = ssh_run_on_remote_no_wait(host, port, extra_args_ssh, vformat("\"%s/%s\"", temp_dir, basepath.get_file() + "_start.sh"), &ssh_pid, (use_remote) ? dbg_port : -1);
+ if (err != OK) {
+ CLEANUP_AND_RETURN(err);
+ }
+
+ cleanup_commands.clear();
+ cleanup_commands.push_back(SSHCleanupCommand(host, port, extra_args_ssh, vformat("\"%s/%s\"", temp_dir, basepath.get_file() + "_clean.sh")));
+
+ print_line("Project started.");
+
+ CLEANUP_AND_RETURN(OK);
+#undef CLEANUP_AND_RETURN
}
-EditorExportPlatformMacOS::~EditorExportPlatformMacOS() {
+EditorExportPlatformMacOS::EditorExportPlatformMacOS() {
+#ifdef MODULE_SVG_ENABLED
+ Ref<Image> img = memnew(Image);
+ const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE);
+
+ ImageLoaderSVG img_loader;
+ img_loader.create_image_from_string(img, _macos_logo_svg, EDSCALE, upsample, false);
+ logo = ImageTexture::create_from_image(img);
+
+ img_loader.create_image_from_string(img, _macos_run_icon_svg, EDSCALE, upsample, false);
+ run_icon = ImageTexture::create_from_image(img);
+#endif
+
+ Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme();
+ if (theme.is_valid()) {
+ stop_icon = theme->get_icon(SNAME("Stop"), SNAME("EditorIcons"));
+ } else {
+ stop_icon.instantiate();
+ }
}
diff --git a/platform/macos/export/export_plugin.h b/platform/macos/export/export_plugin.h
index 7de6ddf304..c10192949c 100644
--- a/platform/macos/export/export_plugin.h
+++ b/platform/macos/export/export_plugin.h
@@ -36,12 +36,10 @@
#include "core/io/file_access.h"
#include "core/io/marshalls.h"
#include "core/io/resource_saver.h"
-#include "core/io/zip_io.h"
#include "core/os/os.h"
#include "core/version.h"
#include "editor/editor_settings.h"
#include "editor/export/editor_export.h"
-#include "platform/macos/logo.gen.h"
#include <sys/stat.h>
@@ -52,6 +50,30 @@ class EditorExportPlatformMacOS : public EditorExportPlatform {
Ref<ImageTexture> logo;
+ struct SSHCleanupCommand {
+ String host;
+ String port;
+ Vector<String> ssh_args;
+ String cmd_args;
+ bool wait = false;
+
+ SSHCleanupCommand(){};
+ SSHCleanupCommand(const String &p_host, const String &p_port, const Vector<String> &p_ssh_arg, const String &p_cmd_args, bool p_wait = false) {
+ host = p_host;
+ port = p_port;
+ ssh_args = p_ssh_arg;
+ cmd_args = p_cmd_args;
+ wait = p_wait;
+ };
+ };
+
+ Ref<ImageTexture> run_icon;
+ Ref<ImageTexture> stop_icon;
+
+ Vector<SSHCleanupCommand> cleanup_commands;
+ OS::ProcessID ssh_pid = 0;
+ int menu_options = 0;
+
void _fix_plist(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &plist, const String &p_binary);
void _make_icon(const Ref<EditorExportPreset> &p_preset, const Ref<Image> &p_icon, Vector<uint8_t> &p_data);
@@ -65,14 +87,17 @@ class EditorExportPlatformMacOS : public EditorExportPlatform {
Ref<DirAccess> &dir_access, bool p_sign_enabled, const Ref<EditorExportPreset> &p_preset,
const String &p_ent_path);
Error _create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name);
- void _zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name);
Error _export_debug_script(const Ref<EditorExportPreset> &p_preset, const String &p_app_name, const String &p_pkg_name, const String &p_path);
bool use_codesign() const { return true; }
#ifdef MACOS_ENABLED
- bool use_dmg() const { return true; }
+ bool use_dmg() const {
+ return true;
+ }
#else
- bool use_dmg() const { return false; }
+ bool use_dmg() const {
+ return false;
+ }
#endif
bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const {
@@ -97,7 +122,7 @@ class EditorExportPlatformMacOS : public EditorExportPlatform {
return true;
}
- bool is_shbang(const String &p_path) const;
+ bool is_shebang(const String &p_path) const;
protected:
virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const override;
@@ -105,9 +130,15 @@ protected:
virtual bool get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option, const HashMap<StringName, Variant> &p_options) const override;
public:
- virtual String get_name() const override { return "macOS"; }
- virtual String get_os_name() const override { return "macOS"; }
- virtual Ref<Texture2D> get_logo() const override { return logo; }
+ virtual String get_name() const override {
+ return "macOS";
+ }
+ virtual String get_os_name() const override {
+ return "macOS";
+ }
+ virtual Ref<Texture2D> get_logo() const override {
+ return logo;
+ }
virtual bool is_executable(const String &p_path) const override;
virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override {
@@ -133,8 +164,16 @@ public:
virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, HashSet<String> &p_features) override {
}
+ virtual Ref<Texture2D> get_run_icon() const override;
+ virtual bool poll_export() override;
+ virtual Ref<ImageTexture> get_option_icon(int p_index) const override;
+ virtual int get_options_count() const override;
+ virtual String get_option_label(int p_index) const override;
+ virtual String get_option_tooltip(int p_index) const override;
+ virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) override;
+ virtual void cleanup() override;
+
EditorExportPlatformMacOS();
- ~EditorExportPlatformMacOS();
};
#endif // MACOS_EXPORT_PLUGIN_H
diff --git a/platform/macos/godot_content_view.h b/platform/macos/godot_content_view.h
index c0fde4d765..ba7f15c32b 100644
--- a/platform/macos/godot_content_view.h
+++ b/platform/macos/godot_content_view.h
@@ -66,7 +66,7 @@
- (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)processMouseEvent:(NSEvent *)event index:(MouseButton)index pressed:(bool)pressed;
- (void)setWindowID:(DisplayServer::WindowID)wid;
- (void)updateLayerDelegate;
- (void)cancelComposition;
diff --git a/platform/macos/godot_content_view.mm b/platform/macos/godot_content_view.mm
index 50befc997d..337aa59a5e 100644
--- a/platform/macos/godot_content_view.mm
+++ b/platform/macos/godot_content_view.mm
@@ -371,19 +371,21 @@
ds->cursor_update_shape();
}
-- (void)processMouseEvent:(NSEvent *)event index:(MouseButton)index mask:(MouseButton)mask pressed:(bool)pressed {
+- (void)processMouseEvent:(NSEvent *)event index:(MouseButton)index pressed:(bool)pressed {
DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton();
if (!ds || !ds->has_window(window_id)) {
return;
}
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
- MouseButton last_button_state = ds->mouse_get_button_state();
+ BitField<MouseButtonMask> last_button_state = ds->mouse_get_button_state();
+
+ MouseButtonMask mask = mouse_button_to_mask(index);
if (pressed) {
- last_button_state |= mask;
+ last_button_state.set_flag(mask);
} else {
- last_button_state &= (MouseButton)~mask;
+ last_button_state.clear_flag(mask);
}
ds->mouse_set_button_state(last_button_state);
@@ -407,10 +409,10 @@
- (void)mouseDown:(NSEvent *)event {
if (([event modifierFlags] & NSEventModifierFlagControl)) {
mouse_down_control = true;
- [self processMouseEvent:event index:MouseButton::RIGHT mask:MouseButton::MASK_RIGHT pressed:true];
+ [self processMouseEvent:event index:MouseButton::RIGHT pressed:true];
} else {
mouse_down_control = false;
- [self processMouseEvent:event index:MouseButton::LEFT mask:MouseButton::MASK_LEFT pressed:true];
+ [self processMouseEvent:event index:MouseButton::LEFT pressed:true];
}
}
@@ -420,9 +422,9 @@
- (void)mouseUp:(NSEvent *)event {
if (mouse_down_control) {
- [self processMouseEvent:event index:MouseButton::RIGHT mask:MouseButton::MASK_RIGHT pressed:false];
+ [self processMouseEvent:event index:MouseButton::RIGHT pressed:false];
} else {
- [self processMouseEvent:event index:MouseButton::LEFT mask:MouseButton::MASK_LEFT pressed:false];
+ [self processMouseEvent:event index:MouseButton::LEFT pressed:false];
}
}
@@ -469,7 +471,7 @@
}
- (void)rightMouseDown:(NSEvent *)event {
- [self processMouseEvent:event index:MouseButton::RIGHT mask:MouseButton::MASK_RIGHT pressed:true];
+ [self processMouseEvent:event index:MouseButton::RIGHT pressed:true];
}
- (void)rightMouseDragged:(NSEvent *)event {
@@ -477,16 +479,16 @@
}
- (void)rightMouseUp:(NSEvent *)event {
- [self processMouseEvent:event index:MouseButton::RIGHT mask:MouseButton::MASK_RIGHT pressed:false];
+ [self processMouseEvent:event index:MouseButton::RIGHT pressed:false];
}
- (void)otherMouseDown:(NSEvent *)event {
if ((int)[event buttonNumber] == 2) {
- [self processMouseEvent:event index:MouseButton::MIDDLE mask:MouseButton::MASK_MIDDLE pressed:true];
+ [self processMouseEvent:event index:MouseButton::MIDDLE pressed:true];
} else if ((int)[event buttonNumber] == 3) {
- [self processMouseEvent:event index:MouseButton::MB_XBUTTON1 mask:MouseButton::MASK_XBUTTON1 pressed:true];
+ [self processMouseEvent:event index:MouseButton::MB_XBUTTON1 pressed:true];
} else if ((int)[event buttonNumber] == 4) {
- [self processMouseEvent:event index:MouseButton::MB_XBUTTON2 mask:MouseButton::MASK_XBUTTON2 pressed:true];
+ [self processMouseEvent:event index:MouseButton::MB_XBUTTON2 pressed:true];
} else {
return;
}
@@ -498,11 +500,11 @@
- (void)otherMouseUp:(NSEvent *)event {
if ((int)[event buttonNumber] == 2) {
- [self processMouseEvent:event index:MouseButton::MIDDLE mask:MouseButton::MASK_MIDDLE pressed:false];
+ [self processMouseEvent:event index:MouseButton::MIDDLE pressed:false];
} else if ((int)[event buttonNumber] == 3) {
- [self processMouseEvent:event index:MouseButton::MB_XBUTTON1 mask:MouseButton::MASK_XBUTTON1 pressed:false];
+ [self processMouseEvent:event index:MouseButton::MB_XBUTTON1 pressed:false];
} else if ((int)[event buttonNumber] == 4) {
- [self processMouseEvent:event index:MouseButton::MB_XBUTTON2 mask:MouseButton::MASK_XBUTTON2 pressed:false];
+ [self processMouseEvent:event index:MouseButton::MB_XBUTTON2 pressed:false];
} else {
return;
}
@@ -751,7 +753,8 @@
}
DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);
- MouseButton mask = mouse_button_to_mask(button);
+ MouseButtonMask mask = mouse_button_to_mask(button);
+ BitField<MouseButtonMask> last_button_state = ds->mouse_get_button_state();
Ref<InputEventMouseButton> sc;
sc.instantiate();
@@ -763,7 +766,7 @@
sc->set_pressed(true);
sc->set_position(wd.mouse_pos);
sc->set_global_position(wd.mouse_pos);
- MouseButton last_button_state = ds->mouse_get_button_state() | (MouseButton)mask;
+ last_button_state.set_flag(mask);
sc->set_button_mask(last_button_state);
ds->mouse_set_button_state(last_button_state);
@@ -776,7 +779,7 @@
sc->set_pressed(false);
sc->set_position(wd.mouse_pos);
sc->set_global_position(wd.mouse_pos);
- last_button_state &= (MouseButton)~mask;
+ last_button_state.clear_flag(mask);
sc->set_button_mask(last_button_state);
ds->mouse_set_button_state(last_button_state);
diff --git a/platform/macos/joypad_macos.cpp b/platform/macos/joypad_macos.cpp
index 4ea18916fe..b7b8e0604c 100644
--- a/platform/macos/joypad_macos.cpp
+++ b/platform/macos/joypad_macos.cpp
@@ -402,10 +402,10 @@ bool joypad::check_ff_features() {
return false;
}
-static HatMask process_hat_value(int p_min, int p_max, int p_value, bool p_offset_hat) {
+static BitField<HatMask> process_hat_value(int p_min, int p_max, int p_value, bool p_offset_hat) {
int range = (p_max - p_min + 1);
int value = p_value - p_min;
- HatMask hat_value = HatMask::CENTER;
+ BitField<HatMask> hat_value;
if (range == 4) {
value *= 2;
}
@@ -415,31 +415,34 @@ static HatMask process_hat_value(int p_min, int p_max, int p_value, bool p_offse
switch (value) {
case 0:
- hat_value = HatMask::UP;
+ hat_value.set_flag(HatMask::UP);
break;
case 1:
- hat_value = (HatMask::UP | HatMask::RIGHT);
+ hat_value.set_flag(HatMask::UP);
+ hat_value.set_flag(HatMask::RIGHT);
break;
case 2:
- hat_value = HatMask::RIGHT;
+ hat_value.set_flag(HatMask::RIGHT);
break;
case 3:
- hat_value = (HatMask::DOWN | HatMask::RIGHT);
+ hat_value.set_flag(HatMask::DOWN);
+ hat_value.set_flag(HatMask::RIGHT);
break;
case 4:
- hat_value = HatMask::DOWN;
+ hat_value.set_flag(HatMask::DOWN);
break;
case 5:
- hat_value = (HatMask::DOWN | HatMask::LEFT);
+ hat_value.set_flag(HatMask::DOWN);
+ hat_value.set_flag(HatMask::LEFT);
break;
case 6:
- hat_value = HatMask::LEFT;
+ hat_value.set_flag(HatMask::LEFT);
break;
case 7:
- hat_value = (HatMask::UP | HatMask::LEFT);
+ hat_value.set_flag(HatMask::UP);
+ hat_value.set_flag(HatMask::LEFT);
break;
default:
- hat_value = HatMask::CENTER;
break;
}
return hat_value;
@@ -474,7 +477,7 @@ void JoypadMacOS::process_joypads() {
for (int j = 0; j < joy.hat_elements.size(); j++) {
rec_element &elem = joy.hat_elements.write[j];
int value = joy.get_hid_element_state(&elem);
- HatMask hat_value = process_hat_value(elem.min, elem.max, value, joy.offset_hat);
+ BitField<HatMask> hat_value = process_hat_value(elem.min, elem.max, value, joy.offset_hat);
input->joy_hat(joy.id, hat_value);
}
diff --git a/platform/macos/logo.png b/platform/macos/logo.png
deleted file mode 100644
index b5a660b165..0000000000
--- a/platform/macos/logo.png
+++ /dev/null
Binary files differ
diff --git a/platform/macos/logo.svg b/platform/macos/logo.svg
new file mode 100644
index 0000000000..759583d76b
--- /dev/null
+++ b/platform/macos/logo.svg
@@ -0,0 +1 @@
+<svg height="32" viewBox="0 0 25.6 25.6" width="32" xmlns="http://www.w3.org/2000/svg"><path d="M.948 19.474V6.126c0-2.767 2.42-5.178 5.178-5.178h6.904s-2.992 7.506-2.992 13.233c0 .346.337.69.69.69h2.877c0 6.648.906 8.436 1.266 9.781H6.126c-2.75 0-5.178-2.407-5.178-5.178z" fill="#1c9ef8"/><path d="M7.162 8.312v1.496" fill="none" stroke="#323232" stroke-linecap="round" stroke-width="1.036"/><path d="M10.729 14.871c-.352 0-.69-.344-.69-.69C10.038 8.414 13.03.948 13.03.948h6.444c2.77 0 5.178 2.416 5.178 5.178v13.348c0 2.754-2.407 5.178-5.178 5.178h-4.603c-.357-1.333-1.266-2.887-1.266-9.78z" fill="#e1e6ed"/><g fill="none" stroke="#323232" stroke-linecap="round"><path d="M17.575 8.255V9.75" stroke-width="1.036"/><path d="M5.55 16.943c1.037 1.794 3.907 2.876 6.79 2.876 2.877 0 5.745-1.068 6.789-2.876" stroke-width=".69"/></g></svg>
diff --git a/platform/macos/run_icon.svg b/platform/macos/run_icon.svg
new file mode 100644
index 0000000000..c7067bb4b6
--- /dev/null
+++ b/platform/macos/run_icon.svg
@@ -0,0 +1 @@
+<svg height="16" width="16" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path style="color:#000;fill:#e0e0e0;fill-opacity:1;stroke-width:.93168;-inkscape-stroke:none" d="M4.418 1.055c-1.82 0-3.365 1.537-3.365 3.363v7.164c0 1.829 1.548 3.363 3.365 3.363h7.164c1.828 0 3.363-1.544 3.363-3.363V4.418c0-1.822-1.537-3.363-3.363-3.363H7.729Zm3.875 1.164h3.291c1.149 0 2.2 1.053 2.2 2.199v7.164c0 1.14-1.052 2.2-2.2 2.2h-2.15a8.884 8.884 0 0 1-.358-1.598c-.135.02-.487.082-.693.117a3.947 3.947 0 0 1-.049.004l-.008-.002a7.345 7.345 0 0 1-1.205-.004 7.114 7.114 0 0 1-.926-.139 6.057 6.057 0 0 1-.867-.271 3.843 3.843 0 0 1-.988-.566 3.214 3.214 0 0 1-.397-.378 2.8 2.8 0 0 1-.318-.441.558.558 0 0 1-.059-.424.564.564 0 0 1 .881-.299.56.56 0 0 1 .145.164c.083.138.188.26.312.362.096.082.2.158.307.224.12.075.243.142.371.201.285.139.583.247.89.319a5.35 5.35 0 0 0 1.282.158c.065 0 .129-.005.184-.006.056 0 .102-.005.148-.008l.096-.006c.114-.009.228-.02.31-.032.083-.013.11-.021.143-.028.099-.022.204-.058.327-.089a28.438 28.438 0 0 1-.06-1.929V8.53H6.887c.048-1.963.746-4.357 1.181-5.677.1-.293.184-.527.225-.633ZM4.973 5.03h.002a.562.562 0 0 1 .558.559v.805a.556.556 0 0 1-.558.558.56.56 0 0 1-.397-.162.565.565 0 0 1-.164-.396V5.59a.561.561 0 0 1 .559-.559Z"/><path style="color:#000;fill:#e0e0e0;fill-opacity:1;stroke-linecap:round;-inkscape-stroke:none" d="M26.117 11.467c.008.11.014.225.022.328.022.283.052.565.088.846l.012.053c1.238-.252 2.448-.829 3.011-1.803a.6.6 0 1 0-1.039-.6c-.28.486-1.161.936-2.094 1.176z" transform="translate(-15.37 .357) scale(.93168)"/><g style="fill:#e0e0e0;fill-opacity:1;stroke:none;stroke-opacity:1"><path style="color:#000;fill:#e0e0e0;fill-opacity:1;stroke:none;stroke-width:1.2;stroke-linecap:round;stroke-opacity:1;-inkscape-stroke:none" d="M27.836 5.585v.862" transform="translate(-15.37 .357) scale(.93168)"/><path style="color:#000;fill:#e0e0e0;fill-opacity:1;stroke:none;stroke-linecap:round;stroke-opacity:1;-inkscape-stroke:none" d="M27.836 4.984a.6.6 0 0 0-.6.6v.863a.6.6 0 0 0 .6.6.6.6 0 0 0 .6-.6v-.863a.6.6 0 0 0-.6-.6Z" transform="translate(-15.37 .357) scale(.93168)"/></g></svg>
diff --git a/platform/uwp/README.md b/platform/uwp/README.md
index 575f90e3c7..d69a8a8850 100644
--- a/platform/uwp/README.md
+++ b/platform/uwp/README.md
@@ -14,7 +14,7 @@ project template used for packaging the UWP export templates.
## Documentation
-- [Compiling for Universal Windows Platform](https://docs.godotengine.org/en/latest/development/compiling/compiling_for_uwp.html)
+- [Compiling for Universal Windows Platform](https://docs.godotengine.org/en/latest/contributing/development/compiling/compiling_for_uwp.html)
- Instructions on building this platform port from source.
- [Exporting for Universal Windows Platform](https://docs.godotengine.org/en/latest/tutorials/export/exporting_for_uwp.html)
- Instructions on using the compiled export templates to export a project.
diff --git a/platform/uwp/export/export_plugin.cpp b/platform/uwp/export/export_plugin.cpp
index 6eb74fd90a..f810cb0ca9 100644
--- a/platform/uwp/export/export_plugin.cpp
+++ b/platform/uwp/export/export_plugin.cpp
@@ -30,8 +30,14 @@
#include "export_plugin.h"
+#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
-#include "platform/uwp/logo.gen.h"
+#include "platform/uwp/logo_svg.gen.h"
+
+#include "modules/modules_enabled.gen.h" // For svg and regex.
+#ifdef MODULE_SVG_ENABLED
+#include "modules/svg/image_loader_svg.h"
+#endif
String EditorExportPlatformUWP::get_name() const {
return "UWP";
@@ -504,5 +510,13 @@ void EditorExportPlatformUWP::resolve_platform_feature_priorities(const Ref<Edit
}
EditorExportPlatformUWP::EditorExportPlatformUWP() {
- logo = ImageTexture::create_from_image(memnew(Image(_uwp_logo)));
+#ifdef MODULE_SVG_ENABLED
+ Ref<Image> img = memnew(Image);
+ const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE);
+
+ ImageLoaderSVG img_loader;
+ img_loader.create_image_from_string(img, _uwp_logo_svg, EDSCALE, upsample, false);
+
+ logo = ImageTexture::create_from_image(img);
+#endif
}
diff --git a/platform/uwp/logo.png b/platform/uwp/logo.png
deleted file mode 100644
index 9017a30636..0000000000
--- a/platform/uwp/logo.png
+++ /dev/null
Binary files differ
diff --git a/platform/uwp/logo.svg b/platform/uwp/logo.svg
new file mode 100644
index 0000000000..5bcbdcfcd4
--- /dev/null
+++ b/platform/uwp/logo.svg
@@ -0,0 +1 @@
+<svg height="32" width="32" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><g transform="translate(8.981 1.816)"><circle style="fill:#2f75bb;fill-opacity:1;stroke:none;stroke-width:.815427" cx="7.019" cy="14.184" r="13.825"/><path d="m-1.192 8.234 6.73-.927v6.503h-6.73Zm0 11.899 6.73.927v-6.422h-6.73Zm7.47 1.026 8.952 1.236v-7.757H6.278Zm0-13.951v6.602h8.952V5.973Z" fill="#00abed" style="fill:#fff;fill-opacity:1"/></g></svg>
diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp
index 40c19056b3..38df68c764 100644
--- a/platform/uwp/os_uwp.cpp
+++ b/platform/uwp/os_uwp.cpp
@@ -680,7 +680,7 @@ bool OS_UWP::set_environment(const String &p_var, const String &p_value) const {
return false;
}
-String OS_UWP::get_stdin_string(bool p_block) {
+String OS_UWP::get_stdin_string() {
return String();
}
diff --git a/platform/uwp/os_uwp.h b/platform/uwp/os_uwp.h
index 02a81f1a47..153656add7 100644
--- a/platform/uwp/os_uwp.h
+++ b/platform/uwp/os_uwp.h
@@ -162,7 +162,7 @@ public:
HANDLE mouse_mode_changed;
virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
- String get_stdin_string(bool p_block);
+ String get_stdin_string();
void set_mouse_mode(MouseMode p_mode);
MouseMode get_mouse_mode() const;
diff --git a/platform/web/README.md b/platform/web/README.md
index 1265ca09df..906eb508fe 100644
--- a/platform/web/README.md
+++ b/platform/web/README.md
@@ -10,7 +10,7 @@ this platform such as the html shell (web page).
## Documentation
-- [Compiling for the Web](https://docs.godotengine.org/en/latest/development/compiling/compiling_for_web.html)
+- [Compiling for the Web](https://docs.godotengine.org/en/latest/contributing/development/compiling/compiling_for_web.html)
- Instructions on building this platform port from source.
- [Exporting for the Web](https://docs.godotengine.org/en/latest/tutorials/export/exporting_for_web.html)
- Instructions on using the compiled export templates to export a project.
diff --git a/platform/web/api/api.cpp b/platform/web/api/api.cpp
index eb3a199ae1..a630e3d866 100644
--- a/platform/web/api/api.cpp
+++ b/platform/web/api/api.cpp
@@ -73,6 +73,7 @@ void JavaScriptBridge::_bind_methods() {
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);
+ ClassDB::bind_method(D_METHOD("force_fs_sync"), &JavaScriptBridge::force_fs_sync);
ADD_SIGNAL(MethodInfo("pwa_update_available"));
}
@@ -111,6 +112,8 @@ bool JavaScriptBridge::pwa_needs_update() const {
Error JavaScriptBridge::pwa_update() {
return ERR_UNAVAILABLE;
}
+void JavaScriptBridge::force_fs_sync() {
+}
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_bridge_singleton.h b/platform/web/api/javascript_bridge_singleton.h
index bcf1ed653a..456fa6b313 100644
--- a/platform/web/api/javascript_bridge_singleton.h
+++ b/platform/web/api/javascript_bridge_singleton.h
@@ -61,6 +61,7 @@ public:
void download_buffer(Vector<uint8_t> p_arr, const String &p_name, const String &p_mime = "application/octet-stream");
bool pwa_needs_update() const;
Error pwa_update();
+ void force_fs_sync();
static JavaScriptBridge *get_singleton();
JavaScriptBridge();
diff --git a/platform/web/display_server_web.cpp b/platform/web/display_server_web.cpp
index 2c69c0873d..fdb9d107a7 100644
--- a/platform/web/display_server_web.cpp
+++ b/platform/web/display_server_web.cpp
@@ -193,12 +193,12 @@ int DisplayServerWeb::mouse_button_callback(int p_pressed, int p_button, double
}
}
- MouseButton mask = Input::get_singleton()->get_mouse_button_mask();
- MouseButton button_flag = mouse_button_to_mask(ev->get_button_index());
+ BitField<MouseButtonMask> mask = Input::get_singleton()->get_mouse_button_mask();
+ MouseButtonMask button_flag = mouse_button_to_mask(ev->get_button_index());
if (ev->is_pressed()) {
- mask |= button_flag;
- } else if ((mask & button_flag) != MouseButton::NONE) {
- mask &= ~button_flag;
+ mask.set_flag(button_flag);
+ } else if (mask.has_flag(button_flag)) {
+ mask.clear_flag(button_flag);
} else {
// Received release event, but press was outside the canvas, so ignore.
return false;
@@ -218,10 +218,10 @@ int DisplayServerWeb::mouse_button_callback(int p_pressed, int p_button, double
}
void DisplayServerWeb::mouse_move_callback(double p_x, double p_y, double p_rel_x, double p_rel_y, int p_modifiers) {
- MouseButton input_mask = Input::get_singleton()->get_mouse_button_mask();
+ BitField<MouseButtonMask> input_mask = Input::get_singleton()->get_mouse_button_mask();
// For motion outside the canvas, only read mouse movement if dragging
// started inside the canvas; imitating desktop app behavior.
- if (!get_singleton()->cursor_inside_canvas && input_mask == MouseButton::NONE) {
+ if (!get_singleton()->cursor_inside_canvas && input_mask.is_empty()) {
return;
}
@@ -525,15 +525,17 @@ int DisplayServerWeb::mouse_wheel_callback(double p_delta_x, double p_delta_y) {
// Different browsers give wildly different delta values, and we can't
// interpret deltaMode, so use default value for wheel events' factor.
- MouseButton button_flag = mouse_button_to_mask(ev->get_button_index());
+ MouseButtonMask button_flag = mouse_button_to_mask(ev->get_button_index());
+ BitField<MouseButtonMask> button_mask = input->get_mouse_button_mask();
+ button_mask.set_flag(button_flag);
ev->set_pressed(true);
- ev->set_button_mask(input->get_mouse_button_mask() | button_flag);
+ ev->set_button_mask(button_mask);
input->parse_input_event(ev);
Ref<InputEventMouseButton> release = ev->duplicate();
release->set_pressed(false);
- release->set_button_mask(MouseButton(input->get_mouse_button_mask() & ~button_flag));
+ release->set_button_mask(input->get_mouse_button_mask());
input->parse_input_event(release);
return true;
@@ -785,8 +787,10 @@ DisplayServerWeb::DisplayServerWeb(const String &p_rendering_driver, WindowMode
RasterizerGLES3::make_current();
} else {
- OS::get_singleton()->alert("Your browser does not seem to support WebGL2. Please update your browser version.",
- "Unable to initialize video driver");
+ OS::get_singleton()->alert(
+ "Your browser seems not to support WebGL 2.\n\n"
+ "If possible, consider updating your browser version and video card drivers.",
+ "Unable to initialize WebGL 2 video driver");
RasterizerDummy::make_current();
}
#else
diff --git a/platform/web/export/export_plugin.cpp b/platform/web/export/export_plugin.cpp
index 3e11db6887..d8e04904c7 100644
--- a/platform/web/export/export_plugin.cpp
+++ b/platform/web/export/export_plugin.cpp
@@ -31,7 +31,15 @@
#include "export_plugin.h"
#include "core/config/project_settings.h"
+#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
+#include "platform/web/logo_svg.gen.h"
+#include "platform/web/run_icon_svg.gen.h"
+
+#include "modules/modules_enabled.gen.h" // For svg.
+#ifdef MODULE_SVG_ENABLED
+#include "modules/svg/image_loader_svg.h"
+#endif
Error EditorExportPlatformWeb::_extract_template(const String &p_template, const String &p_dir, const String &p_name, bool pwa) {
Ref<FileAccess> io_fa;
@@ -153,7 +161,7 @@ void EditorExportPlatformWeb::_fix_html(Vector<uint8_t> &p_html, const Ref<Edito
const String custom_head_include = p_preset->get("html/head_include");
HashMap<String, String> replaces;
replaces["$GODOT_URL"] = p_name + ".js";
- replaces["$GODOT_PROJECT_NAME"] = ProjectSettings::get_singleton()->get_setting("application/config/name");
+ replaces["$GODOT_PROJECT_NAME"] = GLOBAL_GET("application/config/name");
replaces["$GODOT_HEAD_INCLUDE"] = head_include + custom_head_include;
replaces["$GODOT_CONFIG"] = str_config;
_replace_strings(replaces, p_html);
@@ -193,7 +201,7 @@ Error EditorExportPlatformWeb::_add_manifest_icon(const String &p_path, const St
}
Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_preset, const String p_path, const Vector<SharedObject> &p_shared_objects) {
- String proj_name = ProjectSettings::get_singleton()->get_setting("application/config/name");
+ String proj_name = GLOBAL_GET("application/config/name");
if (proj_name.is_empty()) {
proj_name = "Godot Game";
}
@@ -651,8 +659,17 @@ EditorExportPlatformWeb::EditorExportPlatformWeb() {
server.instantiate();
server_thread.start(_server_thread_poll, this);
- logo = ImageTexture::create_from_image(memnew(Image(_web_logo)));
- run_icon = ImageTexture::create_from_image(memnew(Image(_web_run_icon)));
+#ifdef MODULE_SVG_ENABLED
+ Ref<Image> img = memnew(Image);
+ const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE);
+
+ ImageLoaderSVG img_loader;
+ img_loader.create_image_from_string(img, _web_logo_svg, EDSCALE, upsample, false);
+ logo = ImageTexture::create_from_image(img);
+
+ img_loader.create_image_from_string(img, _web_run_icon_svg, EDSCALE, upsample, false);
+ run_icon = ImageTexture::create_from_image(img);
+#endif
Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme();
if (theme.is_valid()) {
diff --git a/platform/web/export/export_plugin.h b/platform/web/export/export_plugin.h
index b85492b66f..e74c945837 100644
--- a/platform/web/export/export_plugin.h
+++ b/platform/web/export/export_plugin.h
@@ -38,11 +38,8 @@
#include "core/io/zip_io.h"
#include "editor/editor_node.h"
#include "editor/export/editor_export_platform.h"
-#include "main/splash.gen.h"
-#include "platform/web/logo.gen.h"
-#include "platform/web/run_icon.gen.h"
-
#include "editor_http_server.h"
+#include "main/splash.gen.h"
class EditorExportPlatformWeb : public EditorExportPlatform {
GDCLASS(EditorExportPlatformWeb, EditorExportPlatform);
diff --git a/platform/web/javascript_bridge_singleton.cpp b/platform/web/javascript_bridge_singleton.cpp
index 308ca2d5de..dba630404f 100644
--- a/platform/web/javascript_bridge_singleton.cpp
+++ b/platform/web/javascript_bridge_singleton.cpp
@@ -361,6 +361,11 @@ void JavaScriptBridge::download_buffer(Vector<uint8_t> p_arr, const String &p_na
bool JavaScriptBridge::pwa_needs_update() const {
return OS_Web::get_singleton()->pwa_needs_update();
}
+
Error JavaScriptBridge::pwa_update() {
return OS_Web::get_singleton()->pwa_update();
}
+
+void JavaScriptBridge::force_fs_sync() {
+ OS_Web::get_singleton()->force_fs_sync();
+}
diff --git a/platform/web/logo.png b/platform/web/logo.png
deleted file mode 100644
index c046d87dc4..0000000000
--- a/platform/web/logo.png
+++ /dev/null
Binary files differ
diff --git a/platform/web/logo.svg b/platform/web/logo.svg
new file mode 100644
index 0000000000..567b6f3c77
--- /dev/null
+++ b/platform/web/logo.svg
@@ -0,0 +1 @@
+<svg height="32" width="32" xmlns="http://www.w3.org/2000/svg"><path d="M7 5h18v21H7z" fill="#fff"/><path d="M3.143 1 5.48 27.504 15.967 31l10.553-3.496L28.857 1zM23.78 9.565H11.473l.275 3.308h11.759l-.911 9.937-6.556 1.808v.02h-.073l-6.61-1.828-.402-5.076h3.195l.234 2.552 3.583.97 3.595-.97.402-4.165H8.788L7.93 6.37h16.145z" fill="#eb6428"/></svg>
diff --git a/platform/web/os_web.cpp b/platform/web/os_web.cpp
index cb304ce7ac..e12f62f4ad 100644
--- a/platform/web/os_web.cpp
+++ b/platform/web/os_web.cpp
@@ -196,6 +196,12 @@ void OS_Web::update_pwa_state_callback() {
}
}
+void OS_Web::force_fs_sync() {
+ if (is_userfs_persistent()) {
+ idb_needs_sync = true;
+ }
+}
+
Error OS_Web::pwa_update() {
return godot_js_pwa_update() ? FAILED : OK;
}
diff --git a/platform/web/os_web.h b/platform/web/os_web.h
index c8fdea2ee0..70d8af9db9 100644
--- a/platform/web/os_web.h
+++ b/platform/web/os_web.h
@@ -69,6 +69,7 @@ public:
bool pwa_needs_update() const { return pwa_is_waiting; }
Error pwa_update();
+ void force_fs_sync();
void initialize_joypads() override;
diff --git a/platform/web/package-lock.json b/platform/web/package-lock.json
index e1428546c6..4399e8243c 100644
--- a/platform/web/package-lock.json
+++ b/platform/web/package-lock.json
@@ -1529,9 +1529,9 @@
"dev": true
},
"node_modules/json5": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
- "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
+ "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
"dev": true,
"dependencies": {
"minimist": "^1.2.0"
@@ -3662,9 +3662,9 @@
"dev": true
},
"json5": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
- "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
+ "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
"dev": true,
"requires": {
"minimist": "^1.2.0"
diff --git a/platform/web/run_icon.png b/platform/web/run_icon.png
deleted file mode 100644
index 574abb0150..0000000000
--- a/platform/web/run_icon.png
+++ /dev/null
Binary files differ
diff --git a/platform/web/run_icon.svg b/platform/web/run_icon.svg
new file mode 100644
index 0000000000..494f53cb90
--- /dev/null
+++ b/platform/web/run_icon.svg
@@ -0,0 +1 @@
+<svg height="16" width="16" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="M3.143 1 5.48 27.504 15.967 31l10.553-3.496L28.857 1ZM23.78 9.565H11.473l.275 3.308h11.759l-.911 9.937-6.556 1.808v.02h-.073l-6.61-1.828-.402-5.076h3.195l.234 2.552 3.583.97 3.595-.97.402-4.165H8.788L7.93 6.37h16.145Z" fill="#eb6428" style="fill:#e0e0e0;fill-opacity:1" transform="translate(.586 .586) scale(.46337)"/></svg>
diff --git a/platform/windows/README.md b/platform/windows/README.md
index c04032ae1d..4c775576fe 100644
--- a/platform/windows/README.md
+++ b/platform/windows/README.md
@@ -7,7 +7,7 @@ used by this platform.
## Documentation
-- [Compiling for Windows](https://docs.godotengine.org/en/latest/development/compiling/compiling_for_windows.html)
+- [Compiling for Windows](https://docs.godotengine.org/en/latest/contributing/development/compiling/compiling_for_windows.html)
- Instructions on building this platform port from source.
- [Exporting for Windows](https://docs.godotengine.org/en/latest/tutorials/export/exporting_for_windows.html)
- Instructions on using the compiled export templates to export a project.
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index 114bfcb6f8..fa81c81b20 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -257,7 +257,7 @@ Point2i DisplayServerWindows::mouse_get_position() const {
return Point2i(p.x, p.y) - _get_screens_origin();
}
-MouseButton DisplayServerWindows::mouse_get_button_state() const {
+BitField<MouseButtonMask> DisplayServerWindows::mouse_get_button_state() const {
return last_button_state;
}
@@ -348,11 +348,9 @@ typedef struct {
static BOOL CALLBACK _MonitorEnumProcPrim(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
EnumScreenData *data = (EnumScreenData *)dwData;
- if (data->monitor == hMonitor) {
- if ((lprcMonitor->left == 0) && (lprcMonitor->top == 0)) {
- data->screen = data->count;
- return FALSE;
- }
+ if ((lprcMonitor->left == 0) && (lprcMonitor->top == 0)) {
+ data->screen = data->count;
+ return FALSE;
}
data->count++;
@@ -737,6 +735,9 @@ DisplayServer::WindowID DisplayServerWindows::create_sub_window(WindowMode p_mod
if (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) {
wd.no_focus = true;
}
+ if (p_flags & WINDOW_FLAG_MOUSE_PASSTHROUGH_BIT) {
+ wd.mpass = true;
+ }
if (p_flags & WINDOW_FLAG_POPUP_BIT) {
wd.is_popup = true;
}
@@ -920,7 +921,7 @@ void DisplayServerWindows::window_set_mouse_passthrough(const Vector<Vector2> &p
void DisplayServerWindows::_update_window_mouse_passthrough(WindowID p_window) {
ERR_FAIL_COND(!windows.has(p_window));
- if (windows[p_window].mpath.size() == 0) {
+ if (windows[p_window].mpass || windows[p_window].mpath.size() == 0) {
SetWindowRgn(windows[p_window].hWnd, nullptr, TRUE);
} else {
POINT *points = (POINT *)memalloc(sizeof(POINT) * windows[p_window].mpath.size());
@@ -1501,6 +1502,10 @@ void DisplayServerWindows::window_set_flag(WindowFlags p_flag, bool p_enabled, W
wd.no_focus = p_enabled;
_update_window_style(p_window);
} break;
+ case WINDOW_FLAG_MOUSE_PASSTHROUGH: {
+ wd.mpass = p_enabled;
+ _update_window_mouse_passthrough(p_window);
+ } break;
case WINDOW_FLAG_POPUP: {
ERR_FAIL_COND_MSG(p_window == MAIN_WINDOW_ID, "Main window can't be popup.");
ERR_FAIL_COND_MSG(IsWindowVisible(wd.hWnd) && (wd.is_popup != p_enabled), "Popup flag can't changed while window is opened.");
@@ -1532,6 +1537,9 @@ bool DisplayServerWindows::window_get_flag(WindowFlags p_flag, WindowID p_window
case WINDOW_FLAG_NO_FOCUS: {
return wd.no_focus;
} break;
+ case WINDOW_FLAG_MOUSE_PASSTHROUGH: {
+ return wd.mpass;
+ } break;
case WINDOW_FLAG_POPUP: {
return wd.is_popup;
} break;
@@ -2466,6 +2474,11 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
// Process window messages.
switch (uMsg) {
+ case WM_NCHITTEST: {
+ if (windows[window_id].mpass) {
+ return HTTRANSPARENT;
+ }
+ } break;
case WM_MOUSEACTIVATE: {
if (windows[window_id].no_focus) {
return MA_NOACTIVATEANDEAT; // Do not activate, and discard mouse messages.
@@ -2545,6 +2558,25 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
return 0;
}
} break;
+ case WM_ERASEBKGND: {
+ Color early_color;
+ if (!_get_window_early_clear_override(early_color)) {
+ break;
+ }
+ bool must_recreate_brush = !window_bkg_brush || window_bkg_brush_color != early_color.to_argb32();
+ if (must_recreate_brush) {
+ if (window_bkg_brush) {
+ DeleteObject(window_bkg_brush);
+ }
+ window_bkg_brush = CreateSolidBrush(RGB(early_color.get_r8(), early_color.get_g8(), early_color.get_b8()));
+ }
+ HDC hdc = (HDC)wParam;
+ RECT rect = {};
+ if (GetUpdateRect(hWnd, &rect, true)) {
+ FillRect(hdc, &rect, window_bkg_brush);
+ }
+ return 1;
+ } break;
case WM_PAINT: {
Main::force_redraw();
} break;
@@ -3164,9 +3196,9 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
mb->set_alt_pressed(alt_mem);
// mb->is_alt_pressed()=(wParam&MK_MENU)!=0;
if (mb->is_pressed()) {
- last_button_state |= mouse_button_to_mask(mb->get_button_index());
+ last_button_state.set_flag(mouse_button_to_mask(mb->get_button_index()));
} else {
- last_button_state &= ~mouse_button_to_mask(mb->get_button_index());
+ last_button_state.clear_flag(mouse_button_to_mask(mb->get_button_index()));
}
mb->set_button_mask(last_button_state);
@@ -3207,7 +3239,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
// Send release for mouse wheel.
Ref<InputEventMouseButton> mbd = mb->duplicate();
mbd->set_window_id(window_id);
- last_button_state &= ~mouse_button_to_mask(mbd->get_button_index());
+ last_button_state.clear_flag(mouse_button_to_mask(mbd->get_button_index()));
mbd->set_button_mask(last_button_state);
mbd->set_pressed(false);
Input::get_singleton()->parse_input_event(mbd);
@@ -3639,7 +3671,7 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
DWORD dwExStyle;
DWORD dwStyle;
- _get_window_style(window_id_counter == MAIN_WINDOW_ID, (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN), p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN, p_flags & WINDOW_FLAG_BORDERLESS_BIT, !(p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT), p_mode == WINDOW_MODE_MAXIMIZED, (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) | (p_flags & WINDOW_FLAG_POPUP), dwStyle, dwExStyle);
+ _get_window_style(window_id_counter == MAIN_WINDOW_ID, (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN), p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN, p_flags & WINDOW_FLAG_BORDERLESS_BIT, !(p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT), p_mode == WINDOW_MODE_MAXIMIZED, (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) | (p_flags & WINDOW_FLAG_POPUP), dwStyle, dwExStyle);
RECT WindowRect;
@@ -3984,7 +4016,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
memset(&wc, 0, sizeof(WNDCLASSEXW));
wc.cbSize = sizeof(WNDCLASSEXW);
- wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS;
+ wc.style = CS_OWNDC | CS_DBLCLKS;
wc.lpfnWndProc = (WNDPROC)::WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
@@ -4113,18 +4145,20 @@ DisplayServer *DisplayServerWindows::create_func(const String &p_rendering_drive
if (r_error != OK) {
if (p_rendering_driver == "vulkan") {
String executable_name = OS::get_singleton()->get_executable_path().get_file();
- OS::get_singleton()->alert("Your video card driver does not support the selected Vulkan version.\n"
- "Please try updating your GPU driver or try using the OpenGL 3 driver.\n"
- "You can enable the OpenGL 3 driver by starting the engine from the\n"
- "command line with the command:\n'./" +
- executable_name + " --rendering-driver opengl3'.\n "
- "If you have updated your graphics drivers recently, try rebooting.",
- "Unable to initialize Video driver");
+ OS::get_singleton()->alert(
+ vformat("Your video card drivers seem not to support the required Vulkan version.\n\n"
+ "If possible, consider updating your video card drivers or using the OpenGL 3 driver.\n\n"
+ "You can enable the OpenGL 3 driver by starting the engine from the\n"
+ "command line with the command:\n'%s --rendering-driver opengl3'\n\n"
+ "If you have recently updated your video card drivers, try rebooting.",
+ executable_name),
+ "Unable to initialize Vulkan video driver");
} else {
- OS::get_singleton()->alert("Your video card driver does not support the selected OpenGL version.\n"
- "Please try updating your GPU driver.\n"
- "If you have updated your graphics drivers recently, try rebooting.",
- "Unable to initialize Video driver");
+ OS::get_singleton()->alert(
+ "Your video card drivers seem not to support the required OpenGL 3.3 version.\n\n"
+ "If possible, consider updating your video card drivers.\n\n"
+ "If you have recently updated your video card drivers, try rebooting.",
+ "Unable to initialize OpenGL video driver");
}
}
return ds;
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index feed9f35ed..ce4b94af59 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -370,6 +370,7 @@ class DisplayServerWindows : public DisplayServer {
bool window_has_focus = false;
bool exclusive = false;
bool context_created = false;
+ bool mpass = false;
// Used to transfer data between events using timer.
WPARAM saved_wparam;
@@ -446,12 +447,14 @@ class DisplayServerWindows : public DisplayServer {
bool shift_mem = false;
bool control_mem = false;
bool meta_mem = false;
- MouseButton last_button_state = MouseButton::NONE;
+ BitField<MouseButtonMask> last_button_state;
bool use_raw_input = false;
bool drop_events = false;
bool in_dispatch_input_event = false;
WNDCLASSEXW wc;
+ HBRUSH window_bkg_brush = nullptr;
+ uint32_t window_bkg_brush_color = 0;
HCURSOR cursors[CURSOR_MAX] = { nullptr };
CursorShape cursor_shape = CursorShape::CURSOR_ARROW;
@@ -506,7 +509,7 @@ public:
virtual void warp_mouse(const Point2i &p_position) override;
virtual Point2i mouse_get_position() const override;
- virtual MouseButton mouse_get_button_state() const override;
+ virtual BitField<MouseButtonMask> mouse_get_button_state() const override;
virtual void clipboard_set(const String &p_text) override;
virtual String clipboard_get() const override;
diff --git a/platform/windows/export/export.cpp b/platform/windows/export/export.cpp
index b0e2f5a05b..4112bb84b5 100644
--- a/platform/windows/export/export.cpp
+++ b/platform/windows/export/export.cpp
@@ -51,7 +51,6 @@ void register_windows_exporter() {
Ref<EditorExportPlatformWindows> platform;
platform.instantiate();
- platform->set_logo(ImageTexture::create_from_image(memnew(Image(_windows_logo))));
platform->set_name("Windows Desktop");
platform->set_os_name("Windows");
diff --git a/platform/windows/export/export_plugin.cpp b/platform/windows/export/export_plugin.cpp
index 7c61a79fc8..bf32b16018 100644
--- a/platform/windows/export/export_plugin.cpp
+++ b/platform/windows/export/export_plugin.cpp
@@ -34,6 +34,14 @@
#include "core/io/image_loader.h"
#include "editor/editor_node.h"
#include "editor/editor_paths.h"
+#include "editor/editor_scale.h"
+#include "platform/windows/logo_svg.gen.h"
+#include "platform/windows/run_icon_svg.gen.h"
+
+#include "modules/modules_enabled.gen.h" // For svg.
+#ifdef MODULE_SVG_ENABLED
+#include "modules/svg/image_loader_svg.h"
+#endif
Error EditorExportPlatformWindows::_process_icon(const Ref<EditorExportPreset> &p_preset, const String &p_src_path, const String &p_dst_path) {
static const uint8_t icon_size[] = { 16, 32, 48, 64, 128, 0 /*256*/ };
@@ -168,9 +176,39 @@ Error EditorExportPlatformWindows::modify_template(const Ref<EditorExportPreset>
}
Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
- String pck_path = p_path;
- if (p_preset->get("binary_format/embed_pck")) {
- pck_path = p_path.get_basename() + ".tmp";
+ bool export_as_zip = p_path.ends_with("zip");
+ bool embedded = p_preset->get("binary_format/embed_pck");
+
+ String pkg_name;
+ if (String(ProjectSettings::get_singleton()->get("application/config/name")) != "") {
+ pkg_name = String(ProjectSettings::get_singleton()->get("application/config/name"));
+ } else {
+ pkg_name = "Unnamed";
+ }
+
+ pkg_name = OS::get_singleton()->get_safe_dir_name(pkg_name);
+
+ // Setup temp folder.
+ String path = p_path;
+ String tmp_dir_path = EditorPaths::get_singleton()->get_cache_dir().path_join(pkg_name);
+ Ref<DirAccess> tmp_app_dir = DirAccess::create_for_path(tmp_dir_path);
+ if (export_as_zip) {
+ if (tmp_app_dir.is_null()) {
+ return ERR_CANT_CREATE;
+ }
+ if (DirAccess::exists(tmp_dir_path)) {
+ if (tmp_app_dir->change_dir(tmp_dir_path) == OK) {
+ tmp_app_dir->erase_contents_recursive();
+ }
+ }
+ tmp_app_dir->make_dir_recursive(tmp_dir_path);
+ path = tmp_dir_path.path_join(p_path.get_file().get_basename() + ".exe");
+ }
+
+ // Export project.
+ String pck_path = path;
+ if (embedded) {
+ pck_path = pck_path.get_basename() + ".tmp";
}
Error err = EditorExportPlatformPC::export_project(p_preset, p_debug, pck_path, p_flags);
if (p_preset->get("codesign/enable") && err == OK) {
@@ -181,7 +219,7 @@ Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset>
}
}
- if (p_preset->get("binary_format/embed_pck") && err == OK) {
+ if (embedded && err == OK) {
Ref<DirAccess> tmp_dir = DirAccess::create_for_path(p_path.get_base_dir());
err = tmp_dir->rename(pck_path, p_path);
if (err != OK) {
@@ -189,6 +227,27 @@ Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset>
}
}
+ // ZIP project.
+ if (export_as_zip) {
+ if (FileAccess::exists(p_path)) {
+ OS::get_singleton()->move_to_trash(p_path);
+ }
+
+ Ref<FileAccess> io_fa_dst;
+ zlib_filefunc_def io_dst = zipio_create_io(&io_fa_dst);
+ zipFile zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io_dst);
+
+ zip_folder_recursive(zip, tmp_dir_path, "", pkg_name);
+
+ zipClose(zip, nullptr);
+
+ if (tmp_app_dir->change_dir(tmp_dir_path) == OK) {
+ tmp_app_dir->erase_contents_recursive();
+ tmp_app_dir->change_dir("..");
+ tmp_app_dir->remove(pkg_name);
+ }
+ }
+
return err;
}
@@ -199,6 +258,7 @@ String EditorExportPlatformWindows::get_template_file_name(const String &p_targe
List<String> EditorExportPlatformWindows::get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const {
List<String> list;
list.push_back("exe");
+ list.push_back("zip");
return list;
}
@@ -212,6 +272,7 @@ bool EditorExportPlatformWindows::get_export_option_visibility(const EditorExpor
void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_options) {
EditorExportPlatformPC::get_export_options(r_options);
+
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "binary_format/architecture", PROPERTY_HINT_ENUM, "x86_64,x86_32,arm64"), "x86_64"));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/enable"), false));
@@ -235,6 +296,29 @@ void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_optio
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_description"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/trademarks"), ""));
+
+ String run_script = "Expand-Archive -LiteralPath '{temp_dir}\\{archive_name}' -DestinationPath '{temp_dir}'\n"
+ "$action = New-ScheduledTaskAction -Execute '{temp_dir}\\{exe_name}' -Argument '{cmd_args}'\n"
+ "$trigger = New-ScheduledTaskTrigger -Once -At 00:00\n"
+ "$settings = New-ScheduledTaskSettingsSet\n"
+ "$task = New-ScheduledTask -Action $action -Trigger $trigger -Settings $settings\n"
+ "Register-ScheduledTask godot_remote_debug -InputObject $task -Force:$true\n"
+ "Start-ScheduledTask -TaskName godot_remote_debug\n"
+ "while (Get-ScheduledTask -TaskName godot_remote_debug | ? State -eq running) { Start-Sleep -Milliseconds 100 }\n"
+ "Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue";
+
+ String cleanup_script = "Stop-ScheduledTask -TaskName godot_remote_debug -ErrorAction:SilentlyContinue\n"
+ "Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue\n"
+ "Remove-Item -Recurse -Force '{temp_dir}'";
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "ssh_remote_deploy/enabled"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/host"), "user@host_ip"));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/port"), "22"));
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/extra_args_ssh", PROPERTY_HINT_MULTILINE_TEXT), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/extra_args_scp", PROPERTY_HINT_MULTILINE_TEXT), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/run_script", PROPERTY_HINT_MULTILINE_TEXT), run_script));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "ssh_remote_deploy/cleanup_script", PROPERTY_HINT_MULTILINE_TEXT), cleanup_script));
}
Error EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path, bool p_console_icon) {
@@ -659,3 +743,227 @@ Error EditorExportPlatformWindows::fixup_embedded_pck(const String &p_path, int6
}
return OK;
}
+
+Ref<Texture2D> EditorExportPlatformWindows::get_run_icon() const {
+ return run_icon;
+}
+
+bool EditorExportPlatformWindows::poll_export() {
+ Ref<EditorExportPreset> preset;
+
+ for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
+ Ref<EditorExportPreset> ep = EditorExport::get_singleton()->get_export_preset(i);
+ if (ep->is_runnable() && ep->get_platform() == this) {
+ preset = ep;
+ break;
+ }
+ }
+
+ int prev = menu_options;
+ menu_options = (preset.is_valid() && preset->get("ssh_remote_deploy/enabled").operator bool());
+ if (ssh_pid != 0 || !cleanup_commands.is_empty()) {
+ if (menu_options == 0) {
+ cleanup();
+ } else {
+ menu_options += 1;
+ }
+ }
+ return menu_options != prev;
+}
+
+Ref<ImageTexture> EditorExportPlatformWindows::get_option_icon(int p_index) const {
+ return p_index == 1 ? stop_icon : EditorExportPlatform::get_option_icon(p_index);
+}
+
+int EditorExportPlatformWindows::get_options_count() const {
+ return menu_options;
+}
+
+String EditorExportPlatformWindows::get_option_label(int p_index) const {
+ return (p_index) ? TTR("Stop and uninstall") : TTR("Run on remote Windows system");
+}
+
+String EditorExportPlatformWindows::get_option_tooltip(int p_index) const {
+ return (p_index) ? TTR("Stop and uninstall running project from the remote system") : TTR("Run exported project on remote Windows system");
+}
+
+void EditorExportPlatformWindows::cleanup() {
+ if (ssh_pid != 0 && OS::get_singleton()->is_process_running(ssh_pid)) {
+ print_line("Terminating connection...");
+ OS::get_singleton()->kill(ssh_pid);
+ OS::get_singleton()->delay_usec(1000);
+ }
+
+ if (!cleanup_commands.is_empty()) {
+ print_line("Stopping and deleting previous version...");
+ for (const SSHCleanupCommand &cmd : cleanup_commands) {
+ if (cmd.wait) {
+ ssh_run_on_remote(cmd.host, cmd.port, cmd.ssh_args, cmd.cmd_args);
+ } else {
+ ssh_run_on_remote_no_wait(cmd.host, cmd.port, cmd.ssh_args, cmd.cmd_args);
+ }
+ }
+ }
+ ssh_pid = 0;
+ cleanup_commands.clear();
+}
+
+Error EditorExportPlatformWindows::run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) {
+ cleanup();
+ if (p_device) { // Stop command, cleanup only.
+ return OK;
+ }
+
+ EditorProgress ep("run", TTR("Running..."), 5);
+
+ const String dest = EditorPaths::get_singleton()->get_cache_dir().path_join("windows");
+ Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ if (!da->dir_exists(dest)) {
+ Error err = da->make_dir_recursive(dest);
+ if (err != OK) {
+ EditorNode::get_singleton()->show_warning(TTR("Could not create temp directory:") + "\n" + dest);
+ return err;
+ }
+ }
+
+ String host = p_preset->get("ssh_remote_deploy/host").operator String();
+ String port = p_preset->get("ssh_remote_deploy/port").operator String();
+ if (port.is_empty()) {
+ port = "22";
+ }
+ Vector<String> extra_args_ssh = p_preset->get("ssh_remote_deploy/extra_args_ssh").operator String().split(" ");
+ Vector<String> extra_args_scp = p_preset->get("ssh_remote_deploy/extra_args_scp").operator String().split(" ");
+
+ const String basepath = dest.path_join("tmp_windows_export");
+
+#define CLEANUP_AND_RETURN(m_err) \
+ { \
+ if (da->file_exists(basepath + ".zip")) { \
+ da->remove(basepath + ".zip"); \
+ } \
+ if (da->file_exists(basepath + "_start.ps1")) { \
+ da->remove(basepath + "_start.ps1"); \
+ } \
+ if (da->file_exists(basepath + "_clean.ps1")) { \
+ da->remove(basepath + "_clean.ps1"); \
+ } \
+ return m_err; \
+ } \
+ ((void)0)
+
+ if (ep.step(TTR("Exporting project..."), 1)) {
+ return ERR_SKIP;
+ }
+ Error err = export_project(p_preset, true, basepath + ".zip", p_debug_flags);
+ if (err != OK) {
+ DirAccess::remove_file_or_error(basepath + ".zip");
+ return err;
+ }
+
+ String cmd_args;
+ {
+ Vector<String> cmd_args_list;
+ gen_debug_flags(cmd_args_list, p_debug_flags);
+ for (int i = 0; i < cmd_args_list.size(); i++) {
+ if (i != 0) {
+ cmd_args += " ";
+ }
+ cmd_args += cmd_args_list[i];
+ }
+ }
+
+ const bool use_remote = (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) || (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT);
+ int dbg_port = EditorSettings::get_singleton()->get("network/debug/remote_port");
+
+ print_line("Creating temporary directory...");
+ ep.step(TTR("Creating temporary directory..."), 2);
+ String temp_dir;
+ err = ssh_run_on_remote(host, port, extra_args_ssh, "powershell -command \\\"\\$tmp = Join-Path \\$Env:Temp \\$(New-Guid); New-Item -Type Directory -Path \\$tmp | Out-Null; Write-Output \\$tmp\\\"", &temp_dir);
+ if (err != OK || temp_dir.is_empty()) {
+ CLEANUP_AND_RETURN(err);
+ }
+
+ print_line("Uploading archive...");
+ ep.step(TTR("Uploading archive..."), 3);
+ err = ssh_push_to_remote(host, port, extra_args_scp, basepath + ".zip", temp_dir);
+ if (err != OK) {
+ CLEANUP_AND_RETURN(err);
+ }
+
+ {
+ String run_script = p_preset->get("ssh_remote_deploy/run_script");
+ run_script = run_script.replace("{temp_dir}", temp_dir);
+ run_script = run_script.replace("{archive_name}", basepath.get_file() + ".zip");
+ run_script = run_script.replace("{exe_name}", basepath.get_file() + ".exe");
+ run_script = run_script.replace("{cmd_args}", cmd_args);
+
+ Ref<FileAccess> f = FileAccess::open(basepath + "_start.ps1", FileAccess::WRITE);
+ if (f.is_null()) {
+ CLEANUP_AND_RETURN(err);
+ }
+
+ f->store_string(run_script);
+ }
+
+ {
+ String clean_script = p_preset->get("ssh_remote_deploy/cleanup_script");
+ clean_script = clean_script.replace("{temp_dir}", temp_dir);
+ clean_script = clean_script.replace("{archive_name}", basepath.get_file() + ".zip");
+ clean_script = clean_script.replace("{exe_name}", basepath.get_file() + ".exe");
+ clean_script = clean_script.replace("{cmd_args}", cmd_args);
+
+ Ref<FileAccess> f = FileAccess::open(basepath + "_clean.ps1", FileAccess::WRITE);
+ if (f.is_null()) {
+ CLEANUP_AND_RETURN(err);
+ }
+
+ f->store_string(clean_script);
+ }
+
+ print_line("Uploading scripts...");
+ ep.step(TTR("Uploading scripts..."), 4);
+ err = ssh_push_to_remote(host, port, extra_args_scp, basepath + "_start.ps1", temp_dir);
+ if (err != OK) {
+ CLEANUP_AND_RETURN(err);
+ }
+ err = ssh_push_to_remote(host, port, extra_args_scp, basepath + "_clean.ps1", temp_dir);
+ if (err != OK) {
+ CLEANUP_AND_RETURN(err);
+ }
+
+ print_line("Starting project...");
+ ep.step(TTR("Starting project..."), 5);
+ err = ssh_run_on_remote_no_wait(host, port, extra_args_ssh, vformat("powershell -file \"%s\\%s\"", temp_dir, basepath.get_file() + "_start.ps1"), &ssh_pid, (use_remote) ? dbg_port : -1);
+ if (err != OK) {
+ CLEANUP_AND_RETURN(err);
+ }
+
+ cleanup_commands.clear();
+ cleanup_commands.push_back(SSHCleanupCommand(host, port, extra_args_ssh, vformat("powershell -file \"%s\\%s\"", temp_dir, basepath.get_file() + "_clean.ps1")));
+
+ print_line("Project started.");
+
+ CLEANUP_AND_RETURN(OK);
+#undef CLEANUP_AND_RETURN
+}
+
+EditorExportPlatformWindows::EditorExportPlatformWindows() {
+#ifdef MODULE_SVG_ENABLED
+ Ref<Image> img = memnew(Image);
+ const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE);
+
+ ImageLoaderSVG img_loader;
+ img_loader.create_image_from_string(img, _windows_logo_svg, EDSCALE, upsample, false);
+ set_logo(ImageTexture::create_from_image(img));
+
+ img_loader.create_image_from_string(img, _windows_run_icon_svg, EDSCALE, upsample, false);
+ run_icon = ImageTexture::create_from_image(img);
+#endif
+
+ Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme();
+ if (theme.is_valid()) {
+ stop_icon = theme->get_icon(SNAME("Stop"), SNAME("EditorIcons"));
+ } else {
+ stop_icon.instantiate();
+ }
+}
diff --git a/platform/windows/export/export_plugin.h b/platform/windows/export/export_plugin.h
index 96608728d3..fa75a17a1f 100644
--- a/platform/windows/export/export_plugin.h
+++ b/platform/windows/export/export_plugin.h
@@ -35,9 +35,32 @@
#include "core/os/os.h"
#include "editor/editor_settings.h"
#include "editor/export/editor_export_platform_pc.h"
-#include "platform/windows/logo.gen.h"
class EditorExportPlatformWindows : public EditorExportPlatformPC {
+ struct SSHCleanupCommand {
+ String host;
+ String port;
+ Vector<String> ssh_args;
+ String cmd_args;
+ bool wait = false;
+
+ SSHCleanupCommand(){};
+ SSHCleanupCommand(const String &p_host, const String &p_port, const Vector<String> &p_ssh_arg, const String &p_cmd_args, bool p_wait = false) {
+ host = p_host;
+ port = p_port;
+ ssh_args = p_ssh_arg;
+ cmd_args = p_cmd_args;
+ wait = p_wait;
+ };
+ };
+
+ Ref<ImageTexture> run_icon;
+ Ref<ImageTexture> stop_icon;
+
+ Vector<SSHCleanupCommand> cleanup_commands;
+ OS::ProcessID ssh_pid = 0;
+ int menu_options = 0;
+
Error _process_icon(const Ref<EditorExportPreset> &p_preset, const String &p_src_path, const String &p_dst_path);
Error _rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path, bool p_console_icon);
Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path);
@@ -53,6 +76,17 @@ public:
virtual bool get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option, const HashMap<StringName, Variant> &p_options) const override;
virtual String get_template_file_name(const String &p_target, const String &p_arch) const override;
virtual Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) override;
+
+ virtual Ref<Texture2D> get_run_icon() const override;
+ virtual bool poll_export() override;
+ virtual Ref<ImageTexture> get_option_icon(int p_index) const override;
+ virtual int get_options_count() const override;
+ virtual String get_option_label(int p_index) const override;
+ virtual String get_option_tooltip(int p_index) const override;
+ virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) override;
+ virtual void cleanup() override;
+
+ EditorExportPlatformWindows();
};
#endif // WINDOWS_EXPORT_PLUGIN_H
diff --git a/platform/windows/joypad_windows.cpp b/platform/windows/joypad_windows.cpp
index aed5f5b8dc..183958cf40 100644
--- a/platform/windows/joypad_windows.cpp
+++ b/platform/windows/joypad_windows.cpp
@@ -420,38 +420,43 @@ void JoypadWindows::process_joypads() {
}
void JoypadWindows::post_hat(int p_device, DWORD p_dpad) {
- HatMask dpad_val = (HatMask)0;
+ BitField<HatMask> dpad_val;
// Should be -1 when centered, but according to docs:
// "Some drivers report the centered position of the POV indicator as 65,535. Determine whether the indicator is centered as follows:
// BOOL POVCentered = (LOWORD(dwPOV) == 0xFFFF);"
// https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ee416628(v%3Dvs.85)#remarks
if (LOWORD(p_dpad) == 0xFFFF) {
- dpad_val = (HatMask)HatMask::CENTER;
+ // Do nothing.
+ // dpad_val.set_flag(HatMask::CENTER);
}
if (p_dpad == 0) {
- dpad_val = (HatMask)HatMask::UP;
+ dpad_val.set_flag(HatMask::UP);
} else if (p_dpad == 4500) {
- dpad_val = (HatMask)(HatMask::UP | HatMask::RIGHT);
+ dpad_val.set_flag(HatMask::UP);
+ dpad_val.set_flag(HatMask::RIGHT);
} else if (p_dpad == 9000) {
- dpad_val = (HatMask)HatMask::RIGHT;
+ dpad_val.set_flag(HatMask::RIGHT);
} else if (p_dpad == 13500) {
- dpad_val = (HatMask)(HatMask::RIGHT | HatMask::DOWN);
+ dpad_val.set_flag(HatMask::RIGHT);
+ dpad_val.set_flag(HatMask::DOWN);
} else if (p_dpad == 18000) {
- dpad_val = (HatMask)HatMask::DOWN;
+ dpad_val.set_flag(HatMask::DOWN);
} else if (p_dpad == 22500) {
- dpad_val = (HatMask)(HatMask::DOWN | HatMask::LEFT);
+ dpad_val.set_flag(HatMask::DOWN);
+ dpad_val.set_flag(HatMask::LEFT);
} else if (p_dpad == 27000) {
- dpad_val = (HatMask)HatMask::LEFT;
+ dpad_val.set_flag(HatMask::LEFT);
} else if (p_dpad == 31500) {
- dpad_val = (HatMask)(HatMask::LEFT | HatMask::UP);
+ dpad_val.set_flag(HatMask::LEFT);
+ dpad_val.set_flag(HatMask::UP);
}
input->joy_hat(p_device, dpad_val);
}
diff --git a/platform/windows/logo.png b/platform/windows/logo.png
deleted file mode 100644
index f06b463850..0000000000
--- a/platform/windows/logo.png
+++ /dev/null
Binary files differ
diff --git a/platform/windows/logo.svg b/platform/windows/logo.svg
new file mode 100644
index 0000000000..77a0b20766
--- /dev/null
+++ b/platform/windows/logo.svg
@@ -0,0 +1 @@
+<svg height="32" width="32" xmlns="http://www.w3.org/2000/svg"><path d="m1 5.132 12.295-1.694v11.879H1zm0 21.736 12.295 1.695V16.83H1zm13.647 1.875L31 31V16.83H14.647zm0-25.486v12.06H31V1z" fill="#00abed"/></svg>
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index 2a44a19085..130c5f7b97 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -718,15 +718,23 @@ Error OS_Windows::create_process(const String &p_path, const List<String> &p_arg
}
Error OS_Windows::kill(const ProcessID &p_pid) {
- ERR_FAIL_COND_V(!process_map->has(p_pid), FAILED);
+ int ret = 0;
+ if (process_map->has(p_pid)) {
+ const PROCESS_INFORMATION pi = (*process_map)[p_pid].pi;
+ process_map->erase(p_pid);
- const PROCESS_INFORMATION pi = (*process_map)[p_pid].pi;
- process_map->erase(p_pid);
+ ret = TerminateProcess(pi.hProcess, 0);
- const int ret = TerminateProcess(pi.hProcess, 0);
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ } else {
+ HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, false, (DWORD)p_pid);
+ if (hProcess != NULL) {
+ ret = TerminateProcess(hProcess, 0);
- CloseHandle(pi.hProcess);
- CloseHandle(pi.hThread);
+ CloseHandle(hProcess);
+ }
+ }
return ret != 0 ? OK : FAILED;
}
@@ -1158,17 +1166,24 @@ String OS_Windows::get_environment(const String &p_var) const {
return "";
}
-bool OS_Windows::set_environment(const String &p_var, const String &p_value) const {
- return (bool)SetEnvironmentVariableW((LPCWSTR)(p_var.utf16().get_data()), (LPCWSTR)(p_value.utf16().get_data()));
+void OS_Windows::set_environment(const String &p_var, const String &p_value) const {
+ ERR_FAIL_COND_MSG(p_var.is_empty() || p_var.contains("="), vformat("Invalid environment variable name '%s', cannot be empty or include '='.", p_var));
+ Char16String var = p_var.utf16();
+ Char16String value = p_value.utf16();
+ ERR_FAIL_COND_MSG(var.length() + value.length() + 2 > 32767, vformat("Invalid definition for environment variable '%s', cannot exceed 32767 characters.", p_var));
+ SetEnvironmentVariableW((LPCWSTR)(var.get_data()), (LPCWSTR)(value.get_data()));
}
-String OS_Windows::get_stdin_string(bool p_block) {
- if (p_block) {
- WCHAR buff[1024];
- DWORD count = 0;
- if (ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE), buff, 1024, &count, nullptr)) {
- return String::utf16((const char16_t *)buff, count);
- }
+void OS_Windows::unset_environment(const String &p_var) const {
+ ERR_FAIL_COND_MSG(p_var.is_empty() || p_var.contains("="), vformat("Invalid environment variable name '%s', cannot be empty or include '='.", p_var));
+ SetEnvironmentVariableW((LPCWSTR)(p_var.utf16().get_data()), nullptr); // Null to delete.
+}
+
+String OS_Windows::get_stdin_string() {
+ WCHAR buff[1024];
+ DWORD count = 0;
+ if (ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE), buff, 1024, &count, nullptr)) {
+ return String::utf16((const char16_t *)buff, count);
}
return String();
diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h
index 9cb3977030..05110c2614 100644
--- a/platform/windows/os_windows.h
+++ b/platform/windows/os_windows.h
@@ -140,7 +140,7 @@ protected:
virtual void finalize() override;
virtual void finalize_core() override;
- virtual String get_stdin_string(bool p_block) override;
+ virtual String get_stdin_string() override;
String _quote_command_line_argument(const String &p_text) const;
@@ -186,7 +186,8 @@ public:
virtual bool has_environment(const String &p_var) const override;
virtual String get_environment(const String &p_var) const override;
- virtual bool set_environment(const String &p_var, const String &p_value) const override;
+ virtual void set_environment(const String &p_var, const String &p_value) const override;
+ virtual void unset_environment(const String &p_var) const override;
virtual Vector<String> get_system_fonts() const override;
virtual String get_system_font_path(const String &p_font_name, int p_weight = 400, int p_stretch = 100, bool p_italic = false) const override;
diff --git a/platform/windows/run_icon.svg b/platform/windows/run_icon.svg
new file mode 100644
index 0000000000..0897276ef7
--- /dev/null
+++ b/platform/windows/run_icon.svg
@@ -0,0 +1 @@
+<svg height="16" width="16" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="m1.095 2.997 5.66-.78v5.469h-5.66zm0 10.006 5.66.78v-5.4h-5.66zm6.282.863 7.528 1.04V8.381H7.377Zm0-11.732v5.552h7.528V1.095Z" fill="#00abed" style="stroke-width:.460341;fill:#e0e0e0;fill-opacity:1"/></svg>